001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.jwk; 019 020 021import java.io.Serializable; 022import java.math.BigInteger; 023import java.net.URI; 024import java.security.*; 025import java.security.cert.Certificate; 026import java.security.cert.CertificateEncodingException; 027import java.security.cert.X509Certificate; 028import java.security.interfaces.ECPrivateKey; 029import java.security.interfaces.ECPublicKey; 030import java.security.spec.*; 031import java.text.ParseException; 032import java.util.Collections; 033import java.util.LinkedHashMap; 034import java.util.List; 035import java.util.Set; 036 037import com.nimbusds.jose.Algorithm; 038import com.nimbusds.jose.JOSEException; 039import com.nimbusds.jose.JWSAlgorithm; 040import com.nimbusds.jose.crypto.utils.ECChecks; 041import com.nimbusds.jose.util.Base64; 042import com.nimbusds.jose.util.Base64URL; 043import com.nimbusds.jose.util.BigIntegerUtils; 044import com.nimbusds.jose.util.JSONObjectUtils; 045import net.jcip.annotations.Immutable; 046import net.minidev.json.JSONObject; 047import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; 048 049 050/** 051 * Public and private {@link KeyType#EC Elliptic Curve} JSON Web Key (JWK). 052 * Uses the BouncyCastle.org provider for EC key import and export. This class 053 * is immutable. 054 * 055 * <p>Provides EC JWK import from / export to the following standard Java 056 * interfaces and classes: 057 * 058 * <ul> 059 * <li>{@link java.security.interfaces.ECPublicKey} 060 * <li>{@link java.security.interfaces.ECPrivateKey} 061 * <li>{@link java.security.PrivateKey} for an EC key in a PKCS#11 store 062 * <li>{@link java.security.KeyPair} 063 * </ul> 064 * 065 * <p>Example JSON object representation of a public EC JWK: 066 * 067 * <pre> 068 * { 069 * "kty" : "EC", 070 * "crv" : "P-256", 071 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 072 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 073 * "use" : "enc", 074 * "kid" : "1" 075 * } 076 * </pre> 077 * 078 * <p>Example JSON object representation of a public and private EC JWK: 079 * 080 * <pre> 081 * { 082 * "kty" : "EC", 083 * "crv" : "P-256", 084 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 085 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 086 * "d" : "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE", 087 * "use" : "enc", 088 * "kid" : "1" 089 * } 090 * </pre> 091 * 092 * <p>Use the builder to create a new EC JWK: 093 * 094 * <pre> 095 * ECKey key = new ECKey.Builder(ECKey.Curve.P_256, x, y) 096 * .keyUse(KeyUse.SIGNATURE) 097 * .keyID("123") 098 * .build(); 099 * </pre> 100 * 101 * <p>See http://en.wikipedia.org/wiki/Elliptic_curve_cryptography 102 * 103 * @author Vladimir Dzhuvinov 104 * @author Justin Richer 105 * @version 2017-04-08 106 */ 107@Immutable 108public final class ECKey extends JWK implements AssymetricJWK { 109 110 111 private static final long serialVersionUID = 1L; 112 113 114 /** 115 * Cryptographic curve. This class is immutable. 116 * 117 * <p>Includes constants for the following standard cryptographic 118 * curves: 119 * 120 * <ul> 121 * <li>{@link #P_256} 122 * <li>{@link #P_384} 123 * <li>{@link #P_521} 124 * </ul> 125 * 126 * <p>See "Digital Signature Standard (DSS)", FIPS PUB 186-3, June 127 * 2009, National Institute of Standards and Technology (NIST). 128 */ 129 @Immutable 130 public static class Curve implements Serializable { 131 132 133 private static final long serialVersionUID = 1L; 134 135 136 /** 137 * P-256 curve (secp256r1, also called prime256v1, 138 * OID = 1.2.840.10045.3.1.7). 139 */ 140 public static final Curve P_256 = new Curve("P-256", "secp256r1", "1.2.840.10045.3.1.7"); 141 142 143 /** 144 * P-384 curve (secp384r1, OID = 1.3.132.0.34). 145 */ 146 public static final Curve P_384 = new Curve("P-384", "secp384r1", "1.3.132.0.34"); 147 148 149 /** 150 * P-521 curve (secp521r1). 151 */ 152 public static final Curve P_521 = new Curve("P-521", "secp521r1", "1.3.132.0.35"); 153 154 155 /** 156 * The JOSE curve name. 157 */ 158 private final String name; 159 160 161 /** 162 * The standard curve name, {@code null} if not specified. 163 */ 164 private final String stdName; 165 166 167 /** 168 * The standard object identifier for the curve, {@code null} 169 * if not specified. 170 */ 171 private final String oid; 172 173 174 /** 175 * Creates a new cryptographic curve with the specified JOSE 176 * name. A standard curve name and object identifier (OID) are 177 * not unspecified. 178 * 179 * @param name The JOSE name of the cryptographic curve. Must not be 180 * {@code null}. 181 */ 182 public Curve(final String name) { 183 184 this(name, null, null); 185 } 186 187 188 /** 189 * Creates a new cryptographic curve with the specified JOSE 190 * name, standard name and object identifier (OID). 191 * 192 * @param name The JOSE name of the cryptographic curve. 193 * Must not be {@code null}. 194 * @param stdName The standard name of the cryptographic curve, 195 * {@code null} if not specified. 196 * @param oid The object identifier (OID) of the 197 * cryptographic curve, {@code null} if not 198 * specified. 199 */ 200 public Curve(final String name, final String stdName, final String oid) { 201 202 if (name == null) { 203 throw new IllegalArgumentException("The JOSE cryptographic curve name must not be null"); 204 } 205 206 this.name = name; 207 208 this.stdName = stdName; 209 210 this.oid = oid; 211 } 212 213 214 /** 215 * Returns the JOSE name of this cryptographic curve. 216 * 217 * @return The JOSE name. 218 */ 219 public String getName() { 220 221 return name; 222 } 223 224 225 /** 226 * Returns the standard name of this cryptographic curve. 227 * 228 * @return The standard name, {@code null} if not specified. 229 */ 230 public String getStdName() { 231 232 return stdName; 233 } 234 235 236 /** 237 * Returns the standard object identifier (OID) of this 238 * cryptographic curve. 239 * 240 * @return The OID, {@code null} if not specified. 241 */ 242 public String getOID() { 243 244 return oid; 245 } 246 247 248 /** 249 * Returns the parameter specification for this cryptographic 250 * curve. 251 * 252 * @return The EC parameter specification, {@code null} if it 253 * cannot be determined. 254 */ 255 public ECParameterSpec toECParameterSpec() { 256 257 return ECParameterTable.get(this); 258 } 259 260 261 /** 262 * @see #getName 263 */ 264 @Override 265 public String toString() { 266 267 return getName(); 268 } 269 270 271 @Override 272 public boolean equals(final Object object) { 273 274 return object instanceof Curve && 275 this.toString().equals(object.toString()); 276 } 277 278 279 /** 280 * Parses a cryptographic curve from the specified string. 281 * 282 * @param s The string to parse. Must not be {@code null} or 283 * empty. 284 * 285 * @return The cryptographic curve. 286 */ 287 public static Curve parse(final String s) { 288 289 if (s == null || s.trim().isEmpty()) { 290 throw new IllegalArgumentException("The cryptographic curve string must not be null or empty"); 291 } 292 293 if (s.equals(P_256.getName())) { 294 return P_256; 295 296 } else if (s.equals(P_384.getName())) { 297 return P_384; 298 299 } else if (s.equals(P_521.getName())) { 300 return P_521; 301 302 } else { 303 return new Curve(s); 304 } 305 } 306 307 308 /** 309 * Gets the cryptographic curve for the specified standard 310 * name. 311 * 312 * @param stdName The standard curve name. May be {@code null}. 313 * 314 * @return The curve, {@code null} if it cannot be determined. 315 */ 316 public static Curve forStdName(final String stdName) { 317 if( "secp256r1".equals(stdName) || "prime256v1".equals(stdName)) { 318 return P_256; 319 } else if( "secp384r1".equals(stdName) ) { 320 return P_384; 321 } else if( "secp521r1".equals(stdName) ) { 322 return P_521; 323 } else { 324 return null; 325 } 326 } 327 328 329 /** 330 * Gets the cryptographic curve for the specified object 331 * identifier (OID). 332 * 333 * @param oid The object OID. May be {@code null}. 334 * 335 * @return The curve, {@code null} if it cannot be determined. 336 */ 337 public static Curve forOID(final String oid) { 338 339 if (P_256.getOID().equals(oid)) { 340 return P_256; 341 } else if (P_384.getOID().equals(oid)) { 342 return P_384; 343 } else if (P_521.getOID().equals(oid)) { 344 return P_521; 345 } else { 346 return null; 347 } 348 } 349 350 351 /** 352 * Gets the cryptographic curve for the specified JWS 353 * algorithm. 354 * 355 * @param alg The JWS algorithm. May be {@code null}. 356 * 357 * @return The curve, {@code null} if the JWS algorithm is not 358 * curve based, or the JWS algorithm is not supported. 359 */ 360 public static Curve forJWSAlgoritm(final JWSAlgorithm alg) { 361 362 if (JWSAlgorithm.ES256.equals(alg)) { 363 return P_256; 364 } else if (JWSAlgorithm.ES384.equals(alg)) { 365 return P_384; 366 } else if (JWSAlgorithm.ES512.equals(alg)) { 367 return P_521; 368 } else { 369 return null; 370 } 371 } 372 373 374 /** 375 * Gets the cryptographic curve for the specified parameter 376 * specification. 377 * 378 * @param spec The EC parameter spec. May be {@code null}. 379 * 380 * @return The curve, {@code null} if it cannot be determined. 381 */ 382 public static Curve forECParameterSpec(final ECParameterSpec spec) { 383 384 return ECParameterTable.get(spec); 385 } 386 } 387 388 389 /** 390 * Builder for constructing Elliptic Curve JWKs. 391 * 392 * <p>Example usage: 393 * 394 * <pre> 395 * ECKey key = new ECKey.Builder(Curve.P521, x, y). 396 * d(d). 397 * algorithm(JWSAlgorithm.ES512). 398 * keyID("789"). 399 * build(); 400 * </pre> 401 */ 402 public static class Builder { 403 404 405 /** 406 * The curve name. 407 */ 408 private final Curve crv; 409 410 411 /** 412 * The public 'x' EC coordinate. 413 */ 414 private final Base64URL x; 415 416 417 /** 418 * The public 'y' EC coordinate. 419 */ 420 private final Base64URL y; 421 422 423 /** 424 * The private 'd' EC coordinate, optional. 425 */ 426 private Base64URL d; 427 428 429 /** 430 * The private EC key, as PKCS#11 handle, optional. 431 */ 432 private PrivateKey priv; 433 434 435 /** 436 * The key use, optional. 437 */ 438 private KeyUse use; 439 440 441 /** 442 * The key operations, optional. 443 */ 444 private Set<KeyOperation> ops; 445 446 447 /** 448 * The intended JOSE algorithm for the key, optional. 449 */ 450 private Algorithm alg; 451 452 453 /** 454 * The key ID, optional. 455 */ 456 private String kid; 457 458 459 /** 460 * X.509 certificate URL, optional. 461 */ 462 private URI x5u; 463 464 465 /** 466 * X.509 certificate SHA-1 thumbprint, optional. 467 */ 468 @Deprecated 469 private Base64URL x5t; 470 471 472 /** 473 * X.509 certificate SHA-256 thumbprint, optional. 474 */ 475 private Base64URL x5t256; 476 477 478 /** 479 * The X.509 certificate chain, optional. 480 */ 481 private List<Base64> x5c; 482 483 484 /** 485 * Reference to the underlying key store, {@code null} if none. 486 */ 487 private KeyStore ks; 488 489 490 /** 491 * Creates a new Elliptic Curve JWK builder. 492 * 493 * @param crv The cryptographic curve. Must not be 494 * {@code null}. 495 * @param x The public 'x' coordinate for the elliptic curve 496 * point. It is represented as the Base64URL 497 * encoding of the coordinate's big endian 498 * representation. Must not be {@code null}. 499 * @param y The public 'y' coordinate for the elliptic curve 500 * point. It is represented as the Base64URL 501 * encoding of the coordinate's big endian 502 * representation. Must not be {@code null}. 503 */ 504 public Builder(final Curve crv, final Base64URL x, final Base64URL y) { 505 506 if (crv == null) { 507 throw new IllegalArgumentException("The curve must not be null"); 508 } 509 510 this.crv = crv; 511 512 if (x == null) { 513 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 514 } 515 516 this.x = x; 517 518 if (y == null) { 519 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 520 } 521 522 this.y = y; 523 } 524 525 526 /** 527 * Creates a new Elliptic Curve JWK builder. 528 * 529 * @param crv The cryptographic curve. Must not be 530 * {@code null}. 531 * @param pub The public EC key to represent. Must not be 532 * {@code null}. 533 */ 534 public Builder(final Curve crv, final ECPublicKey pub) { 535 536 this(crv, 537 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 538 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY())); 539 } 540 541 542 /** 543 * Creates a new Elliptic Curve JWK builder. 544 * 545 * @param ecJWK The EC JWK to start with. Must not be 546 * {@code null}. 547 */ 548 public Builder(final ECKey ecJWK) { 549 550 crv = ecJWK.crv; 551 x = ecJWK.x; 552 y = ecJWK.y; 553 d = ecJWK.d; 554 priv = ecJWK.privateKey; 555 use = ecJWK.getKeyUse(); 556 ops = ecJWK.getKeyOperations(); 557 alg = ecJWK.getAlgorithm(); 558 kid = ecJWK.getKeyID(); 559 x5u = ecJWK.getX509CertURL(); 560 x5t = ecJWK.getX509CertThumbprint(); 561 x5t256 = ecJWK.getX509CertSHA256Thumbprint(); 562 x5c = ecJWK.getX509CertChain(); 563 ks = ecJWK.getKeyStore(); 564 } 565 566 567 /** 568 * Sets the private 'd' coordinate for the elliptic curve 569 * point. The alternative method is {@link #privateKey}. 570 * 571 * @param d The 'd' coordinate. It is represented as the 572 * Base64URL encoding of the coordinate's big endian 573 * representation. {@code null} if not specified (for 574 * a public key). 575 * 576 * @return This builder. 577 */ 578 public Builder d(final Base64URL d) { 579 580 this.d = d; 581 return this; 582 } 583 584 585 /** 586 * Sets the private Elliptic Curve key. The alternative method 587 * is {@link #d}. 588 * 589 * @param priv The private EC key, used to obtain the private 590 * 'd' coordinate for the elliptic curve point. 591 * {@code null} if not specified (for a public 592 * key). 593 * 594 * @return This builder. 595 */ 596 public Builder privateKey(final ECPrivateKey priv) { 597 598 if (priv != null) { 599 this.d = encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()); 600 } 601 602 return this; 603 } 604 605 606 /** 607 * Sets the private EC key, typically for a key located in a 608 * PKCS#11 store that doesn't expose the private key parameters 609 * (such as a smart card or HSM). 610 * 611 * @param priv The private EC key reference. Its algorithm must 612 * be "EC". Must not be {@code null}. 613 * 614 * @return This builder. 615 */ 616 public Builder privateKey(final PrivateKey priv) { 617 618 if (! "EC".equalsIgnoreCase(priv.getAlgorithm())) { 619 throw new IllegalArgumentException("The private key algorithm must be EC"); 620 } 621 622 this.priv = priv; 623 return this; 624 } 625 626 627 /** 628 * Sets the use ({@code use}) of the JWK. 629 * 630 * @param use The key use, {@code null} if not specified or if 631 * the key is intended for signing as well as 632 * encryption. 633 * 634 * @return This builder. 635 */ 636 public Builder keyUse(final KeyUse use) { 637 638 this.use = use; 639 return this; 640 } 641 642 643 /** 644 * Sets the operations ({@code key_ops}) of the JWK. 645 * 646 * @param ops The key operations, {@code null} if not 647 * specified. 648 * 649 * @return This builder. 650 */ 651 public Builder keyOperations(final Set<KeyOperation> ops) { 652 653 this.ops = ops; 654 return this; 655 } 656 657 658 /** 659 * Sets the intended JOSE algorithm ({@code alg}) for the JWK. 660 * 661 * @param alg The intended JOSE algorithm, {@code null} if not 662 * specified. 663 * 664 * @return This builder. 665 */ 666 public Builder algorithm(final Algorithm alg) { 667 668 this.alg = alg; 669 return this; 670 } 671 672 /** 673 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 674 * to match a specific key. This can be used, for instance, to 675 * choose a key within a {@link JWKSet} during key rollover. 676 * The key ID may also correspond to a JWS/JWE {@code kid} 677 * header parameter value. 678 * 679 * @param kid The key ID, {@code null} if not specified. 680 * 681 * @return This builder. 682 */ 683 public Builder keyID(final String kid) { 684 685 this.kid = kid; 686 return this; 687 } 688 689 690 /** 691 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK 692 * thumbprint (RFC 7638). The key ID can be used to match a 693 * specific key. This can be used, for instance, to choose a 694 * key within a {@link JWKSet} during key rollover. The key ID 695 * may also correspond to a JWS/JWE {@code kid} header 696 * parameter value. 697 * 698 * @return This builder. 699 * 700 * @throws JOSEException If the SHA-256 hash algorithm is not 701 * supported. 702 */ 703 public Builder keyIDFromThumbprint() 704 throws JOSEException { 705 706 return keyIDFromThumbprint("SHA-256"); 707 } 708 709 710 /** 711 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint 712 * (RFC 7638). The key ID can be used to match a specific key. 713 * This can be used, for instance, to choose a key within a 714 * {@link JWKSet} during key rollover. The key ID may also 715 * correspond to a JWS/JWE {@code kid} header parameter value. 716 * 717 * @param hashAlg The hash algorithm for the JWK thumbprint 718 * computation. Must not be {@code null}. 719 * 720 * @return This builder. 721 * 722 * @throws JOSEException If the hash algorithm is not 723 * supported. 724 */ 725 public Builder keyIDFromThumbprint(final String hashAlg) 726 throws JOSEException { 727 728 // Put mandatory params in sorted order 729 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 730 requiredParams.put("crv", crv.toString()); 731 requiredParams.put("kty", KeyType.EC.getValue()); 732 requiredParams.put("x", x.toString()); 733 requiredParams.put("y", y.toString()); 734 this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString(); 735 return this; 736 } 737 738 739 /** 740 * Sets the X.509 certificate URL ({@code x5u}) of the JWK. 741 * 742 * @param x5u The X.509 certificate URL, {@code null} if not 743 * specified. 744 * 745 * @return This builder. 746 */ 747 public Builder x509CertURL(final URI x5u) { 748 749 this.x5u = x5u; 750 return this; 751 } 752 753 754 /** 755 * Sets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of 756 * the JWK. 757 * 758 * @param x5t The X.509 certificate SHA-1 thumbprint, 759 * {@code null} if not specified. 760 * 761 * @return This builder. 762 */ 763 @Deprecated 764 public Builder x509CertThumbprint(final Base64URL x5t) { 765 766 this.x5t = x5t; 767 return this; 768 } 769 770 771 /** 772 * Sets the X.509 certificate SHA-256 thumbprint 773 * ({@code x5t#S256}) of the JWK. 774 * 775 * @param x5t256 The X.509 certificate SHA-256 thumbprint, 776 * {@code null} if not specified. 777 * 778 * @return This builder. 779 */ 780 public Builder x509CertSHA256Thumbprint(final Base64URL x5t256) { 781 782 this.x5t256 = x5t256; 783 return this; 784 } 785 786 787 /** 788 * Sets the X.509 certificate chain ({@code x5c}) of the JWK. 789 * 790 * @param x5c The X.509 certificate chain as a unmodifiable 791 * list, {@code null} if not specified. 792 * 793 * @return This builder. 794 */ 795 public Builder x509CertChain(final List<Base64> x5c) { 796 797 this.x5c = x5c; 798 return this; 799 } 800 801 802 /** 803 * Sets the underlying key store. 804 * 805 * @param keyStore Reference to the underlying key store, 806 * {@code null} if none. 807 * 808 * @return This builder. 809 */ 810 public Builder keyStore(final KeyStore keyStore) { 811 812 this.ks = keyStore; 813 return this; 814 } 815 816 817 /** 818 * Builds a new octet sequence JWK. 819 * 820 * @return The octet sequence JWK. 821 * 822 * @throws IllegalStateException If the JWK parameters were 823 * inconsistently specified. 824 */ 825 public ECKey build() { 826 827 try { 828 if (d == null && priv == null) { 829 // Public key 830 return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 831 } 832 833 if (priv != null) { 834 // PKCS#11 reference to private key 835 return new ECKey(crv, x, y, priv, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 836 } 837 838 // Public / private key pair with 'd' 839 return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 840 841 } catch (IllegalArgumentException e) { 842 throw new IllegalStateException(e.getMessage(), e); 843 } 844 } 845 } 846 847 848 /** 849 * Returns the Base64URL encoding of the specified elliptic curve 'x', 850 * 'y' or 'd' coordinate, with leading zero padding up to the specified 851 * field size in bits. 852 * 853 * @param fieldSize The field size in bits. 854 * @param coordinate The elliptic curve coordinate. Must not be 855 * {@code null}. 856 * 857 * @return The Base64URL-encoded coordinate, with leading zero padding 858 * up to the curve's field size. 859 */ 860 public static Base64URL encodeCoordinate(final int fieldSize, final BigInteger coordinate) { 861 862 final byte[] notPadded = BigIntegerUtils.toBytesUnsigned(coordinate); 863 864 int bytesToOutput = (fieldSize + 7)/8; 865 866 if (notPadded.length >= bytesToOutput) { 867 // Greater-than check to prevent exception on malformed 868 // key below 869 return Base64URL.encode(notPadded); 870 } 871 872 final byte[] padded = new byte[bytesToOutput]; 873 874 System.arraycopy(notPadded, 0, padded, bytesToOutput - notPadded.length, notPadded.length); 875 876 return Base64URL.encode(padded); 877 } 878 879 880 /** 881 * The curve name. 882 */ 883 private final Curve crv; 884 885 886 /** 887 * The public 'x' EC coordinate. 888 */ 889 private final Base64URL x; 890 891 892 /** 893 * The public 'y' EC coordinate. 894 */ 895 private final Base64URL y; 896 897 898 /** 899 * The private 'd' EC coordinate. 900 */ 901 private final Base64URL d; 902 903 904 /** 905 * Private PKCS#11 key handle. 906 */ 907 private final PrivateKey privateKey; 908 909 910 /** 911 * Ensures the specified 'x' and 'y' public coordinates are on the 912 * given curve. 913 * 914 * @param crv The curve. Must not be {@code null}. 915 * @param x The public 'x' coordinate. Must not be {@code null}. 916 * @param y The public 'y' coordinate. Must not be {@code null}. 917 */ 918 private static void ensurePublicCoordinatesOnCurve(final Curve crv, final Base64URL x, final Base64URL y) { 919 920 ECParameterSpec ecSpec = crv.toECParameterSpec(); 921 922 if (ecSpec == null) { 923 throw new IllegalArgumentException("Unknown / unsupported curve: " + crv); 924 } 925 926 if (! ECChecks.isPointOnCurve(x.decodeToBigInteger(), y.decodeToBigInteger(), crv.toECParameterSpec())) { 927 throw new IllegalArgumentException("Invalid EC JWK: The 'x' and 'y' public coordinates are not on the " + crv + " curve"); 928 } 929 } 930 931 932 /** 933 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 934 * specified parameters. 935 * 936 * @param crv The cryptographic curve. Must not be {@code null}. 937 * @param x The public 'x' coordinate for the elliptic curve 938 * point. It is represented as the Base64URL encoding of 939 * the coordinate's big endian representation. Must not 940 * be {@code null}. 941 * @param y The public 'y' coordinate for the elliptic curve 942 * point. It is represented as the Base64URL encoding of 943 * the coordinate's big endian representation. Must not 944 * be {@code null}. 945 * @param use The key use, {@code null} if not specified or if the 946 * key is intended for signing as well as encryption. 947 * @param ops The key operations, {@code null} if not specified. 948 * @param alg The intended JOSE algorithm for the key, {@code null} 949 * if not specified. 950 * @param kid The key ID, {@code null} if not specified. 951 * @param x5u The X.509 certificate URL, {@code null} if not 952 * specified. 953 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 954 * if not specified. 955 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 956 * if not specified. 957 * @param x5c The X.509 certificate chain, {@code null} if not 958 * specified. 959 * @param ks Reference to the underlying key store, {@code null} if 960 * not specified. 961 */ 962 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 963 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 964 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 965 final KeyStore ks) { 966 967 super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 968 969 if (crv == null) { 970 throw new IllegalArgumentException("The curve must not be null"); 971 } 972 973 this.crv = crv; 974 975 if (x == null) { 976 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 977 } 978 979 this.x = x; 980 981 if (y == null) { 982 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 983 } 984 985 this.y = y; 986 987 ensurePublicCoordinatesOnCurve(crv, x, y); 988 989 this.d = null; 990 991 this.privateKey = null; 992 } 993 994 995 /** 996 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 997 * with the specified parameters. 998 * 999 * @param crv The cryptographic curve. Must not be {@code null}. 1000 * @param x The public 'x' coordinate for the elliptic curve 1001 * point. It is represented as the Base64URL encoding of 1002 * the coordinate's big endian representation. Must not 1003 * be {@code null}. 1004 * @param y The public 'y' coordinate for the elliptic curve 1005 * point. It is represented as the Base64URL encoding of 1006 * the coordinate's big endian representation. Must not 1007 * be {@code null}. 1008 * @param d The private 'd' coordinate for the elliptic curve 1009 * point. It is represented as the Base64URL encoding of 1010 * the coordinate's big endian representation. Must not 1011 * be {@code null}. 1012 * @param use The key use, {@code null} if not specified or if the 1013 * key is intended for signing as well as encryption. 1014 * @param ops The key operations, {@code null} if not specified. 1015 * @param alg The intended JOSE algorithm for the key, {@code null} 1016 * if not specified. 1017 * @param kid The key ID, {@code null} if not specified. 1018 * @param x5u The X.509 certificate URL, {@code null} if not 1019 * specified. 1020 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 1021 * if not specified. 1022 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 1023 * if not specified. 1024 * @param x5c The X.509 certificate chain, {@code null} if not 1025 * specified. 1026 * @param ks Reference to the underlying key store, {@code null} if 1027 * not specified. 1028 */ 1029 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final Base64URL d, 1030 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 1031 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 1032 final KeyStore ks) { 1033 1034 super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 1035 1036 if (crv == null) { 1037 throw new IllegalArgumentException("The curve must not be null"); 1038 } 1039 1040 this.crv = crv; 1041 1042 if (x == null) { 1043 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 1044 } 1045 1046 this.x = x; 1047 1048 if (y == null) { 1049 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 1050 } 1051 1052 this.y = y; 1053 1054 ensurePublicCoordinatesOnCurve(crv, x, y); 1055 1056 if (d == null) { 1057 throw new IllegalArgumentException("The 'd' coordinate must not be null"); 1058 } 1059 1060 this.d = d; 1061 1062 this.privateKey = null; 1063 } 1064 1065 1066 /** 1067 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 1068 * with the specified parameters. The private key is specified by its 1069 * PKCS#11 handle. 1070 * 1071 * @param crv The cryptographic curve. Must not be {@code null}. 1072 * @param x The public 'x' coordinate for the elliptic curve 1073 * point. It is represented as the Base64URL encoding of 1074 * the coordinate's big endian representation. Must not 1075 * be {@code null}. 1076 * @param y The public 'y' coordinate for the elliptic curve 1077 * point. It is represented as the Base64URL encoding of 1078 * the coordinate's big endian representation. Must not 1079 * be {@code null}. 1080 * @param priv The private key as a PKCS#11 handle, {@code null} if 1081 * not specified. 1082 * @param use The key use, {@code null} if not specified or if the 1083 * key is intended for signing as well as encryption. 1084 * @param ops The key operations, {@code null} if not specified. 1085 * @param alg The intended JOSE algorithm for the key, {@code null} 1086 * if not specified. 1087 * @param kid The key ID, {@code null} if not specified. 1088 * @param x5u The X.509 certificate URL, {@code null} if not 1089 * specified. 1090 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 1091 * if not specified. 1092 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 1093 * if not specified. 1094 * @param x5c The X.509 certificate chain, {@code null} if not 1095 * specified. 1096 */ 1097 public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final PrivateKey priv, 1098 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 1099 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 1100 final KeyStore ks) { 1101 1102 super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 1103 1104 if (crv == null) { 1105 throw new IllegalArgumentException("The curve must not be null"); 1106 } 1107 1108 this.crv = crv; 1109 1110 if (x == null) { 1111 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 1112 } 1113 1114 this.x = x; 1115 1116 if (y == null) { 1117 throw new IllegalArgumentException("The 'y' coordinate must not be null"); 1118 } 1119 1120 this.y = y; 1121 1122 ensurePublicCoordinatesOnCurve(crv, x, y); 1123 1124 d = null; 1125 1126 this.privateKey = priv; 1127 } 1128 1129 1130 /** 1131 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 1132 * specified parameters. 1133 * 1134 * @param crv The cryptographic curve. Must not be {@code null}. 1135 * @param pub The public EC key to represent. Must not be 1136 * {@code null}. 1137 * @param use The key use, {@code null} if not specified or if the 1138 * key is intended for signing as well as encryption. 1139 * @param ops The key operations, {@code null} if not specified. 1140 * @param alg The intended JOSE algorithm for the key, {@code null} 1141 * if not specified. 1142 * @param kid The key ID, {@code null} if not specified. 1143 * @param x5u The X.509 certificate URL, {@code null} if not 1144 * specified. 1145 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 1146 * if not specified. 1147 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 1148 * if not specified. 1149 * @param x5c The X.509 certificate chain, {@code null} if not 1150 * specified. 1151 * @param ks Reference to the underlying key store, {@code null} if 1152 * not specified. 1153 */ 1154 public ECKey(final Curve crv, final ECPublicKey pub, 1155 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 1156 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 1157 final KeyStore ks) { 1158 1159 this(crv, 1160 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 1161 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()), 1162 use, ops, alg, kid, 1163 x5u, x5t, x5t256, x5c, 1164 ks); 1165 } 1166 1167 1168 /** 1169 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 1170 * with the specified parameters. 1171 * 1172 * @param crv The cryptographic curve. Must not be {@code null}. 1173 * @param pub The public EC key to represent. Must not be 1174 * {@code null}. 1175 * @param priv The private EC key to represent. Must not be 1176 * {@code null}. 1177 * @param use The key use, {@code null} if not specified or if the 1178 * key is intended for signing as well as encryption. 1179 * @param ops The key operations, {@code null} if not specified. 1180 * @param alg The intended JOSE algorithm for the key, {@code null} 1181 * if not specified. 1182 * @param kid The key ID, {@code null} if not specified. 1183 * @param x5u The X.509 certificate URL, {@code null} if not 1184 * specified. 1185 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 1186 * if not specified. 1187 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 1188 * if not specified. 1189 * @param x5c The X.509 certificate chain, {@code null} if not 1190 * specified. 1191 * @param ks Reference to the underlying key store, {@code null} if 1192 * not specified. 1193 */ 1194 public ECKey(final Curve crv, final ECPublicKey pub, final ECPrivateKey priv, 1195 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 1196 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 1197 final KeyStore ks) { 1198 1199 this(crv, 1200 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 1201 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()), 1202 encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()), 1203 use, ops, alg, kid, 1204 x5u, x5t, x5t256, x5c, 1205 ks); 1206 } 1207 1208 1209 /** 1210 * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 1211 * with the specified parameters. The private key is specified by its 1212 * PKCS#11 handle. 1213 * 1214 * @param crv The cryptographic curve. Must not be {@code null}. 1215 * @param pub The public EC key to represent. Must not be 1216 * {@code null}. 1217 * @param priv The private key as a PKCS#11 handle, {@code null} if 1218 * not specified. 1219 * @param use The key use, {@code null} if not specified or if the 1220 * key is intended for signing as well as encryption. 1221 * @param ops The key operations, {@code null} if not specified. 1222 * @param alg The intended JOSE algorithm for the key, {@code null} 1223 * if not specified. 1224 * @param kid The key ID, {@code null} if not specified. 1225 * @param x5u The X.509 certificate URL, {@code null} if not 1226 * specified. 1227 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 1228 * if not specified. 1229 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 1230 * if not specified. 1231 * @param x5c The X.509 certificate chain, {@code null} if not 1232 * specified. 1233 * @param ks Reference to the underlying key store, {@code null} if 1234 * not specified. 1235 */ 1236 public ECKey(final Curve crv, final ECPublicKey pub, final PrivateKey priv, 1237 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 1238 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 1239 final KeyStore ks) { 1240 1241 this( 1242 crv, 1243 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()), 1244 encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()), 1245 priv, 1246 use, ops, alg, kid, x5u, x5t, x5t256, x5c, 1247 ks); 1248 } 1249 1250 1251 /** 1252 * Gets the cryptographic curve. 1253 * 1254 * @return The cryptographic curve. 1255 */ 1256 public Curve getCurve() { 1257 1258 return crv; 1259 } 1260 1261 1262 /** 1263 * Gets the public 'x' coordinate for the elliptic curve point. 1264 * 1265 * @return The 'x' coordinate. It is represented as the Base64URL 1266 * encoding of the coordinate's big endian representation. 1267 */ 1268 public Base64URL getX() { 1269 1270 return x; 1271 } 1272 1273 1274 /** 1275 * Gets the public 'y' coordinate for the elliptic curve point. 1276 * 1277 * @return The 'y' coordinate. It is represented as the Base64URL 1278 * encoding of the coordinate's big endian representation. 1279 */ 1280 public Base64URL getY() { 1281 1282 return y; 1283 } 1284 1285 1286 /** 1287 * Gets the private 'd' coordinate for the elliptic curve point. It is 1288 * represented as the Base64URL encoding of the coordinate's big endian 1289 * representation. 1290 * 1291 * @return The 'd' coordinate. It is represented as the Base64URL 1292 * encoding of the coordinate's big endian representation. 1293 * {@code null} if not specified (for a public key). 1294 */ 1295 public Base64URL getD() { 1296 1297 return d; 1298 } 1299 1300 1301 /** 1302 * Returns a standard {@code java.security.interfaces.ECPublicKey} 1303 * representation of this Elliptic Curve JWK. Uses the default JCA 1304 * provider. 1305 * 1306 * @return The public Elliptic Curve key. 1307 * 1308 * @throws JOSEException If EC is not supported by the underlying Java 1309 * Cryptography (JCA) provider or if the JWK 1310 * parameters are invalid for a public EC key. 1311 */ 1312 public ECPublicKey toECPublicKey() 1313 throws JOSEException { 1314 1315 return toECPublicKey(null); 1316 } 1317 1318 1319 /** 1320 * Returns a standard {@code java.security.interfaces.ECPublicKey} 1321 * representation of this Elliptic Curve JWK. 1322 * 1323 * @param provider The specific JCA provider to use, {@code null} 1324 * implies the default one. 1325 * 1326 * @return The public Elliptic Curve key. 1327 * 1328 * @throws JOSEException If EC is not supported by the underlying Java 1329 * Cryptography (JCA) provider or if the JWK 1330 * parameters are invalid for a public EC key. 1331 */ 1332 public ECPublicKey toECPublicKey(final Provider provider) 1333 throws JOSEException { 1334 1335 ECParameterSpec spec = crv.toECParameterSpec(); 1336 1337 if (spec == null) { 1338 throw new JOSEException("Couldn't get EC parameter spec for curve " + crv); 1339 } 1340 1341 ECPoint w = new ECPoint(x.decodeToBigInteger(), y.decodeToBigInteger()); 1342 1343 ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, spec); 1344 1345 try { 1346 KeyFactory keyFactory; 1347 1348 if (provider == null) { 1349 keyFactory = KeyFactory.getInstance("EC"); 1350 } else { 1351 keyFactory = KeyFactory.getInstance("EC", provider); 1352 } 1353 1354 return (ECPublicKey) keyFactory.generatePublic(publicKeySpec); 1355 1356 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 1357 1358 throw new JOSEException(e.getMessage(), e); 1359 } 1360 } 1361 1362 1363 /** 1364 * Returns a standard {@code java.security.interfaces.ECPrivateKey} 1365 * representation of this Elliptic Curve JWK. Uses the default JCA 1366 * provider. 1367 * 1368 * @return The private Elliptic Curve key, {@code null} if not 1369 * specified by this JWK. 1370 * 1371 * @throws JOSEException If EC is not supported by the underlying Java 1372 * Cryptography (JCA) provider or if the JWK 1373 * parameters are invalid for a private EC key. 1374 */ 1375 public ECPrivateKey toECPrivateKey() 1376 throws JOSEException { 1377 1378 return toECPrivateKey(null); 1379 } 1380 1381 1382 /** 1383 * Returns a standard {@code java.security.interfaces.ECPrivateKey} 1384 * representation of this Elliptic Curve JWK. 1385 * 1386 * @param provider The specific JCA provider to use, {@code null} 1387 * implies the default one. 1388 * 1389 * @return The private Elliptic Curve key, {@code null} if not 1390 * specified by this JWK. 1391 * 1392 * @throws JOSEException If EC is not supported by the underlying Java 1393 * Cryptography (JCA) provider or if the JWK 1394 * parameters are invalid for a private EC key. 1395 */ 1396 public ECPrivateKey toECPrivateKey(final Provider provider) 1397 throws JOSEException { 1398 1399 if (d == null) { 1400 // No private 'd' param 1401 return null; 1402 } 1403 1404 ECParameterSpec spec = crv.toECParameterSpec(); 1405 1406 if (spec == null) { 1407 throw new JOSEException("Couldn't get EC parameter spec for curve " + crv); 1408 } 1409 1410 ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d.decodeToBigInteger(), spec); 1411 1412 try { 1413 KeyFactory keyFactory; 1414 1415 if (provider == null) { 1416 keyFactory = KeyFactory.getInstance("EC"); 1417 } else { 1418 keyFactory = KeyFactory.getInstance("EC", provider); 1419 } 1420 1421 return (ECPrivateKey) keyFactory.generatePrivate(privateKeySpec); 1422 1423 } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 1424 1425 throw new JOSEException(e.getMessage(), e); 1426 } 1427 } 1428 1429 1430 @Override 1431 public PublicKey toPublicKey() 1432 throws JOSEException { 1433 1434 return toECPublicKey(); 1435 } 1436 1437 1438 @Override 1439 public PrivateKey toPrivateKey() 1440 throws JOSEException { 1441 1442 PrivateKey prv = toECPrivateKey(); 1443 1444 if (prv != null) { 1445 // Return private EC key with key material 1446 return prv; 1447 } 1448 1449 // Return private EC key as PKCS#11 handle, or null 1450 return privateKey; 1451 } 1452 1453 1454 /** 1455 * Returns a standard {@code java.security.KeyPair} representation of 1456 * this Elliptic Curve JWK. Uses the default JCA provider. 1457 * 1458 * @return The Elliptic Curve key pair. The private Elliptic Curve key 1459 * will be {@code null} if not specified. 1460 * 1461 * @throws JOSEException If EC is not supported by the underlying Java 1462 * Cryptography (JCA) provider or if the JWK 1463 * parameters are invalid for a public and / or 1464 * private EC key. 1465 */ 1466 @Override 1467 public KeyPair toKeyPair() 1468 throws JOSEException { 1469 1470 return toKeyPair(null); 1471 } 1472 1473 1474 /** 1475 * Returns a standard {@code java.security.KeyPair} representation of 1476 * this Elliptic Curve JWK. 1477 * 1478 * @param provider The specific JCA provider to use, {@code null} 1479 * implies the default one. 1480 * 1481 * @return The Elliptic Curve key pair. The private Elliptic Curve key 1482 * will be {@code null} if not specified. 1483 * 1484 * @throws JOSEException If EC is not supported by the underlying Java 1485 * Cryptography (JCA) provider or if the JWK 1486 * parameters are invalid for a public and / or 1487 * private EC key. 1488 */ 1489 public KeyPair toKeyPair(final Provider provider) 1490 throws JOSEException { 1491 1492 if (privateKey != null) { 1493 // Private key as PKCS#11 handle 1494 return new KeyPair(toECPublicKey(provider), privateKey); 1495 } else { 1496 return new KeyPair(toECPublicKey(provider), toECPrivateKey(provider)); 1497 } 1498 } 1499 1500 1501 @Override 1502 public LinkedHashMap<String,?> getRequiredParams() { 1503 1504 // Put mandatory params in sorted order 1505 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 1506 requiredParams.put("crv", crv.toString()); 1507 requiredParams.put("kty", getKeyType().getValue()); 1508 requiredParams.put("x", x.toString()); 1509 requiredParams.put("y", y.toString()); 1510 return requiredParams; 1511 } 1512 1513 1514 @Override 1515 public boolean isPrivate() { 1516 1517 return d != null || privateKey != null; 1518 } 1519 1520 1521 @Override 1522 public int size() { 1523 1524 ECParameterSpec ecParameterSpec = crv.toECParameterSpec(); 1525 1526 if (ecParameterSpec == null) { 1527 throw new UnsupportedOperationException("Couldn't determine field size for curve " + crv.getName()); 1528 } 1529 1530 return ecParameterSpec.getCurve().getField().getFieldSize(); 1531 } 1532 1533 1534 /** 1535 * Returns a copy of this Elliptic Curve JWK with any private values 1536 * removed. 1537 * 1538 * @return The copied public Elliptic Curve JWK. 1539 */ 1540 @Override 1541 public ECKey toPublicJWK() { 1542 1543 return new ECKey( 1544 getCurve(), getX(), getY(), 1545 getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(), 1546 getX509CertURL(), getX509CertThumbprint(), getX509CertSHA256Thumbprint(), getX509CertChain(), 1547 getKeyStore()); 1548 } 1549 1550 1551 @Override 1552 public JSONObject toJSONObject() { 1553 1554 JSONObject o = super.toJSONObject(); 1555 1556 // Append EC specific attributes 1557 o.put("crv", crv.toString()); 1558 o.put("x", x.toString()); 1559 o.put("y", y.toString()); 1560 1561 if (d != null) { 1562 o.put("d", d.toString()); 1563 } 1564 1565 return o; 1566 } 1567 1568 1569 /** 1570 * Parses a public / private Elliptic Curve JWK from the specified JSON 1571 * object string representation. 1572 * 1573 * @param s The JSON object string to parse. Must not be {@code null}. 1574 * 1575 * @return The public / private Elliptic Curve JWK. 1576 * 1577 * @throws ParseException If the string couldn't be parsed to an 1578 * Elliptic Curve JWK. 1579 */ 1580 public static ECKey parse(final String s) 1581 throws ParseException { 1582 1583 return parse(JSONObjectUtils.parse(s)); 1584 } 1585 1586 1587 /** 1588 * Parses a public / private Elliptic Curve JWK from the specified JSON 1589 * object representation. 1590 * 1591 * @param jsonObject The JSON object to parse. Must not be 1592 * {@code null}. 1593 * 1594 * @return The public / private Elliptic Curve JWK. 1595 * 1596 * @throws ParseException If the JSON object couldn't be parsed to an 1597 * Elliptic Curve JWK. 1598 */ 1599 public static ECKey parse(final JSONObject jsonObject) 1600 throws ParseException { 1601 1602 // Parse the mandatory parameters first 1603 Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv")); 1604 Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x")); 1605 Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y")); 1606 1607 // Check key type 1608 KeyType kty = JWKMetadata.parseKeyType(jsonObject); 1609 1610 if (kty != KeyType.EC) { 1611 throw new ParseException("The key type \"kty\" must be EC", 0); 1612 } 1613 1614 // Get optional private key 1615 Base64URL d = null; 1616 if (jsonObject.get("d") != null) { 1617 d = new Base64URL(JSONObjectUtils.getString(jsonObject, "d")); 1618 } 1619 1620 1621 try { 1622 if (d == null) { 1623 // Public key 1624 return new ECKey(crv, x, y, 1625 JWKMetadata.parseKeyUse(jsonObject), 1626 JWKMetadata.parseKeyOperations(jsonObject), 1627 JWKMetadata.parseAlgorithm(jsonObject), 1628 JWKMetadata.parseKeyID(jsonObject), 1629 JWKMetadata.parseX509CertURL(jsonObject), 1630 JWKMetadata.parseX509CertThumbprint(jsonObject), 1631 JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject), 1632 JWKMetadata.parseX509CertChain(jsonObject), 1633 null); 1634 1635 } else { 1636 // Key pair 1637 return new ECKey(crv, x, y, d, 1638 JWKMetadata.parseKeyUse(jsonObject), 1639 JWKMetadata.parseKeyOperations(jsonObject), 1640 JWKMetadata.parseAlgorithm(jsonObject), 1641 JWKMetadata.parseKeyID(jsonObject), 1642 JWKMetadata.parseX509CertURL(jsonObject), 1643 JWKMetadata.parseX509CertThumbprint(jsonObject), 1644 JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject), 1645 JWKMetadata.parseX509CertChain(jsonObject), 1646 null); 1647 } 1648 1649 } catch (IllegalArgumentException ex) { 1650 1651 // Conflicting 'use' and 'key_ops' 1652 throw new ParseException(ex.getMessage(), 0); 1653 } 1654 } 1655 1656 1657 /** 1658 * Parses a public Elliptic Curve JWK from the specified X.509 1659 * certificate. Requires BouncyCastle. 1660 * 1661 * <p><strong>Important:</strong> The X.509 certificate is not 1662 * validated! 1663 * 1664 * <p>Sets the following JWK parameters: 1665 * 1666 * <ul> 1667 * <li>The curve is obtained from the subject public key info 1668 * algorithm parameters. 1669 * <li>The JWK use inferred by {@link KeyUse#from}. 1670 * <li>The JWK ID from the X.509 serial number (in base 10). 1671 * <li>The JWK X.509 certificate chain (this certificate only). 1672 * <li>The JWK X.509 certificate SHA-256 thumbprint. 1673 * </ul> 1674 * 1675 * @param cert The X.509 certificate. Must not be {@code null}. 1676 * 1677 * @return The public Elliptic Curve JWK. 1678 * 1679 * @throws JOSEException If parsing failed. 1680 */ 1681 public static ECKey parse(final X509Certificate cert) 1682 throws JOSEException { 1683 1684 if (! (cert.getPublicKey() instanceof ECPublicKey)) { 1685 throw new JOSEException("The public key of the X.509 certificate is not EC"); 1686 } 1687 1688 ECPublicKey publicKey = (ECPublicKey) cert.getPublicKey(); 1689 1690 try { 1691 JcaX509CertificateHolder certHolder = new JcaX509CertificateHolder(cert); 1692 1693 String oid = certHolder.getSubjectPublicKeyInfo().getAlgorithm().getParameters().toString(); 1694 1695 Curve crv = Curve.forOID(oid); 1696 1697 if (crv == null) { 1698 throw new JOSEException("Couldn't determine EC JWK curve for OID " + oid); 1699 } 1700 1701 MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 1702 1703 return new ECKey.Builder(crv, publicKey) 1704 .keyUse(KeyUse.from(cert)) 1705 .keyID(cert.getSerialNumber().toString(10)) 1706 .x509CertChain(Collections.singletonList(Base64.encode(cert.getEncoded()))) 1707 .x509CertSHA256Thumbprint(Base64URL.encode(sha256.digest(cert.getEncoded()))) 1708 .build(); 1709 } catch (NoSuchAlgorithmException e) { 1710 throw new JOSEException("Couldn't encode x5t parameter: " + e.getMessage(), e); 1711 } catch (CertificateEncodingException e) { 1712 throw new JOSEException("Couldn't encode x5c parameter: " + e.getMessage(), e); 1713 } 1714 } 1715 1716 1717 /** 1718 * Loads a public / private Elliptic Curve JWK from the specified JCA 1719 * key store. Requires BouncyCastle. 1720 * 1721 * <p><strong>Important:</strong> The X.509 certificate is not 1722 * validated! 1723 * 1724 * @param keyStore The key store. Must not be {@code null}. 1725 * @param alias The alias. Must not be {@code null}. 1726 * @param pin The pin to unlock the private key if any, empty or 1727 * {@code null} if not required. 1728 * 1729 * @return The public / private Elliptic Curve JWK., {@code null} if no 1730 * key with the specified alias was found. 1731 * 1732 * @throws KeyStoreException On a key store exception. 1733 * @throws JOSEException If EC key loading failed. 1734 */ 1735 public static ECKey load(final KeyStore keyStore, 1736 final String alias, 1737 final char[] pin) 1738 throws KeyStoreException, JOSEException { 1739 1740 Certificate cert = keyStore.getCertificate(alias); 1741 1742 if (cert == null || ! (cert instanceof X509Certificate)) { 1743 return null; 1744 } 1745 1746 X509Certificate x509Cert = (X509Certificate)cert; 1747 1748 if (! (x509Cert.getPublicKey() instanceof ECPublicKey)) { 1749 throw new JOSEException("Couldn't load EC JWK: The key algorithm is not EC"); 1750 } 1751 1752 ECKey ecJWK = ECKey.parse(x509Cert); 1753 1754 // Let kid=alias 1755 ecJWK = new ECKey.Builder(ecJWK).keyID(alias).keyStore(keyStore).build(); 1756 1757 // Check for private counterpart 1758 Key key; 1759 try { 1760 key = keyStore.getKey(alias, pin); 1761 } catch (UnrecoverableKeyException | NoSuchAlgorithmException e) { 1762 throw new JOSEException("Couldn't retrieve private EC key (bad pin?): " + e.getMessage(), e); 1763 } 1764 1765 if (key instanceof ECPrivateKey) { 1766 // Simple file based key store 1767 return new ECKey.Builder(ecJWK) 1768 .privateKey((ECPrivateKey)key) 1769 .build(); 1770 } else if (key instanceof PrivateKey && "EC".equalsIgnoreCase(key.getAlgorithm())) { 1771 // PKCS#11 store 1772 return new ECKey.Builder(ecJWK) 1773 .privateKey((PrivateKey)key) 1774 .build(); 1775 } else { 1776 return ecJWK; 1777 } 1778 } 1779}