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