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} or 238 * empty. 239 * 240 * @return The cryptographic curve. 241 */ 242 public static Curve parse(final String s) { 243 244 if (s == null || s.trim().isEmpty()) { 245 throw new IllegalArgumentException("The cryptographic curve string must not be null or empty"); 246 } 247 248 if (s.equals(P_256.getName())) { 249 return P_256; 250 251 } else if (s.equals(P_384.getName())) { 252 return P_384; 253 254 } else if (s.equals(P_521.getName())) { 255 return P_521; 256 257 } else { 258 return new Curve(s); 259 } 260 } 261 262 263 /** 264 * Gets the cryptographic curve for the specified standard 265 * (JCA) name. 266 * 267 * @param stdName The standard (JCA) name. Must not be 268 * {@code null}. 269 * 270 * @throws IllegalArgumentException If no matching JOSE curve 271 * constant could be found. 272 */ 273 public static Curve forStdName(final String stdName) { 274 if( "secp256r1".equals(stdName) ) { 275 return P_256; 276 } else if( "secp384r1".equals(stdName) ) { 277 return P_384; 278 } else if( "secp521r1".equals(stdName) ) { 279 return P_521; 280 } else { 281 throw new IllegalArgumentException("No matching curve constant for standard (JCA) name " + stdName); 282 } 283 } 284 } 285 286 287 /** 288 * Builder for constructing Elliptic Curve JWKs. 289 * 290 * <p>Example use: 291 * 292 * <pre> 293 * ECKey key = new ECKey.Builder(Curve.P521, x, y). 294 * d(d). 295 * algorithm(JWSAlgorithm.ES512). 296 * keyID("789"). 297 * build(); 298 * </pre> 299 */ 300 public static class Builder { 301 302 303 /** 304 * The curve name. 305 */ 306 private final Curve crv; 307 308 309 /** 310 * The public 'x' EC coordinate. 311 */ 312 private final Base64URL x; 313 314 315 /** 316 * The public 'y' EC coordinate. 317 */ 318 private final Base64URL y; 319 320 321 /** 322 * The private 'd' EC coordinate, optional. 323 */ 324 private Base64URL d; 325 326 327 /** 328 * The key use, optional. 329 */ 330 private KeyUse use; 331 332 333 /** 334 * The key operations, optional. 335 */ 336 private Set<KeyOperation> ops; 337 338 339 /** 340 * The intended JOSE algorithm for the key, optional. 341 */ 342 private Algorithm alg; 343 344 345 /** 346 * The key ID, optional. 347 */ 348 private String kid; 349 350 351 /** 352 * X.509 certificate URL, optional. 353 */ 354 private URL x5u; 355 356 357 /** 358 * X.509 certificate thumbprint, optional. 359 */ 360 private Base64URL x5t; 361 362 363 /** 364 * The X.509 certificate chain, optional. 365 */ 366 private List<Base64> x5c; 367 368 369 /** 370 * Creates a new Elliptic Curve JWK builder. 371 * 372 * @param crv The cryptographic curve. Must not be 373 * {@code null}. 374 * @param x The public 'x' coordinate for the elliptic curve 375 * point. It is represented as the Base64URL 376 * encoding of the coordinate's big endian 377 * representation. Must not be {@code null}. 378 * @param y The public 'y' coordinate for the elliptic curve 379 * point. It is represented as the Base64URL 380 * encoding of the coordinate's big endian 381 * representation. Must not be {@code null}. 382 */ 383 public Builder(final Curve crv, final Base64URL x, final Base64URL y) { 384 385 if (crv == null) { 386 throw new IllegalArgumentException("The curve must not be null"); 387 } 388 389 this.crv = crv; 390 391 if (x == null) { 392 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 393 } 394 395 this.x = x; 396 397 if (y == null) { 398 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 399 } 400 401 this.y = y; 402 } 403 404 405 /** 406 * Creates a new Elliptic Curve JWK builder. 407 * 408 * @param crv The cryptographic curve. Must not be 409 * {@code null}. 410 * @param pub The public EC key to represent. Must not be 411 * {@code null}. 412 */ 413 public Builder(final Curve crv, final ECPublicKey pub) { 414 415 this(crv, 416 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 417 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY())); 418 } 419 420 421 /** 422 * Sets the private 'd' coordinate for the elliptic curve 423 * point. The alternative method is {@link #privateKey}. 424 * 425 * @param d The 'd' coordinate. It is represented as the 426 * Base64URL encoding of the coordinate's big endian 427 * representation. {@code null} if not specified (for 428 * a public key). 429 * 430 * @return This builder. 431 */ 432 public Builder d(final Base64URL d) { 433 434 this.d = d; 435 return this; 436 } 437 438 439 /** 440 * Sets the private Elliptic Curve key. The alternative method 441 * is {@link #d}. 442 * 443 * @param priv The private EC key, used to obtain the private 444 * 'd' coordinate for the elliptic curve point. 445 * {@code null} if not specified (for a public 446 * key). 447 * 448 * @return This builder. 449 */ 450 public Builder privateKey(final ECPrivateKey priv) { 451 452 if (priv != null) { 453 this.d = encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()); 454 } 455 456 return this; 457 } 458 459 460 /** 461 * Sets the use ({@code use}) of the JWK. 462 * 463 * @param use The key use, {@code null} if not specified or if 464 * the key is intended for signing as well as 465 * encryption. 466 * 467 * @return This builder. 468 */ 469 public Builder keyUse(final KeyUse use) { 470 471 this.use = use; 472 return this; 473 } 474 475 476 /** 477 * Sets the operations ({@code key_ops}) of the JWK. 478 * 479 * @param ops The key operations, {@code null} if not 480 * specified. 481 * 482 * @return This builder. 483 */ 484 public Builder keyOperations(final Set<KeyOperation> ops) { 485 486 this.ops = ops; 487 return this; 488 } 489 490 491 /** 492 * Sets the intended JOSE algorithm ({@code alg}) for the JWK. 493 * 494 * @param alg The intended JOSE algorithm, {@code null} if not 495 * specified. 496 * 497 * @return This builder. 498 */ 499 public Builder algorithm(final Algorithm alg) { 500 501 this.alg = alg; 502 return this; 503 } 504 505 /** 506 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 507 * to match a specific key. This can be used, for instance, to 508 * choose a key within a {@link JWKSet} during key rollover. 509 * The key ID may also correspond to a JWS/JWE {@code kid} 510 * header parameter value. 511 * 512 * @param kid The key ID, {@code null} if not specified. 513 * 514 * @return This builder. 515 */ 516 public Builder keyID(final String kid) { 517 518 this.kid = kid; 519 return this; 520 } 521 522 523 /** 524 * Sets the X.509 certificate URL ({@code x5u}) of the JWK. 525 * 526 * @param x5u The X.509 certificate URL, {@code null} if not 527 * specified. 528 * 529 * @return This builder. 530 */ 531 public Builder x509CertURL(final URL x5u) { 532 533 this.x5u = x5u; 534 return this; 535 } 536 537 538 /** 539 * Sets the X.509 certificate thumbprint ({@code x5t}) of the 540 * JWK. 541 * 542 * @param x5t The X.509 certificate thumbprint, {@code null} if 543 * not specified. 544 * 545 * @return This builder. 546 */ 547 public Builder x509CertThumbprint(final Base64URL x5t) { 548 549 this.x5t = x5t; 550 return this; 551 } 552 553 554 /** 555 * Sets the X.509 certificate chain ({@code x5c}) of the JWK. 556 * 557 * @param x5c The X.509 certificate chain as a unmodifiable 558 * list, {@code null} if not specified. 559 * 560 * @return This builder. 561 */ 562 public Builder x509CertChain(final List<Base64> x5c) { 563 564 this.x5c = x5c; 565 return this; 566 } 567 568 569 /** 570 * Builds a new octet sequence JWK. 571 * 572 * @return The octet sequence JWK. 573 * 574 * @throws IllegalStateException If the JWK parameters were 575 * inconsistently specified. 576 */ 577 public ECKey build() { 578 579 try { 580 if (d == null) { 581 // Public key 582 return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5c); 583 } 584 585 // Pair 586 return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5c); 587 588 } catch (IllegalArgumentException e) { 589 590 throw new IllegalStateException(e.getMessage(), e); 591 } 592 } 593 } 594 595 596 /** 597 * Returns the Base64URL encoding of the specified elliptic curve 'x', 598 * 'y' or 'd' coordinate, with leading zero padding up to the specified 599 * field size in bits. 600 * 601 * @param fieldSize The field size in bits. 602 * @param coordinate The elliptic curve coordinate. Must not be 603 * {@code null}. 604 * 605 * @return The Base64URL-encoded coordinate, with leading zero padding 606 * up to the curve's field size. 607 */ 608 public static Base64URL encodeCoordinate(final int fieldSize, final BigInteger coordinate) { 609 610 byte[] unpadded = BigIntegerUtils.toBytesUnsigned(coordinate); 611 612 int bytesToOutput = (fieldSize + 7)/8; 613 614 if (unpadded.length >= bytesToOutput) { 615 // Greater-than check to prevent exception on malformed 616 // key below 617 return Base64URL.encode(unpadded); 618 } 619 620 byte[] padded = new byte[bytesToOutput]; 621 622 System.arraycopy(unpadded, 0, padded, bytesToOutput - unpadded.length, unpadded.length); 623 624 return Base64URL.encode(padded); 625 } 626 627 628 /** 629 * The curve name. 630 */ 631 private final Curve crv; 632 633 634 /** 635 * The public 'x' EC coordinate. 636 */ 637 private final Base64URL x; 638 639 640 /** 641 * The public 'y' EC coordinate. 642 */ 643 private final Base64URL y; 644 645 646 /** 647 * The private 'd' EC coordinate 648 */ 649 private final Base64URL d; 650 651 652 /** 653 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 654 * specified parameters. 655 * 656 * @param crv The cryptographic curve. Must not be {@code null}. 657 * @param x The public 'x' 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 y The public 'y' 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 or if the key 666 * is intended for signing as well as encryption. 667 * @param ops The key operations, {@code null} if not specified. 668 * @param alg The intended JOSE algorithm for the key, {@code null} if 669 * not specified. 670 * @param kid The key ID, {@code null} if not specified. 671 * @param x5u The X.509 certificate URL, {@code null} if not specified. 672 * @param x5t The X.509 certificate thumbprint, {@code null} if not 673 * specified. 674 * @param x5c The X.509 certificate chain, {@code null} if not 675 * specified. 676 */ 677 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 678 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 679 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 680 681 super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c); 682 683 if (crv == null) { 684 throw new IllegalArgumentException("The curve must not be null"); 685 } 686 687 this.crv = crv; 688 689 if (x == null) { 690 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 691 } 692 693 this.x = x; 694 695 if (y == null) { 696 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 697 } 698 699 this.y = y; 700 701 this.d = null; 702 } 703 704 705 /** 706 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 707 * with the specified parameters. 708 * 709 * @param crv The cryptographic curve. Must not be {@code null}. 710 * @param x The public 'x' coordinate for the elliptic curve point. 711 * It is represented as the Base64URL encoding of the 712 * coordinate's big endian representation. Must not be 713 * {@code null}. 714 * @param y The public 'y' 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 d The private 'd' 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 use The key use, {@code null} if not specified or if the key 723 * is intended for signing as well as encryption. 724 * @param ops The key operations, {@code null} if not specified. 725 * @param alg The intended JOSE algorithm for the key, {@code null} if 726 * not specified. 727 * @param kid The key ID, {@code null} if not specified. 728 * @param x5u The X.509 certificate URL, {@code null} if not specified. 729 * @param x5t The X.509 certificate thumbprint, {@code null} if not 730 * specified. 731 * @param x5c The X.509 certificate chain, {@code null} if not 732 * specified. 733 */ 734 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final Base64URL d, 735 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 736 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 737 738 super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c); 739 740 if (crv == null) { 741 throw new IllegalArgumentException("The curve must not be null"); 742 } 743 744 this.crv = crv; 745 746 if (x == null) { 747 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 748 } 749 750 this.x = x; 751 752 if (y == null) { 753 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 754 } 755 756 this.y = y; 757 758 if (d == null) { 759 throw new IllegalArgumentException("The 'd' coordinate must not be null"); 760 } 761 762 this.d = d; 763 } 764 765 766 /** 767 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 768 * specified parameters. 769 * 770 * @param crv The cryptographic curve. Must not be {@code null}. 771 * @param pub The public EC key to represent. Must not be {@code null}. 772 * @param use The key use, {@code null} if not specified or if the key 773 * is intended for signing as well as encryption. 774 * @param ops The key operations, {@code null} if not specified. 775 * @param alg The intended JOSE algorithm for the key, {@code null} if 776 * not specified. 777 * @param kid The key ID, {@code null} if not specified. 778 * @param x5u The X.509 certificate URL, {@code null} if not specified. 779 * @param x5t The X.509 certificate thumbprint, {@code null} if not 780 * specified. 781 * @param x5c The X.509 certificate chain, {@code null} if not 782 * specified. 783 */ 784 public ECKey(final Curve crv, final ECPublicKey pub, 785 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 786 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 787 788 this(crv, 789 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 790 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()), 791 use, ops, alg, kid, 792 x5u, x5t, x5c); 793 } 794 795 796 /** 797 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 798 * with the specified parameters. 799 * 800 * @param crv The cryptographic curve. Must not be {@code null}. 801 * @param pub The public EC key to represent. Must not be 802 * {@code null}. 803 * @param priv The private EC key to represent. Must not be 804 * {@code null}. 805 * @param use The key use, {@code null} if not specified or if the key 806 * is intended for signing as well as encryption. 807 * @param ops The key operations, {@code null} if not specified. 808 * @param alg The intended JOSE algorithm for the key, {@code null} if 809 * not specified. 810 * @param kid The key ID, {@code null} if not specified. 811 * @param x5u The X.509 certificate URL, {@code null} if not 812 * specified. 813 * @param x5t The X.509 certificate thumbprint, {@code null} if not 814 * specified. 815 * @param x5c The X.509 certificate chain, {@code null} if not 816 * specified. 817 */ 818 public ECKey(final Curve crv, final ECPublicKey pub, final ECPrivateKey priv, 819 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 820 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 821 822 this(crv, 823 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 824 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()), 825 encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()), 826 use, ops, alg, kid, 827 x5u, x5t, x5c); 828 } 829 830 831 /** 832 * Gets the cryptographic curve. 833 * 834 * @return The cryptographic curve. 835 */ 836 public Curve getCurve() { 837 838 return crv; 839 } 840 841 842 /** 843 * Gets the public 'x' coordinate for the elliptic curve point. 844 * 845 * @return The 'x' coordinate. It is represented as the Base64URL 846 * encoding of the coordinate's big endian representation. 847 */ 848 public Base64URL getX() { 849 850 return x; 851 } 852 853 854 /** 855 * Gets the public 'y' coordinate for the elliptic curve point. 856 * 857 * @return The 'y' coordinate. It is represented as the Base64URL 858 * encoding of the coordinate's big endian representation. 859 */ 860 public Base64URL getY() { 861 862 return y; 863 } 864 865 866 /** 867 * Gets the private 'd' coordinate for the elliptic curve point. It is 868 * represented as the Base64URL encoding of the coordinate's big endian 869 * representation. 870 * 871 * @return The 'd' coordinate. It is represented as the Base64URL 872 * encoding of the coordinate's big endian representation. 873 * {@code null} if not specified (for a public key). 874 */ 875 public Base64URL getD() { 876 877 return d; 878 } 879 880 881 /** 882 * Gets a new BouncyCastle.org EC key factory. 883 * 884 * @return The EC key factory. 885 * 886 * @throws NoSuchAlgorithmException If a JCA provider or algorithm 887 * exception was encountered. 888 */ 889 private static KeyFactory getECKeyFactory() 890 throws NoSuchAlgorithmException { 891 892 return KeyFactory.getInstance("EC", BouncyCastleProviderSingleton.getInstance()); 893 } 894 895 896 /** 897 * Returns a standard {@code java.security.interfaces.ECPublicKey} 898 * representation of this Elliptic Curve JWK. 899 * 900 * @return The public Elliptic Curve key. 901 * 902 * @throws NoSuchAlgorithmException If EC is not supported by the 903 * underlying Java Cryptography (JCA) 904 * provider. 905 * @throws InvalidKeySpecException If the JWK key parameters are 906 * invalid for a public EC key. 907 */ 908 public ECPublicKey toECPublicKey() 909 throws NoSuchAlgorithmException, InvalidKeySpecException { 910 911 ECParameterSpec spec = crv.toECParameterSpec(); 912 913 if (spec == null) { 914 throw new NoSuchAlgorithmException("Couldn't get EC parameter spec for curve " + crv); 915 } 916 917 ECPoint w = new ECPoint(x.decodeToBigInteger(), y.decodeToBigInteger()); 918 919 ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, spec); 920 921 KeyFactory keyFactory = getECKeyFactory(); 922 923 return (ECPublicKey)keyFactory.generatePublic(publicKeySpec); 924 } 925 926 927 /** 928 * Returns a standard {@code java.security.interfaces.ECPrivateKey} 929 * representation of this Elliptic Curve JWK. 930 * 931 * @return The private Elliptic Curve key, {@code null} if not 932 * specified by this JWK. 933 * 934 * @throws NoSuchAlgorithmException If EC is not supported by the 935 * underlying Java Cryptography (JCA) 936 * provider. 937 * @throws InvalidKeySpecException If the JWK key parameters are 938 * invalid for a private EC key. 939 */ 940 public ECPrivateKey toECPrivateKey() 941 throws NoSuchAlgorithmException, InvalidKeySpecException { 942 943 if (d == null) { 944 // No private 'd' param 945 return null; 946 } 947 948 ECParameterSpec spec = crv.toECParameterSpec(); 949 950 if (spec == null) { 951 throw new NoSuchAlgorithmException("Couldn't get EC parameter spec for curve " + crv); 952 } 953 954 ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d.decodeToBigInteger(), spec); 955 956 KeyFactory keyFactory = getECKeyFactory(); 957 958 return (ECPrivateKey)keyFactory.generatePrivate(privateKeySpec); 959 } 960 961 962 /** 963 * Returns a standard {@code java.security.KeyPair} representation of 964 * this Elliptic Curve JWK. 965 * 966 * @return The Elliptic Curve key pair. The private Elliptic Curve key 967 * will be {@code null} if not specified. 968 * 969 * @throws NoSuchAlgorithmException If EC is not supported by the 970 * underlying Java Cryptography (JCA) 971 * provider. 972 * @throws InvalidKeySpecException If the JWK key parameters are 973 * invalid for a public and / or 974 * private EC key. 975 */ 976 public KeyPair toKeyPair() 977 throws NoSuchAlgorithmException, InvalidKeySpecException { 978 979 return new KeyPair(toECPublicKey(), toECPrivateKey()); 980 } 981 982 983 @Override 984 public boolean isPrivate() { 985 986 return d != null; 987 } 988 989 990 /** 991 * Returns a copy of this Elliptic Curve JWK with any private values 992 * removed. 993 * 994 * @return The copied public Elliptic Curve JWK. 995 */ 996 @Override 997 public ECKey toPublicJWK() { 998 999 return new ECKey(getCurve(), getX(), getY(), 1000 getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(), 1001 getX509CertURL(), getX509CertThumbprint(), getX509CertChain()); 1002 } 1003 1004 1005 @Override 1006 public JSONObject toJSONObject() { 1007 1008 JSONObject o = super.toJSONObject(); 1009 1010 // Append EC specific attributes 1011 o.put("crv", crv.toString()); 1012 o.put("x", x.toString()); 1013 o.put("y", y.toString()); 1014 1015 if (d != null) { 1016 o.put("d", d.toString()); 1017 } 1018 1019 return o; 1020 } 1021 1022 1023 /** 1024 * Parses a public / private Elliptic Curve JWK from the specified JSON 1025 * object string representation. 1026 * 1027 * @param s The JSON object string to parse. Must not be {@code null}. 1028 * 1029 * @return The public / private Elliptic Curve JWK. 1030 * 1031 * @throws ParseException If the string couldn't be parsed to an 1032 * Elliptic Curve JWK. 1033 */ 1034 public static ECKey parse(final String s) 1035 throws ParseException { 1036 1037 return parse(JSONObjectUtils.parseJSONObject(s)); 1038 } 1039 1040 1041 /** 1042 * Parses a public / private Elliptic Curve JWK from the specified JSON 1043 * object representation. 1044 * 1045 * @param jsonObject The JSON object to parse. Must not be 1046 * {@code null}. 1047 * 1048 * @return The public / private Elliptic Curve JWK. 1049 * 1050 * @throws ParseException If the JSON object couldn't be parsed to an 1051 * Elliptic Curve JWK. 1052 */ 1053 public static ECKey parse(final JSONObject jsonObject) 1054 throws ParseException { 1055 1056 // Parse the mandatory parameters first 1057 Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv")); 1058 Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x")); 1059 Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y")); 1060 1061 // Check key type 1062 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 1063 1064 if (kty != KeyType.EC) { 1065 throw new ParseException("The key type \"kty\" must be EC", 0); 1066 } 1067 1068 // Get optional private key 1069 Base64URL d = null; 1070 if (jsonObject.get("d") != null) { 1071 d = new Base64URL(JSONObjectUtils.getString(jsonObject, "d")); 1072 } 1073 1074 // Get optional key use 1075 KeyUse use = null; 1076 1077 if (jsonObject.containsKey("use")) { 1078 use = KeyUse.parse(JSONObjectUtils.getString(jsonObject, "use")); 1079 } 1080 1081 // Get optional key operations 1082 Set<KeyOperation> ops = null; 1083 1084 if (jsonObject.containsKey("key_ops")) { 1085 ops = KeyOperation.parse(JSONObjectUtils.getStringList(jsonObject, "key_ops")); 1086 } 1087 1088 // Get optional intended algorithm 1089 Algorithm alg = null; 1090 1091 if (jsonObject.containsKey("alg")) { 1092 alg = new Algorithm(JSONObjectUtils.getString(jsonObject, "alg")); 1093 } 1094 1095 // Get optional key ID 1096 String kid = null; 1097 1098 if (jsonObject.containsKey("kid")) { 1099 kid = JSONObjectUtils.getString(jsonObject, "kid"); 1100 } 1101 1102 // Get optional X.509 cert URL 1103 URL x5u = null; 1104 1105 if (jsonObject.containsKey("x5u")) { 1106 x5u = JSONObjectUtils.getURL(jsonObject, "x5u"); 1107 } 1108 1109 // Get optional X.509 cert thumbprint 1110 Base64URL x5t = null; 1111 1112 if (jsonObject.containsKey("x5t")) { 1113 x5t = new Base64URL(JSONObjectUtils.getString(jsonObject, "x5t")); 1114 } 1115 1116 // Get optional X.509 cert chain 1117 List<Base64> x5c = null; 1118 1119 if (jsonObject.containsKey("x5c")) { 1120 x5c = X509CertChainUtils.parseX509CertChain(JSONObjectUtils.getJSONArray(jsonObject, "x5c")); 1121 } 1122 1123 try { 1124 if (d == null) { 1125 // Public key 1126 return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5c); 1127 1128 } else { 1129 // Key pair 1130 return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5c); 1131 } 1132 1133 } catch (IllegalArgumentException ex) { 1134 1135 // Conflicting 'use' and 'key_ops' 1136 throw new ParseException(ex.getMessage(), 0); 1137 } 1138 } 1139}