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