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