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