001package com.nimbusds.jose.jwk; 002 003 004import java.math.BigInteger; 005import java.net.URI; 006import java.security.*; 007import java.util.*; 008import java.security.interfaces.ECPrivateKey; 009import java.security.interfaces.ECPublicKey; 010import java.security.spec.ECParameterSpec; 011import java.security.spec.ECPoint; 012import java.security.spec.ECPrivateKeySpec; 013import java.security.spec.ECPublicKeySpec; 014import java.security.spec.InvalidKeySpecException; 015import java.text.ParseException; 016 017import net.jcip.annotations.Immutable; 018 019import net.minidev.json.JSONObject; 020 021import com.nimbusds.jose.Algorithm; 022import com.nimbusds.jose.JOSEException; 023import com.nimbusds.jose.util.*; 024 025 026/** 027 * Public and private {@link KeyType#EC Elliptic Curve} JSON Web Key (JWK). 028 * Uses the BouncyCastle.org provider for EC key import and export. This class 029 * is immutable. 030 * 031 * <p>Example JSON object representation of a public EC JWK: 032 * 033 * <pre> 034 * { 035 * "kty" : "EC", 036 * "crv" : "P-256", 037 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 038 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 039 * "use" : "enc", 040 * "kid" : "1" 041 * } 042 * </pre> 043 * 044 * <p>Example JSON object representation of a public and private EC JWK: 045 * 046 * <pre> 047 * { 048 * "kty" : "EC", 049 * "crv" : "P-256", 050 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 051 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 052 * "d" : "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE", 053 * "use" : "enc", 054 * "kid" : "1" 055 * } 056 * </pre> 057 * 058 * <p>See http://en.wikipedia.org/wiki/Elliptic_curve_cryptography 059 * 060 * @author Vladimir Dzhuvinov 061 * @author Justin Richer 062 * @version 2015-09-28 063 */ 064@Immutable 065public final class ECKey extends JWK { 066 067 068 /** 069 * Cryptographic curve. This class is immutable. 070 * 071 * <p>Includes constants for the following standard cryptographic 072 * curves: 073 * 074 * <ul> 075 * <li>{@link #P_256} 076 * <li>{@link #P_384} 077 * <li>{@link #P_521} 078 * </ul> 079 * 080 * <p>See "Digital Signature Standard (DSS)", FIPS PUB 186-3, June 081 * 2009, National Institute of Standards and Technology (NIST). 082 */ 083 @Immutable 084 public static class Curve { 085 086 087 /** 088 * P-256 curve (secp256r1, also called prime256v1). 089 */ 090 public static final Curve P_256 = new Curve("P-256", "secp256r1"); 091 092 093 /** 094 * P-384 curve (secp384r1). 095 */ 096 public static final Curve P_384 = new Curve("P-384", "secp384r1"); 097 098 099 /** 100 * P-521 curve (secp521r1). 101 */ 102 public static final Curve P_521 = new Curve("P-521", "secp521r1"); 103 104 105 /** 106 * The JOSE curve name. 107 */ 108 private final String name; 109 110 111 /** 112 * The standard curve name, {@code null} if not specified. 113 */ 114 private final String stdName; 115 116 117 /** 118 * Creates a new cryptographic curve with the specified JOSE 119 * name. A standard curve name is not unspecified. 120 * 121 * @param name The JOSE name of the cryptographic curve. Must not be 122 * {@code null}. 123 */ 124 public Curve(final String name) { 125 126 this(name, null); 127 } 128 129 130 /** 131 * Creates a new cryptographic curve with the specified JOSE 132 * and standard names. 133 * 134 * @param name The JOSE name of the cryptographic curve. 135 * Must not be {@code null}. 136 * @param stdName The standard name of the cryptographic curve, 137 * {@code null} if not specified. 138 */ 139 public Curve(final String name, final String stdName) { 140 141 if (name == null) { 142 throw new IllegalArgumentException("The JOSE cryptographic curve name must not be null"); 143 } 144 145 this.name = name; 146 147 this.stdName = stdName; 148 } 149 150 151 /** 152 * Returns the JOSE name of this cryptographic curve. 153 * 154 * @return The JOSE name. 155 */ 156 public String getName() { 157 158 return name; 159 } 160 161 162 /** 163 * Returns the standard name of this cryptographic curve. 164 * 165 * @return The standard name, {@code null} if not specified. 166 */ 167 public String getStdName() { 168 169 return stdName; 170 } 171 172 173 /** 174 * Returns the parameter specification for this cryptographic 175 * curve. 176 * 177 * @return The EC parameter specification, {@code null} if it 178 * cannot be determined. 179 */ 180 public ECParameterSpec toECParameterSpec() { 181 182 return ECParameterTable.get(this); 183 } 184 185 186 /** 187 * @see #getName 188 */ 189 @Override 190 public String toString() { 191 192 return getName(); 193 } 194 195 196 @Override 197 public boolean equals(final Object object) { 198 199 return object instanceof Curve && 200 this.toString().equals(object.toString()); 201 } 202 203 204 /** 205 * Parses a cryptographic curve from the specified string. 206 * 207 * @param s The string to parse. Must not be {@code null} or 208 * empty. 209 * 210 * @return The cryptographic curve. 211 */ 212 public static Curve parse(final String s) { 213 214 if (s == null || s.trim().isEmpty()) { 215 throw new IllegalArgumentException("The cryptographic curve string must not be null or empty"); 216 } 217 218 if (s.equals(P_256.getName())) { 219 return P_256; 220 221 } else if (s.equals(P_384.getName())) { 222 return P_384; 223 224 } else if (s.equals(P_521.getName())) { 225 return P_521; 226 227 } else { 228 return new Curve(s); 229 } 230 } 231 232 233 /** 234 * Gets the cryptographic curve for the specified standard 235 * name. 236 * 237 * @param stdName The standard curve name. May be {@code null}. 238 * 239 * @return The curve, {@code null} if it cannot be determined. 240 */ 241 public static Curve forStdName(final String stdName) { 242 if( "secp256r1".equals(stdName) || "prime256v1".equals(stdName)) { 243 return P_256; 244 } else if( "secp384r1".equals(stdName) ) { 245 return P_384; 246 } else if( "secp521r1".equals(stdName) ) { 247 return P_521; 248 } else { 249 return null; 250 } 251 } 252 253 254 /** 255 * Gets the cryptographic curve for the specified parameter 256 * specification. 257 * 258 * @param spec The EC parameter spec. May be {@code null}. 259 * 260 * @return The curve, {@code null} if it cannot be determined. 261 */ 262 public static Curve forECParameterSpec(final ECParameterSpec spec) { 263 264 return ECParameterTable.get(spec); 265 } 266 } 267 268 269 /** 270 * Builder for constructing Elliptic Curve JWKs. 271 * 272 * <p>Example usage: 273 * 274 * <pre> 275 * ECKey key = new ECKey.Builder(Curve.P521, x, y). 276 * d(d). 277 * algorithm(JWSAlgorithm.ES512). 278 * keyID("789"). 279 * build(); 280 * </pre> 281 */ 282 public static class Builder { 283 284 285 /** 286 * The curve name. 287 */ 288 private final Curve crv; 289 290 291 /** 292 * The public 'x' EC coordinate. 293 */ 294 private final Base64URL x; 295 296 297 /** 298 * The public 'y' EC coordinate. 299 */ 300 private final Base64URL y; 301 302 303 /** 304 * The private 'd' EC coordinate, optional. 305 */ 306 private Base64URL d; 307 308 309 /** 310 * The key use, optional. 311 */ 312 private KeyUse use; 313 314 315 /** 316 * The key operations, optional. 317 */ 318 private Set<KeyOperation> ops; 319 320 321 /** 322 * The intended JOSE algorithm for the key, optional. 323 */ 324 private Algorithm alg; 325 326 327 /** 328 * The key ID, optional. 329 */ 330 private String kid; 331 332 333 /** 334 * X.509 certificate URL, optional. 335 */ 336 private URI x5u; 337 338 339 /** 340 * X.509 certificate thumbprint, optional. 341 */ 342 private Base64URL x5t; 343 344 345 /** 346 * The X.509 certificate chain, optional. 347 */ 348 private List<Base64> x5c; 349 350 351 /** 352 * Creates a new Elliptic Curve JWK builder. 353 * 354 * @param crv The cryptographic curve. Must not be 355 * {@code null}. 356 * @param x The public 'x' coordinate for the elliptic curve 357 * point. It is represented as the Base64URL 358 * encoding of the coordinate's big endian 359 * representation. Must not be {@code null}. 360 * @param y The public 'y' coordinate for the elliptic curve 361 * point. It is represented as the Base64URL 362 * encoding of the coordinate's big endian 363 * representation. Must not be {@code null}. 364 */ 365 public Builder(final Curve crv, final Base64URL x, final Base64URL y) { 366 367 if (crv == null) { 368 throw new IllegalArgumentException("The curve must not be null"); 369 } 370 371 this.crv = crv; 372 373 if (x == null) { 374 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 375 } 376 377 this.x = x; 378 379 if (y == null) { 380 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 381 } 382 383 this.y = y; 384 } 385 386 387 /** 388 * Creates a new Elliptic Curve JWK builder. 389 * 390 * @param crv The cryptographic curve. Must not be 391 * {@code null}. 392 * @param pub The public EC key to represent. Must not be 393 * {@code null}. 394 */ 395 public Builder(final Curve crv, final ECPublicKey pub) { 396 397 this(crv, 398 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 399 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY())); 400 } 401 402 403 /** 404 * Sets the private 'd' coordinate for the elliptic curve 405 * point. The alternative method is {@link #privateKey}. 406 * 407 * @param d The 'd' coordinate. It is represented as the 408 * Base64URL encoding of the coordinate's big endian 409 * representation. {@code null} if not specified (for 410 * a public key). 411 * 412 * @return This builder. 413 */ 414 public Builder d(final Base64URL d) { 415 416 this.d = d; 417 return this; 418 } 419 420 421 /** 422 * Sets the private Elliptic Curve key. The alternative method 423 * is {@link #d}. 424 * 425 * @param priv The private EC key, used to obtain the private 426 * 'd' coordinate for the elliptic curve point. 427 * {@code null} if not specified (for a public 428 * key). 429 * 430 * @return This builder. 431 */ 432 public Builder privateKey(final ECPrivateKey priv) { 433 434 if (priv != null) { 435 this.d = encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()); 436 } 437 438 return this; 439 } 440 441 442 /** 443 * Sets the use ({@code use}) of the JWK. 444 * 445 * @param use The key use, {@code null} if not specified or if 446 * the key is intended for signing as well as 447 * encryption. 448 * 449 * @return This builder. 450 */ 451 public Builder keyUse(final KeyUse use) { 452 453 this.use = use; 454 return this; 455 } 456 457 458 /** 459 * Sets the operations ({@code key_ops}) of the JWK. 460 * 461 * @param ops The key operations, {@code null} if not 462 * specified. 463 * 464 * @return This builder. 465 */ 466 public Builder keyOperations(final Set<KeyOperation> ops) { 467 468 this.ops = ops; 469 return this; 470 } 471 472 473 /** 474 * Sets the intended JOSE algorithm ({@code alg}) for the JWK. 475 * 476 * @param alg The intended JOSE algorithm, {@code null} if not 477 * specified. 478 * 479 * @return This builder. 480 */ 481 public Builder algorithm(final Algorithm alg) { 482 483 this.alg = alg; 484 return this; 485 } 486 487 /** 488 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 489 * to match a specific key. This can be used, for instance, to 490 * choose a key within a {@link JWKSet} during key rollover. 491 * The key ID may also correspond to a JWS/JWE {@code kid} 492 * header parameter value. 493 * 494 * @param kid The key ID, {@code null} if not specified. 495 * 496 * @return This builder. 497 */ 498 public Builder keyID(final String kid) { 499 500 this.kid = kid; 501 return this; 502 } 503 504 505 /** 506 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK 507 * thumbprint (RFC 7638). The key ID can be used to match a 508 * specific key. This can be used, for instance, to choose a 509 * key within a {@link JWKSet} during key rollover. The key ID 510 * may also correspond to a JWS/JWE {@code kid} header 511 * parameter value. 512 * 513 * @return This builder. 514 * 515 * @throws JOSEException If the SHA-256 hash algorithm is not 516 * supported. 517 */ 518 public Builder keyIDFromThumbprint() 519 throws JOSEException { 520 521 return keyIDFromThumbprint("SHA-256"); 522 } 523 524 525 /** 526 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint 527 * (RFC 7638). The key ID can be used to match a specific key. 528 * This can be used, for instance, to choose a key within a 529 * {@link JWKSet} during key rollover. The key ID may also 530 * correspond to a JWS/JWE {@code kid} header parameter value. 531 * 532 * @param hashAlg The hash algorithm for the JWK thumbprint 533 * computation. Must not be {@code null}. 534 * 535 * @return This builder. 536 * 537 * @throws JOSEException If the hash algorithm is not 538 * supported. 539 */ 540 public Builder keyIDFromThumbprint(final String hashAlg) 541 throws JOSEException { 542 543 // Put mandatory params in sorted order 544 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 545 requiredParams.put("crv", crv.toString()); 546 requiredParams.put("kty", KeyType.EC.getValue()); 547 requiredParams.put("x", x.toString()); 548 requiredParams.put("y", y.toString()); 549 this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString(); 550 return this; 551 } 552 553 554 /** 555 * Sets the X.509 certificate URL ({@code x5u}) of the JWK. 556 * 557 * @param x5u The X.509 certificate URL, {@code null} if not 558 * specified. 559 * 560 * @return This builder. 561 */ 562 public Builder x509CertURL(final URI x5u) { 563 564 this.x5u = x5u; 565 return this; 566 } 567 568 569 /** 570 * Sets the X.509 certificate thumbprint ({@code x5t}) of the 571 * JWK. 572 * 573 * @param x5t The X.509 certificate thumbprint, {@code null} if 574 * not specified. 575 * 576 * @return This builder. 577 */ 578 public Builder x509CertThumbprint(final Base64URL x5t) { 579 580 this.x5t = x5t; 581 return this; 582 } 583 584 585 /** 586 * Sets the X.509 certificate chain ({@code x5c}) of the JWK. 587 * 588 * @param x5c The X.509 certificate chain as a unmodifiable 589 * list, {@code null} if not specified. 590 * 591 * @return This builder. 592 */ 593 public Builder x509CertChain(final List<Base64> x5c) { 594 595 this.x5c = x5c; 596 return this; 597 } 598 599 600 /** 601 * Builds a new octet sequence JWK. 602 * 603 * @return The octet sequence JWK. 604 * 605 * @throws IllegalStateException If the JWK parameters were 606 * inconsistently specified. 607 */ 608 public ECKey build() { 609 610 try { 611 if (d == null) { 612 // Public key 613 return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5c); 614 } 615 616 // Pair 617 return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5c); 618 619 } catch (IllegalArgumentException e) { 620 621 throw new IllegalStateException(e.getMessage(), e); 622 } 623 } 624 } 625 626 627 /** 628 * Returns the Base64URL encoding of the specified elliptic curve 'x', 629 * 'y' or 'd' coordinate, with leading zero padding up to the specified 630 * field size in bits. 631 * 632 * @param fieldSize The field size in bits. 633 * @param coordinate The elliptic curve coordinate. Must not be 634 * {@code null}. 635 * 636 * @return The Base64URL-encoded coordinate, with leading zero padding 637 * up to the curve's field size. 638 */ 639 public static Base64URL encodeCoordinate(final int fieldSize, final BigInteger coordinate) { 640 641 byte[] unpadded = BigIntegerUtils.toBytesUnsigned(coordinate); 642 643 int bytesToOutput = (fieldSize + 7)/8; 644 645 if (unpadded.length >= bytesToOutput) { 646 // Greater-than check to prevent exception on malformed 647 // key below 648 return Base64URL.encode(unpadded); 649 } 650 651 byte[] padded = new byte[bytesToOutput]; 652 653 System.arraycopy(unpadded, 0, padded, bytesToOutput - unpadded.length, unpadded.length); 654 655 return Base64URL.encode(padded); 656 } 657 658 659 /** 660 * The curve name. 661 */ 662 private final Curve crv; 663 664 665 /** 666 * The public 'x' EC coordinate. 667 */ 668 private final Base64URL x; 669 670 671 /** 672 * The public 'y' EC coordinate. 673 */ 674 private final Base64URL y; 675 676 677 /** 678 * The private 'd' EC coordinate 679 */ 680 private final Base64URL d; 681 682 683 /** 684 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 685 * specified parameters. 686 * 687 * @param crv The cryptographic curve. Must not be {@code null}. 688 * @param x The public 'x' coordinate for the elliptic curve point. 689 * It is represented as the Base64URL encoding of the 690 * coordinate's big endian representation. Must not be 691 * {@code null}. 692 * @param y The public 'y' coordinate for the elliptic curve point. 693 * It is represented as the Base64URL encoding of the 694 * coordinate's big endian representation. Must not be 695 * {@code null}. 696 * @param use The key use, {@code null} if not specified or if the key 697 * is intended for signing as well as encryption. 698 * @param ops The key operations, {@code null} if not specified. 699 * @param alg The intended JOSE algorithm for the key, {@code null} if 700 * not specified. 701 * @param kid The key ID, {@code null} if not specified. 702 * @param x5u The X.509 certificate URL, {@code null} if not specified. 703 * @param x5t The X.509 certificate thumbprint, {@code null} if not 704 * specified. 705 * @param x5c The X.509 certificate chain, {@code null} if not 706 * specified. 707 */ 708 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 709 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 710 final URI x5u, final Base64URL x5t, final List<Base64> x5c) { 711 712 super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c); 713 714 if (crv == null) { 715 throw new IllegalArgumentException("The curve must not be null"); 716 } 717 718 this.crv = crv; 719 720 if (x == null) { 721 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 722 } 723 724 this.x = x; 725 726 if (y == null) { 727 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 728 } 729 730 this.y = y; 731 732 this.d = null; 733 } 734 735 736 /** 737 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 738 * with the specified parameters. 739 * 740 * @param crv The cryptographic curve. Must not be {@code null}. 741 * @param x The public 'x' coordinate for the elliptic curve point. 742 * It is represented as the Base64URL encoding of the 743 * coordinate's big endian representation. Must not be 744 * {@code null}. 745 * @param y The public 'y' coordinate for the elliptic curve point. 746 * It is represented as the Base64URL encoding of the 747 * coordinate's big endian representation. Must not be 748 * {@code null}. 749 * @param d The private 'd' coordinate for the elliptic curve point. 750 * It is represented as the Base64URL encoding of the 751 * coordinate's big endian representation. Must not be 752 * {@code null}. 753 * @param use The key use, {@code null} if not specified or if the key 754 * is intended for signing as well as encryption. 755 * @param ops The key operations, {@code null} if not specified. 756 * @param alg The intended JOSE algorithm for the key, {@code null} if 757 * not specified. 758 * @param kid The key ID, {@code null} if not specified. 759 * @param x5u The X.509 certificate URL, {@code null} if not specified. 760 * @param x5t The X.509 certificate thumbprint, {@code null} if not 761 * specified. 762 * @param x5c The X.509 certificate chain, {@code null} if not 763 * specified. 764 */ 765 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final Base64URL d, 766 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 767 final URI x5u, final Base64URL x5t, final List<Base64> x5c) { 768 769 super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c); 770 771 if (crv == null) { 772 throw new IllegalArgumentException("The curve must not be null"); 773 } 774 775 this.crv = crv; 776 777 if (x == null) { 778 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 779 } 780 781 this.x = x; 782 783 if (y == null) { 784 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 785 } 786 787 this.y = y; 788 789 if (d == null) { 790 throw new IllegalArgumentException("The 'd' coordinate must not be null"); 791 } 792 793 this.d = d; 794 } 795 796 797 /** 798 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 799 * specified parameters. 800 * 801 * @param crv The cryptographic curve. Must not be {@code null}. 802 * @param pub The public EC key to represent. Must not be {@code null}. 803 * @param use The key use, {@code null} if not specified or if the key 804 * is intended for signing as well as encryption. 805 * @param ops The key operations, {@code null} if not specified. 806 * @param alg The intended JOSE algorithm for the key, {@code null} if 807 * not specified. 808 * @param kid The key ID, {@code null} if not specified. 809 * @param x5u The X.509 certificate URL, {@code null} if not specified. 810 * @param x5t The X.509 certificate thumbprint, {@code null} if not 811 * specified. 812 * @param x5c The X.509 certificate chain, {@code null} if not 813 * specified. 814 */ 815 public ECKey(final Curve crv, final ECPublicKey pub, 816 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 817 final URI x5u, final Base64URL x5t, final List<Base64> x5c) { 818 819 this(crv, 820 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 821 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()), 822 use, ops, alg, kid, 823 x5u, x5t, x5c); 824 } 825 826 827 /** 828 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 829 * with the specified parameters. 830 * 831 * @param crv The cryptographic curve. Must not be {@code null}. 832 * @param pub The public EC key to represent. Must not be 833 * {@code null}. 834 * @param priv The private EC key to represent. Must not be 835 * {@code null}. 836 * @param use The key use, {@code null} if not specified or if the key 837 * is intended for signing as well as encryption. 838 * @param ops The key operations, {@code null} if not specified. 839 * @param alg The intended JOSE algorithm for the key, {@code null} if 840 * not specified. 841 * @param kid The key ID, {@code null} if not specified. 842 * @param x5u The X.509 certificate URL, {@code null} if not 843 * specified. 844 * @param x5t The X.509 certificate thumbprint, {@code null} if not 845 * specified. 846 * @param x5c The X.509 certificate chain, {@code null} if not 847 * specified. 848 */ 849 public ECKey(final Curve crv, final ECPublicKey pub, final ECPrivateKey priv, 850 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 851 final URI x5u, final Base64URL x5t, final List<Base64> x5c) { 852 853 this(crv, 854 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 855 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()), 856 encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()), 857 use, ops, alg, kid, 858 x5u, x5t, x5c); 859 } 860 861 862 /** 863 * Gets the cryptographic curve. 864 * 865 * @return The cryptographic curve. 866 */ 867 public Curve getCurve() { 868 869 return crv; 870 } 871 872 873 /** 874 * Gets the public 'x' coordinate for the elliptic curve point. 875 * 876 * @return The 'x' coordinate. It is represented as the Base64URL 877 * encoding of the coordinate's big endian representation. 878 */ 879 public Base64URL getX() { 880 881 return x; 882 } 883 884 885 /** 886 * Gets the public 'y' coordinate for the elliptic curve point. 887 * 888 * @return The 'y' coordinate. It is represented as the Base64URL 889 * encoding of the coordinate's big endian representation. 890 */ 891 public Base64URL getY() { 892 893 return y; 894 } 895 896 897 /** 898 * Gets the private 'd' coordinate for the elliptic curve point. It is 899 * represented as the Base64URL encoding of the coordinate's big endian 900 * representation. 901 * 902 * @return The 'd' coordinate. It is represented as the Base64URL 903 * encoding of the coordinate's big endian representation. 904 * {@code null} if not specified (for a public key). 905 */ 906 public Base64URL getD() { 907 908 return d; 909 } 910 911 912 /** 913 * Returns a standard {@code java.security.interfaces.ECPublicKey} 914 * representation of this Elliptic Curve JWK. Uses the default JCA 915 * provider. 916 * 917 * @return The public Elliptic Curve key. 918 * 919 * @throws JOSEException If EC is not supported by the underlying Java 920 * Cryptography (JCA) provider or if the JWK 921 * parameters are invalid for a public EC key. 922 */ 923 public ECPublicKey toECPublicKey() 924 throws JOSEException { 925 926 return toECPublicKey(null); 927 } 928 929 930 /** 931 * Returns a standard {@code java.security.interfaces.ECPublicKey} 932 * representation of this Elliptic Curve JWK. 933 * 934 * @param provider The specific JCA provider to use, {@code null} 935 * implies the default one. 936 * 937 * @return The public Elliptic Curve key. 938 * 939 * @throws JOSEException If EC is not supported by the underlying Java 940 * Cryptography (JCA) provider or if the JWK 941 * parameters are invalid for a public EC key. 942 */ 943 public ECPublicKey toECPublicKey(final Provider provider) 944 throws JOSEException { 945 946 ECParameterSpec spec = crv.toECParameterSpec(); 947 948 if (spec == null) { 949 throw new JOSEException("Couldn't get EC parameter spec for curve " + crv); 950 } 951 952 ECPoint w = new ECPoint(x.decodeToBigInteger(), y.decodeToBigInteger()); 953 954 ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, spec); 955 956 try { 957 KeyFactory keyFactory; 958 959 if (provider == null) { 960 keyFactory = KeyFactory.getInstance("EC"); 961 } else { 962 keyFactory = KeyFactory.getInstance("EC", provider); 963 } 964 965 return (ECPublicKey) keyFactory.generatePublic(publicKeySpec); 966 967 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 968 969 throw new JOSEException(e.getMessage(), e); 970 } 971 } 972 973 974 /** 975 * Returns a standard {@code java.security.interfaces.ECPrivateKey} 976 * representation of this Elliptic Curve JWK. Uses the default JCA 977 * provider. 978 * 979 * @return The private Elliptic Curve key, {@code null} if not 980 * specified by this JWK. 981 * 982 * @throws JOSEException If EC is not supported by the underlying Java 983 * Cryptography (JCA) provider or if the JWK 984 * parameters are invalid for a private EC key. 985 */ 986 public ECPrivateKey toECPrivateKey() 987 throws JOSEException { 988 989 return toECPrivateKey(null); 990 } 991 992 993 /** 994 * Returns a standard {@code java.security.interfaces.ECPrivateKey} 995 * representation of this Elliptic Curve JWK. 996 * 997 * @param provider The specific JCA provider to use, {@code null} 998 * implies the default one. 999 * 1000 * @return The private Elliptic Curve key, {@code null} if not 1001 * specified by this JWK. 1002 * 1003 * @throws JOSEException If EC is not supported by the underlying Java 1004 * Cryptography (JCA) provider or if the JWK 1005 * parameters are invalid for a private EC key. 1006 */ 1007 public ECPrivateKey toECPrivateKey(final Provider provider) 1008 throws JOSEException { 1009 1010 if (d == null) { 1011 // No private 'd' param 1012 return null; 1013 } 1014 1015 ECParameterSpec spec = crv.toECParameterSpec(); 1016 1017 if (spec == null) { 1018 throw new JOSEException("Couldn't get EC parameter spec for curve " + crv); 1019 } 1020 1021 ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d.decodeToBigInteger(), spec); 1022 1023 try { 1024 KeyFactory keyFactory; 1025 1026 if (provider == null) { 1027 keyFactory = KeyFactory.getInstance("EC"); 1028 } else { 1029 keyFactory = KeyFactory.getInstance("EC", provider); 1030 } 1031 1032 return (ECPrivateKey) keyFactory.generatePrivate(privateKeySpec); 1033 1034 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 1035 1036 throw new JOSEException(e.getMessage(), e); 1037 } 1038 } 1039 1040 1041 /** 1042 * Returns a standard {@code java.security.KeyPair} representation of 1043 * this Elliptic Curve JWK. Uses the default JCA provider. 1044 * 1045 * @return The Elliptic Curve key pair. The private Elliptic Curve key 1046 * will be {@code null} if not specified. 1047 * 1048 * @throws JOSEException If EC is not supported by the underlying Java 1049 * Cryptography (JCA) provider or if the JWK 1050 * parameters are invalid for a public and / or 1051 * private EC key. 1052 */ 1053 public KeyPair toKeyPair() 1054 throws JOSEException { 1055 1056 return toKeyPair(null); 1057 } 1058 1059 1060 /** 1061 * Returns a standard {@code java.security.KeyPair} representation of 1062 * this Elliptic Curve JWK. 1063 * 1064 * @param provider The specific JCA provider to use, {@code null} 1065 * implies the default one. 1066 * 1067 * @return The Elliptic Curve key pair. The private Elliptic Curve key 1068 * will be {@code null} if not specified. 1069 * 1070 * @throws JOSEException If EC is not supported by the underlying Java 1071 * Cryptography (JCA) provider or if the JWK 1072 * parameters are invalid for a public and / or 1073 * private EC key. 1074 */ 1075 public KeyPair toKeyPair(final Provider provider) 1076 throws JOSEException { 1077 1078 return new KeyPair(toECPublicKey(provider), toECPrivateKey(provider)); 1079 } 1080 1081 1082 @Override 1083 public LinkedHashMap<String,?> getRequiredParams() { 1084 1085 // Put mandatory params in sorted order 1086 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 1087 requiredParams.put("crv", crv.toString()); 1088 requiredParams.put("kty", getKeyType().getValue()); 1089 requiredParams.put("x", x.toString()); 1090 requiredParams.put("y", y.toString()); 1091 return requiredParams; 1092 } 1093 1094 1095 @Override 1096 public boolean isPrivate() { 1097 1098 return d != null; 1099 } 1100 1101 1102 /** 1103 * Returns a copy of this Elliptic Curve JWK with any private values 1104 * removed. 1105 * 1106 * @return The copied public Elliptic Curve JWK. 1107 */ 1108 @Override 1109 public ECKey toPublicJWK() { 1110 1111 return new ECKey(getCurve(), getX(), getY(), 1112 getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(), 1113 getX509CertURL(), getX509CertThumbprint(), getX509CertChain()); 1114 } 1115 1116 1117 @Override 1118 public JSONObject toJSONObject() { 1119 1120 JSONObject o = super.toJSONObject(); 1121 1122 // Append EC specific attributes 1123 o.put("crv", crv.toString()); 1124 o.put("x", x.toString()); 1125 o.put("y", y.toString()); 1126 1127 if (d != null) { 1128 o.put("d", d.toString()); 1129 } 1130 1131 return o; 1132 } 1133 1134 1135 /** 1136 * Parses a public / private Elliptic Curve JWK from the specified JSON 1137 * object string representation. 1138 * 1139 * @param s The JSON object string to parse. Must not be {@code null}. 1140 * 1141 * @return The public / private Elliptic Curve JWK. 1142 * 1143 * @throws ParseException If the string couldn't be parsed to an 1144 * Elliptic Curve JWK. 1145 */ 1146 public static ECKey parse(final String s) 1147 throws ParseException { 1148 1149 return parse(JSONObjectUtils.parseJSONObject(s)); 1150 } 1151 1152 1153 /** 1154 * Parses a public / private Elliptic Curve JWK from the specified JSON 1155 * object representation. 1156 * 1157 * @param jsonObject The JSON object to parse. Must not be 1158 * {@code null}. 1159 * 1160 * @return The public / private Elliptic Curve JWK. 1161 * 1162 * @throws ParseException If the JSON object couldn't be parsed to an 1163 * Elliptic Curve JWK. 1164 */ 1165 public static ECKey parse(final JSONObject jsonObject) 1166 throws ParseException { 1167 1168 // Parse the mandatory parameters first 1169 Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv")); 1170 Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x")); 1171 Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y")); 1172 1173 // Check key type 1174 KeyType kty = JWKMetadata.parseKeyType(jsonObject); 1175 1176 if (kty != KeyType.EC) { 1177 throw new ParseException("The key type \"kty\" must be EC", 0); 1178 } 1179 1180 // Get optional private key 1181 Base64URL d = null; 1182 if (jsonObject.get("d") != null) { 1183 d = new Base64URL(JSONObjectUtils.getString(jsonObject, "d")); 1184 } 1185 1186 1187 try { 1188 if (d == null) { 1189 // Public key 1190 return new ECKey(crv, x, y, 1191 JWKMetadata.parseKeyUse(jsonObject), 1192 JWKMetadata.parseKeyOperations(jsonObject), 1193 JWKMetadata.parseAlgorithm(jsonObject), 1194 JWKMetadata.parseKeyID(jsonObject), 1195 JWKMetadata.parseX509CertURL(jsonObject), 1196 JWKMetadata.parseX509CertThumbprint(jsonObject), 1197 JWKMetadata.parseX509CertChain(jsonObject)); 1198 1199 } else { 1200 // Key pair 1201 return new ECKey(crv, x, y, d, 1202 JWKMetadata.parseKeyUse(jsonObject), 1203 JWKMetadata.parseKeyOperations(jsonObject), 1204 JWKMetadata.parseAlgorithm(jsonObject), 1205 JWKMetadata.parseKeyID(jsonObject), 1206 JWKMetadata.parseX509CertURL(jsonObject), 1207 JWKMetadata.parseX509CertThumbprint(jsonObject), 1208 JWKMetadata.parseX509CertChain(jsonObject)); 1209 } 1210 1211 } catch (IllegalArgumentException ex) { 1212 1213 // Conflicting 'use' and 'key_ops' 1214 throw new ParseException(ex.getMessage(), 0); 1215 } 1216 } 1217}