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