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