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