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