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