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