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