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 public 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 (for a public key). 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 (for a 481 * non-public key). 482 * 483 * @param ops The key operations, {@code null} if not 484 * specified. 485 * 486 * @return This builder. 487 */ 488 public Builder keyOperations(final Set<KeyOperation> ops) { 489 490 this.ops = ops; 491 return this; 492 } 493 494 495 /** 496 * Sets the intended JOSE algorithm ({@code alg}) for the JWK. 497 * 498 * @param alg The intended JOSE algorithm, {@code null} if not 499 * specified. 500 * 501 * @return This builder. 502 */ 503 public Builder algorithm(final Algorithm alg) { 504 505 this.alg = alg; 506 return this; 507 } 508 509 /** 510 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 511 * to match a specific key. This can be used, for instance, to 512 * choose a key within a {@link JWKSet} during key rollover. 513 * The key ID may also correspond to a JWS/JWE {@code kid} 514 * header parameter value. 515 * 516 * @param kid The key ID, {@code null} if not specified. 517 * 518 * @return This builder. 519 */ 520 public Builder keyID(final String kid) { 521 522 this.kid = kid; 523 return this; 524 } 525 526 527 /** 528 * Sets the X.509 certificate URL ({@code x5u}) of the JWK. 529 * 530 * @param x5u The X.509 certificate URL, {@code null} if not 531 * specified. 532 * 533 * @return This builder. 534 */ 535 public Builder x509CertURL(final URL x5u) { 536 537 this.x5u = x5u; 538 return this; 539 } 540 541 542 /** 543 * Sets the X.509 certificate thumbprint ({@code x5t}) of the 544 * JWK. 545 * 546 * @param x5t The X.509 certificate thumbprint, {@code null} if 547 * not specified. 548 * 549 * @return This builder. 550 */ 551 public Builder x509CertThumbprint(final Base64URL x5t) { 552 553 this.x5t = x5t; 554 return this; 555 } 556 557 558 /** 559 * Sets the X.509 certificate chain ({@code x5c}) of the JWK. 560 * 561 * @param x5c The X.509 certificate chain as a unmodifiable 562 * list, {@code null} if not specified. 563 * 564 * @return This builder. 565 */ 566 public Builder x509CertChain(final List<Base64> x5c) { 567 568 this.x5c = x5c; 569 return this; 570 } 571 572 573 /** 574 * Builds a new octet sequence JWK. 575 * 576 * @return The octet sequence JWK. 577 * 578 * @throws IllegalStateException If the JWK parameters were 579 * inconsistently specified. 580 */ 581 public ECKey build() { 582 583 try { 584 if (d == null) { 585 // Public key 586 return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5c); 587 } 588 589 // Pair 590 return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5c); 591 592 } catch (IllegalArgumentException e) { 593 594 throw new IllegalStateException(e.getMessage(), e); 595 } 596 } 597 } 598 599 600 /** 601 * Returns the Base64URL encoding of the specified elliptic curve 'x', 602 * 'y' or 'd' coordinate, with leading zero padding up to the specified 603 * field size in bits. 604 * 605 * @param fieldSize The field size in bits. 606 * @param coordinate The elliptic curve coordinate. Must not be 607 * {@code null}. 608 * 609 * @return The Base64URL-encoded coordinate, with leading zero padding 610 * up to the curve's field size. 611 */ 612 public static Base64URL encodeCoordinate(final int fieldSize, final BigInteger coordinate) { 613 614 byte[] unpadded = BigIntegerUtils.toBytesUnsigned(coordinate); 615 616 int bytesToOutput = (fieldSize + 7)/8; 617 618 if (unpadded.length >= bytesToOutput) { 619 // Greater-than check to prevent exception on malformed 620 // key below 621 return Base64URL.encode(unpadded); 622 } 623 624 byte[] padded = new byte[bytesToOutput]; 625 626 System.arraycopy(unpadded, 0, padded, bytesToOutput - unpadded.length, unpadded.length); 627 628 return Base64URL.encode(padded); 629 } 630 631 632 /** 633 * The curve name. 634 */ 635 private final Curve crv; 636 637 638 /** 639 * The public 'x' EC coordinate. 640 */ 641 private final Base64URL x; 642 643 644 /** 645 * The public 'y' EC coordinate. 646 */ 647 private final Base64URL y; 648 649 650 /** 651 * The private 'd' EC coordinate 652 */ 653 private final Base64URL d; 654 655 656 /** 657 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 658 * specified parameters. 659 * 660 * @param crv The cryptographic curve. Must not be {@code null}. 661 * @param x The public 'x' 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 y The public 'y' coordinate for the elliptic curve point. 666 * It is represented as the Base64URL encoding of the 667 * coordinate's big endian representation. Must not be 668 * {@code null}. 669 * @param use The public key use, {@code null} if not specified or if 670 * the key is intended for signing as well as encryption. 671 * @param ops The key operations, {@code null} if not specified. 672 * @param alg The intended JOSE algorithm for the key, {@code null} if 673 * not specified. 674 * @param kid The key ID, {@code null} if not specified. 675 * @param x5u The X.509 certificate URL, {@code null} if not specified. 676 * @param x5t The X.509 certificate thumbprint, {@code null} if not 677 * specified. 678 * @param x5c The X.509 certificate chain, {@code null} if not 679 * specified. 680 */ 681 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 682 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 683 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 684 685 super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c); 686 687 if (crv == null) { 688 throw new IllegalArgumentException("The curve must not be null"); 689 } 690 691 this.crv = crv; 692 693 if (x == null) { 694 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 695 } 696 697 this.x = x; 698 699 if (y == null) { 700 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 701 } 702 703 this.y = y; 704 705 this.d = null; 706 } 707 708 709 /** 710 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 711 * with the specified parameters. 712 * 713 * @param crv The cryptographic curve. Must not be {@code null}. 714 * @param x The public 'x' coordinate for the elliptic curve point. 715 * It is represented as the Base64URL encoding of the 716 * coordinate's big endian representation. Must not be 717 * {@code null}. 718 * @param y The public 'y' coordinate for the elliptic curve point. 719 * It is represented as the Base64URL encoding of the 720 * coordinate's big endian representation. Must not be 721 * {@code null}. 722 * @param d The private 'd' coordinate for the elliptic curve point. 723 * It is represented as the Base64URL encoding of the 724 * coordinate's big endian representation. Must not be 725 * {@code null}. 726 * @param use The public key use, {@code null} if not specified or if 727 * the key is intended for signing as well as encryption. 728 * @param ops The key operations, {@code null} if not specified. 729 * @param alg The intended JOSE algorithm for the key, {@code null} if 730 * not specified. 731 * @param kid The key ID, {@code null} if not specified. 732 * @param x5u The X.509 certificate URL, {@code null} if not specified. 733 * @param x5t The X.509 certificate thumbprint, {@code null} if not 734 * specified. 735 * @param x5c The X.509 certificate chain, {@code null} if not 736 * specified. 737 */ 738 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final Base64URL d, 739 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 740 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 741 742 super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c); 743 744 if (crv == null) { 745 throw new IllegalArgumentException("The curve must not be null"); 746 } 747 748 this.crv = crv; 749 750 if (x == null) { 751 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 752 } 753 754 this.x = x; 755 756 if (y == null) { 757 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 758 } 759 760 this.y = y; 761 762 if (d == null) { 763 throw new IllegalArgumentException("The 'd' coordinate must not be null"); 764 } 765 766 this.d = d; 767 } 768 769 770 /** 771 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 772 * specified parameters. 773 * 774 * @param crv The cryptographic curve. Must not be {@code null}. 775 * @param pub The public EC key to represent. Must not be {@code null}. 776 * @param use The public key use, {@code null} if not specified or if 777 * the key is intended for signing as well as encryption. 778 * @param ops The key operations, {@code null} if not specified. 779 * @param alg The intended JOSE algorithm for the key, {@code null} if 780 * not specified. 781 * @param kid The key ID, {@code null} if not specified. 782 * @param x5u The X.509 certificate URL, {@code null} if not specified. 783 * @param x5t The X.509 certificate thumbprint, {@code null} if not 784 * specified. 785 * @param x5c The X.509 certificate chain, {@code null} if not 786 * specified. 787 */ 788 public ECKey(final Curve crv, final ECPublicKey pub, 789 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 790 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 791 792 this(crv, 793 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 794 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()), 795 use, ops, alg, kid, 796 x5u, x5t, x5c); 797 } 798 799 800 /** 801 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 802 * with the specified parameters. 803 * 804 * @param crv The cryptographic curve. Must not be {@code null}. 805 * @param pub The public EC key to represent. Must not be 806 * {@code null}. 807 * @param priv The private EC key to represent. Must not be 808 * {@code null}. 809 * @param use The public key use, {@code null} if not specified or if 810 * the key is intended for signing as well as encryption. 811 * @param ops The key operations, {@code null} if not specified. 812 * @param alg The intended JOSE algorithm for the key, {@code null} if 813 * not specified. 814 * @param kid The key ID, {@code null} if not specified. 815 * @param x5u The X.509 certificate URL, {@code null} if not 816 * specified. 817 * @param x5t The X.509 certificate thumbprint, {@code null} if not 818 * specified. 819 * @param x5c The X.509 certificate chain, {@code null} if not 820 * specified. 821 */ 822 public ECKey(final Curve crv, final ECPublicKey pub, final ECPrivateKey priv, 823 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 824 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 825 826 this(crv, 827 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 828 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()), 829 encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()), 830 use, ops, alg, kid, 831 x5u, x5t, x5c); 832 } 833 834 835 /** 836 * Gets the cryptographic curve. 837 * 838 * @return The cryptographic curve. 839 */ 840 public Curve getCurve() { 841 842 return crv; 843 } 844 845 846 /** 847 * Gets the public 'x' coordinate for the elliptic curve point. 848 * 849 * @return The 'x' coordinate. It is represented as the Base64URL 850 * encoding of the coordinate's big endian representation. 851 */ 852 public Base64URL getX() { 853 854 return x; 855 } 856 857 858 /** 859 * Gets the public 'y' coordinate for the elliptic curve point. 860 * 861 * @return The 'y' coordinate. It is represented as the Base64URL 862 * encoding of the coordinate's big endian representation. 863 */ 864 public Base64URL getY() { 865 866 return y; 867 } 868 869 870 /** 871 * Gets the private 'd' coordinate for the elliptic curve point. It is 872 * represented as the Base64URL encoding of the coordinate's big endian 873 * representation. 874 * 875 * @return The 'd' coordinate. It is represented as the Base64URL 876 * encoding of the coordinate's big endian representation. 877 * {@code null} if not specified (for a public key). 878 */ 879 public Base64URL getD() { 880 881 return d; 882 } 883 884 885 /** 886 * Gets a new BouncyCastle.org EC key factory. 887 * 888 * @return The EC key factory. 889 * 890 * @throws NoSuchAlgorithmException If a JCA provider or algorithm 891 * exception was encountered. 892 */ 893 private static KeyFactory getECKeyFactory() 894 throws NoSuchAlgorithmException { 895 896 return KeyFactory.getInstance("EC", BouncyCastleProviderSingleton.getInstance()); 897 } 898 899 900 /** 901 * Returns a standard {@code java.security.interfaces.ECPublicKey} 902 * representation of this Elliptic Curve JWK. 903 * 904 * @return The public Elliptic Curve key. 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 EC key. 911 */ 912 public ECPublicKey toECPublicKey() 913 throws NoSuchAlgorithmException, InvalidKeySpecException { 914 915 ECParameterSpec spec = crv.toECParameterSpec(); 916 917 if (spec == null) { 918 throw new NoSuchAlgorithmException("Couldn't get EC parameter spec for curve " + crv); 919 } 920 921 ECPoint w = new ECPoint(x.decodeToBigInteger(), y.decodeToBigInteger()); 922 923 ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, spec); 924 925 KeyFactory keyFactory = getECKeyFactory(); 926 927 return (ECPublicKey)keyFactory.generatePublic(publicKeySpec); 928 } 929 930 931 /** 932 * Returns a standard {@code java.security.interfaces.ECPrivateKey} 933 * representation of this Elliptic Curve JWK. 934 * 935 * @return The private Elliptic Curve key, {@code null} if not 936 * specified by this JWK. 937 * 938 * @throws NoSuchAlgorithmException If EC is not supported by the 939 * underlying Java Cryptography (JCA) 940 * provider. 941 * @throws InvalidKeySpecException If the JWK key parameters are 942 * invalid for a private EC key. 943 */ 944 public ECPrivateKey toECPrivateKey() 945 throws NoSuchAlgorithmException, InvalidKeySpecException { 946 947 if (d == null) { 948 // No private 'd' param 949 return null; 950 } 951 952 ECParameterSpec spec = crv.toECParameterSpec(); 953 954 if (spec == null) { 955 throw new NoSuchAlgorithmException("Couldn't get EC parameter spec for curve " + crv); 956 } 957 958 ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d.decodeToBigInteger(), spec); 959 960 KeyFactory keyFactory = getECKeyFactory(); 961 962 return (ECPrivateKey)keyFactory.generatePrivate(privateKeySpec); 963 } 964 965 966 /** 967 * Returns a standard {@code java.security.KeyPair} representation of 968 * this Elliptic Curve JWK. 969 * 970 * @return The Elliptic Curve key pair. The private Elliptic Curve key 971 * will be {@code null} if not specified. 972 * 973 * @throws NoSuchAlgorithmException If EC is not supported by the 974 * underlying Java Cryptography (JCA) 975 * provider. 976 * @throws InvalidKeySpecException If the JWK key parameters are 977 * invalid for a public and / or 978 * private EC key. 979 */ 980 public KeyPair toKeyPair() 981 throws NoSuchAlgorithmException, InvalidKeySpecException { 982 983 return new KeyPair(toECPublicKey(), toECPrivateKey()); 984 } 985 986 987 @Override 988 public boolean isPrivate() { 989 990 return d != null; 991 } 992 993 994 /** 995 * Returns a copy of this Elliptic Curve JWK with any private values 996 * removed. 997 * 998 * @return The copied public Elliptic Curve JWK. 999 */ 1000 @Override 1001 public ECKey toPublicJWK() { 1002 1003 return new ECKey(getCurve(), getX(), getY(), 1004 getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(), 1005 getX509CertURL(), getX509CertThumbprint(), getX509CertChain()); 1006 } 1007 1008 1009 @Override 1010 public JSONObject toJSONObject() { 1011 1012 JSONObject o = super.toJSONObject(); 1013 1014 // Append EC specific attributes 1015 o.put("crv", crv.toString()); 1016 o.put("x", x.toString()); 1017 o.put("y", y.toString()); 1018 1019 if (d != null) { 1020 o.put("d", d.toString()); 1021 } 1022 1023 return o; 1024 } 1025 1026 1027 /** 1028 * Parses a public / private Elliptic Curve JWK from the specified JSON 1029 * object string representation. 1030 * 1031 * @param s The JSON object string to parse. Must not be {@code null}. 1032 * 1033 * @return The public / private Elliptic Curve JWK. 1034 * 1035 * @throws ParseException If the string couldn't be parsed to an 1036 * Elliptic Curve JWK. 1037 */ 1038 public static ECKey parse(final String s) 1039 throws ParseException { 1040 1041 return parse(JSONObjectUtils.parseJSONObject(s)); 1042 } 1043 1044 1045 /** 1046 * Parses a public / private Elliptic Curve JWK from the specified JSON 1047 * object representation. 1048 * 1049 * @param jsonObject The JSON object to parse. Must not be 1050 * {@code null}. 1051 * 1052 * @return The public / private Elliptic Curve JWK. 1053 * 1054 * @throws ParseException If the JSON object couldn't be parsed to an 1055 * Elliptic Curve JWK. 1056 */ 1057 public static ECKey parse(final JSONObject jsonObject) 1058 throws ParseException { 1059 1060 // Parse the mandatory parameters first 1061 Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv")); 1062 Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x")); 1063 Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y")); 1064 1065 // Check key type 1066 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 1067 1068 if (kty != KeyType.EC) { 1069 throw new ParseException("The key type \"kty\" must be EC", 0); 1070 } 1071 1072 // Get optional private key 1073 Base64URL d = null; 1074 if (jsonObject.get("d") != null) { 1075 d = new Base64URL(JSONObjectUtils.getString(jsonObject, "d")); 1076 } 1077 1078 // Get optional key use 1079 KeyUse use = null; 1080 1081 if (jsonObject.containsKey("use")) { 1082 use = KeyUse.parse(JSONObjectUtils.getString(jsonObject, "use")); 1083 } 1084 1085 // Get optional key operations 1086 Set<KeyOperation> ops = null; 1087 1088 if (jsonObject.containsKey("key_ops")) { 1089 ops = KeyOperation.parse(JSONObjectUtils.getStringList(jsonObject, "key_ops")); 1090 } 1091 1092 // Get optional intended algorithm 1093 Algorithm alg = null; 1094 1095 if (jsonObject.containsKey("alg")) { 1096 alg = new Algorithm(JSONObjectUtils.getString(jsonObject, "alg")); 1097 } 1098 1099 // Get optional key ID 1100 String kid = null; 1101 1102 if (jsonObject.containsKey("kid")) { 1103 kid = JSONObjectUtils.getString(jsonObject, "kid"); 1104 } 1105 1106 // Get optional X.509 cert URL 1107 URL x5u = null; 1108 1109 if (jsonObject.containsKey("x5u")) { 1110 x5u = JSONObjectUtils.getURL(jsonObject, "x5u"); 1111 } 1112 1113 // Get optional X.509 cert thumbprint 1114 Base64URL x5t = null; 1115 1116 if (jsonObject.containsKey("x5t")) { 1117 x5t = new Base64URL(JSONObjectUtils.getString(jsonObject, "x5t")); 1118 } 1119 1120 // Get optional X.509 cert chain 1121 List<Base64> x5c = null; 1122 1123 if (jsonObject.containsKey("x5c")) { 1124 x5c = X509CertChainUtils.parseX509CertChain(JSONObjectUtils.getJSONArray(jsonObject, "x5c")); 1125 } 1126 1127 try { 1128 if (d == null) { 1129 // Public key 1130 return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5c); 1131 1132 } else { 1133 // Key pair 1134 return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5c); 1135 } 1136 1137 } catch (IllegalArgumentException ex) { 1138 1139 // Conflicting 'use' and 'key_ops' 1140 throw new ParseException(ex.getMessage(), 0); 1141 } 1142 } 1143}