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