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