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.net.URI; 023import java.security.*; 024import java.security.cert.X509Certificate; 025import java.security.interfaces.ECPrivateKey; 026import java.security.interfaces.ECPublicKey; 027import java.security.interfaces.RSAPrivateKey; 028import java.security.interfaces.RSAPublicKey; 029import java.security.spec.ECParameterSpec; 030import java.text.ParseException; 031import java.util.*; 032 033import com.nimbusds.jose.Algorithm; 034import com.nimbusds.jose.JOSEException; 035import com.nimbusds.jose.util.Base64; 036import com.nimbusds.jose.util.*; 037 038 039/** 040 * The base abstract class for JSON Web Keys (JWKs). It serialises to a JSON 041 * object. 042 * 043 * <p>The following JSON object members are common to all JWK types: 044 * 045 * <ul> 046 * <li>{@link #getKeyType kty} (required) 047 * <li>{@link #getKeyUse use} (optional) 048 * <li>{@link #getKeyOperations key_ops} (optional) 049 * <li>{@link #getKeyID kid} (optional) 050 * <li>{@link #getX509CertURL() x5u} (optional) 051 * <li>{@link #getX509CertThumbprint() x5t} (optional) 052 * <li>{@link #getX509CertSHA256Thumbprint() x5t#S256} (optional) 053 * <li>{@link #getX509CertChain() x5c} (optional) 054 * <li>{@link #getKeyStore()} 055 * </ul> 056 * 057 * <p>Example JWK (of the Elliptic Curve type): 058 * 059 * <pre> 060 * { 061 * "kty" : "EC", 062 * "crv" : "P-256", 063 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 064 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 065 * "use" : "enc", 066 * "kid" : "1" 067 * } 068 * </pre> 069 * 070 * @author Vladimir Dzhuvinov 071 * @author Justin Richer 072 * @author Stefan Larsson 073 * @version 2020-02-21 074 */ 075public abstract class JWK implements Serializable { 076 077 078 private static final long serialVersionUID = 1L; 079 080 081 /** 082 * The MIME type of JWK objects: 083 * {@code application/jwk+json; charset=UTF-8} 084 */ 085 public static final String MIME_TYPE = "application/jwk+json; charset=UTF-8"; 086 087 088 /** 089 * The key type, required. 090 */ 091 private final KeyType kty; 092 093 094 /** 095 * The key use, optional. 096 */ 097 private final KeyUse use; 098 099 100 /** 101 * The key operations, optional. 102 */ 103 private final Set<KeyOperation> ops; 104 105 106 /** 107 * The intended JOSE algorithm for the key, optional. 108 */ 109 private final Algorithm alg; 110 111 112 /** 113 * The key ID, optional. 114 */ 115 private final String kid; 116 117 118 /** 119 * X.509 certificate URL, optional. 120 */ 121 private final URI x5u; 122 123 124 /** 125 * X.509 certificate SHA-1 thumbprint, optional. 126 */ 127 @Deprecated 128 private final Base64URL x5t; 129 130 131 /** 132 * X.509 certificate SHA-256 thumbprint, optional. 133 */ 134 private final Base64URL x5t256; 135 136 137 /** 138 * The X.509 certificate chain, optional. 139 */ 140 private final List<Base64> x5c; 141 142 143 /** 144 * The parsed X.509 certificate chain, optional. 145 */ 146 private final List<X509Certificate> parsedX5c; 147 148 149 /** 150 * Reference to the underlying key store, {@code null} if none. 151 */ 152 private final KeyStore keyStore; 153 154 155 /** 156 * Creates a new JSON Web Key (JWK). 157 * 158 * @param kty The key type. Must not be {@code null}. 159 * @param use The key use, {@code null} if not specified or if the 160 * key is intended for signing as well as encryption. 161 * @param ops The key operations, {@code null} if not specified. 162 * @param alg The intended JOSE algorithm for the key, {@code null} 163 * if not specified. 164 * @param kid The key ID, {@code null} if not specified. 165 * @param x5u The X.509 certificate URL, {@code null} if not 166 * specified. 167 * @param x5t The X.509 certificate thumbprint, {@code null} if not 168 * specified. 169 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 170 * if not specified. 171 * @param x5c The X.509 certificate chain, {@code null} if not 172 * specified. 173 * @param ks Reference to the underlying key store, {@code null} if 174 * none. 175 */ 176 protected JWK(final KeyType kty, 177 final KeyUse use, 178 final Set<KeyOperation> ops, 179 final Algorithm alg, 180 final String kid, 181 final URI x5u, 182 final Base64URL x5t, 183 final Base64URL x5t256, 184 final List<Base64> x5c, 185 final KeyStore ks) { 186 187 if (kty == null) { 188 throw new IllegalArgumentException("The key type \"" + JWKParameterNames.KEY_TYPE + "\" parameter must not be null"); 189 } 190 191 this.kty = kty; 192 193 if (! KeyUseAndOpsConsistency.areConsistent(use, ops)) { 194 throw new IllegalArgumentException("The key use \"" + JWKParameterNames.PUBLIC_KEY_USE + "\" and key options \"" + JWKParameterNames.KEY_OPS + "\" parameters are not consistent, " + 195 "see RFC 7517, section 4.3"); 196 } 197 198 this.use = use; 199 this.ops = ops; 200 201 this.alg = alg; 202 this.kid = kid; 203 204 this.x5u = x5u; 205 this.x5t = x5t; 206 this.x5t256 = x5t256; 207 208 if (x5c != null && x5c.isEmpty()) { 209 throw new IllegalArgumentException("The X.509 certificate chain \"" + JWKParameterNames.X_509_CERT_CHAIN + "\" must not be empty"); 210 } 211 this.x5c = x5c; 212 213 try { 214 parsedX5c = X509CertChainUtils.parse(x5c); 215 } catch (ParseException e) { 216 throw new IllegalArgumentException("Invalid X.509 certificate chain \"" + JWKParameterNames.X_509_CERT_CHAIN + "\": " + e.getMessage(), e); 217 } 218 219 this.keyStore = ks; 220 } 221 222 223 /** 224 * Gets the type ({@code kty}) of this JWK. 225 * 226 * @return The key type. 227 */ 228 public KeyType getKeyType() { 229 230 return kty; 231 } 232 233 234 /** 235 * Gets the use ({@code use}) of this JWK. 236 * 237 * @return The key use, {@code null} if not specified or if the key is 238 * intended for signing as well as encryption. 239 */ 240 public KeyUse getKeyUse() { 241 242 return use; 243 } 244 245 246 /** 247 * Gets the operations ({@code key_ops}) for this JWK. 248 * 249 * @return The key operations, {@code null} if not specified. 250 */ 251 public Set<KeyOperation> getKeyOperations() { 252 253 return ops; 254 } 255 256 257 /** 258 * Gets the intended JOSE algorithm ({@code alg}) for this JWK. 259 * 260 * @return The intended JOSE algorithm, {@code null} if not specified. 261 */ 262 public Algorithm getAlgorithm() { 263 264 return alg; 265 } 266 267 268 /** 269 * Gets the ID ({@code kid}) of this JWK. The key ID can be used to 270 * match a specific key. This can be used, for instance, to choose a 271 * key within a {@link JWKSet} during key rollover. The key ID may also 272 * correspond to a JWS/JWE {@code kid} header parameter value. 273 * 274 * @return The key ID, {@code null} if not specified. 275 */ 276 public String getKeyID() { 277 278 return kid; 279 } 280 281 282 /** 283 * Gets the X.509 certificate URL ({@code x5u}) of this JWK. 284 * 285 * @return The X.509 certificate URL, {@code null} if not specified. 286 */ 287 public URI getX509CertURL() { 288 289 return x5u; 290 } 291 292 293 /** 294 * Gets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of this 295 * JWK. 296 * 297 * @return The X.509 certificate SHA-1 thumbprint, {@code null} if not 298 * specified. 299 */ 300 @Deprecated 301 public Base64URL getX509CertThumbprint() { 302 303 return x5t; 304 } 305 306 307 /** 308 * Gets the X.509 certificate SHA-256 thumbprint ({@code x5t#S256}) of 309 * this JWK. 310 * 311 * @return The X.509 certificate SHA-256 thumbprint, {@code null} if 312 * not specified. 313 */ 314 public Base64URL getX509CertSHA256Thumbprint() { 315 316 return x5t256; 317 } 318 319 320 /** 321 * Gets the X.509 certificate chain ({@code x5c}) of this JWK. 322 * 323 * @return The X.509 certificate chain as a unmodifiable list, 324 * {@code null} if not specified. 325 */ 326 public List<Base64> getX509CertChain() { 327 328 if (x5c == null) { 329 return null; 330 } 331 332 return Collections.unmodifiableList(x5c); 333 } 334 335 336 /** 337 * Gets the parsed X.509 certificate chain ({@code x5c}) of this JWK. 338 * 339 * @return The X.509 certificate chain as a unmodifiable list, 340 * {@code null} if not specified. 341 */ 342 public List<X509Certificate> getParsedX509CertChain() { 343 344 if (parsedX5c == null) { 345 return null; 346 } 347 348 return Collections.unmodifiableList(parsedX5c); 349 } 350 351 352 /** 353 * Returns a reference to the underlying key store. 354 * 355 * @return The underlying key store, {@code null} if none. 356 */ 357 public KeyStore getKeyStore() { 358 359 return keyStore; 360 } 361 362 363 /** 364 * Returns the required JWK parameters. Intended as input for JWK 365 * thumbprint computation. See RFC 7638 for more information. 366 * 367 * @return The required JWK parameters, sorted alphanumerically by key 368 * name and ready for JSON serialisation. 369 */ 370 public abstract LinkedHashMap<String,?> getRequiredParams(); 371 372 373 /** 374 * Computes the SHA-256 thumbprint of this JWK. See RFC 7638 for more 375 * information. 376 * 377 * @return The SHA-256 thumbprint. 378 * 379 * @throws JOSEException If the SHA-256 hash algorithm is not 380 * supported. 381 */ 382 public Base64URL computeThumbprint() 383 throws JOSEException { 384 385 return computeThumbprint("SHA-256"); 386 387 } 388 389 390 /** 391 * Computes the thumbprint of this JWK using the specified hash 392 * algorithm. See RFC 7638 for more information. 393 * 394 * @param hashAlg The hash algorithm. Must not be {@code null}. 395 * 396 * @return The SHA-256 thumbprint. 397 * 398 * @throws JOSEException If the hash algorithm is not supported. 399 */ 400 public Base64URL computeThumbprint(final String hashAlg) 401 throws JOSEException { 402 403 return ThumbprintUtils.compute(hashAlg, this); 404 } 405 406 407 /** 408 * Computes the SHA-256 thumbprint URI of this JWK. See RFC 7638 and 409 * draft-ietf-oauth-jwk-thumbprint-uri for more information. 410 * 411 * @return The SHA-256 thumbprint URI. 412 * 413 * @throws JOSEException If the SHA-256 hash algorithm is not 414 * supported. 415 */ 416 public ThumbprintURI computeThumbprintURI() 417 throws JOSEException { 418 419 return new ThumbprintURI("sha-256", computeThumbprint("SHA-256")); 420 } 421 422 423 /** 424 * Returns {@code true} if this JWK contains private or sensitive 425 * (non-public) parameters. 426 * 427 * @return {@code true} if this JWK contains private parameters, else 428 * {@code false}. 429 */ 430 public abstract boolean isPrivate(); 431 432 433 /** 434 * Creates a copy of this JWK with all private or sensitive parameters 435 * removed. 436 * 437 * @return The newly created public JWK, or {@code null} if none can be 438 * created. 439 */ 440 public abstract JWK toPublicJWK(); 441 442 443 /** 444 * Returns the size of this JWK. 445 * 446 * @return The JWK size, in bits. 447 */ 448 public abstract int size(); 449 450 451 /** 452 * Casts this JWK to an RSA JWK. 453 * 454 * @return The RSA JWK. 455 */ 456 public RSAKey toRSAKey() { 457 return (RSAKey)this; 458 } 459 460 461 /** 462 * Casts this JWK to an EC JWK. 463 * 464 * @return The EC JWK. 465 */ 466 public ECKey toECKey() { 467 return (ECKey)this; 468 } 469 470 471 /** 472 * Casts this JWK to an octet sequence JWK. 473 * 474 * @return The octet sequence JWK. 475 */ 476 public OctetSequenceKey toOctetSequenceKey() { 477 return (OctetSequenceKey)this; 478 } 479 480 481 /** 482 * Casts this JWK to an octet key pair JWK. 483 * 484 * @return The octet key pair JWK. 485 */ 486 public OctetKeyPair toOctetKeyPair() { 487 return (OctetKeyPair)this; 488 } 489 490 491 /** 492 * Returns a JSON object representation of this JWK. This method is 493 * intended to be called from extending classes. 494 * 495 * <p>Example: 496 * 497 * <pre> 498 * { 499 * "kty" : "RSA", 500 * "use" : "sig", 501 * "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b" 502 * } 503 * </pre> 504 * 505 * @return The JSON object representation. 506 */ 507 public Map<String, Object> toJSONObject() { 508 509 Map<String, Object> o = JSONObjectUtils.newJSONObject(); 510 511 o.put(JWKParameterNames.KEY_TYPE, kty.getValue()); 512 513 if (use != null) { 514 o.put(JWKParameterNames.PUBLIC_KEY_USE, use.identifier()); 515 } 516 517 if (ops != null) { 518 List<Object> stringValues = JSONArrayUtils.newJSONArray(); 519 for (KeyOperation op: ops) { 520 stringValues.add(op.identifier()); 521 } 522 o.put(JWKParameterNames.KEY_OPS, stringValues); 523 } 524 525 if (alg != null) { 526 o.put(JWKParameterNames.ALGORITHM, alg.getName()); 527 } 528 529 if (kid != null) { 530 o.put(JWKParameterNames.KEY_ID, kid); 531 } 532 533 if (x5u != null) { 534 o.put(JWKParameterNames.X_509_CERT_URL, x5u.toString()); 535 } 536 537 if (x5t != null) { 538 o.put(JWKParameterNames.X_509_CERT_SHA_1_THUMBPRINT, x5t.toString()); 539 } 540 541 if (x5t256 != null) { 542 o.put(JWKParameterNames.X_509_CERT_SHA_256_THUMBPRINT, x5t256.toString()); 543 } 544 545 if (x5c != null) { 546 List<Object> stringValues = JSONArrayUtils.newJSONArray(); 547 for (Base64 base64: x5c) { 548 stringValues.add(base64.toString()); 549 } 550 o.put(JWKParameterNames.X_509_CERT_CHAIN, stringValues); 551 } 552 553 return o; 554 } 555 556 557 /** 558 * Returns the JSON object string representation of this JWK. 559 * 560 * @return The JSON object string representation. 561 */ 562 public String toJSONString() { 563 return JSONObjectUtils.toJSONString(toJSONObject()); 564 } 565 566 567 /** 568 * @see #toJSONString 569 */ 570 @Override 571 public String toString() { 572 573 return JSONObjectUtils.toJSONString(toJSONObject()); 574 } 575 576 577 /** 578 * Parses a JWK from the specified JSON object string representation. 579 * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 580 * {@link OctetSequenceKey}. 581 * 582 * @param s The JSON object string to parse. Must not be {@code null}. 583 * 584 * @return The JWK. 585 * 586 * @throws ParseException If the string couldn't be parsed to a 587 * supported JWK. 588 */ 589 public static JWK parse(final String s) 590 throws ParseException { 591 592 return parse(JSONObjectUtils.parse(s)); 593 } 594 595 596 /** 597 * Parses a JWK from the specified JSON object representation. The JWK 598 * must be an {@link ECKey}, an {@link RSAKey}, or a 599 * {@link OctetSequenceKey}. 600 * 601 * @param jsonObject The JSON object to parse. Must not be 602 * {@code null}. 603 * 604 * @return The JWK. 605 * 606 * @throws ParseException If the JSON object couldn't be parsed to a 607 * supported JWK. 608 */ 609 public static JWK parse(final Map<String, Object> jsonObject) 610 throws ParseException { 611 612 String ktyString = JSONObjectUtils.getString(jsonObject, JWKParameterNames.KEY_TYPE); 613 614 if (ktyString == null) { 615 throw new ParseException("Missing key type \"kty\" parameter", 0); 616 } 617 618 KeyType kty = KeyType.parse(ktyString); 619 620 if (kty == KeyType.EC) { 621 622 return ECKey.parse(jsonObject); 623 624 } else if (kty == KeyType.RSA) { 625 626 return RSAKey.parse(jsonObject); 627 628 } else if (kty == KeyType.OCT) { 629 630 return OctetSequenceKey.parse(jsonObject); 631 632 } else if (kty == KeyType.OKP) { 633 634 return OctetKeyPair.parse(jsonObject); 635 636 } else { 637 638 throw new ParseException("Unsupported key type \"" + JWKParameterNames.KEY_TYPE + "\" parameter: " + kty, 0); 639 } 640 } 641 642 643 /** 644 * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the 645 * specified X.509 certificate. Requires BouncyCastle. 646 * 647 * <p><strong>Important:</strong> The X.509 certificate is not 648 * validated! 649 * 650 * <p>Sets the following JWK parameters: 651 * 652 * <ul> 653 * <li>For an EC key the curve is obtained from the subject public 654 * key info algorithm parameters. 655 * <li>The JWK use inferred by {@link KeyUse#from}. 656 * <li>The JWK ID from the X.509 serial number (in base 10). 657 * <li>The JWK X.509 certificate chain (this certificate only). 658 * <li>The JWK X.509 certificate SHA-256 thumbprint. 659 * </ul> 660 * 661 * @param cert The X.509 certificate. Must not be {@code null}. 662 * 663 * @return The public RSA or EC JWK. 664 * 665 * @throws JOSEException If parsing failed. 666 */ 667 public static JWK parse(final X509Certificate cert) 668 throws JOSEException { 669 670 if (cert.getPublicKey() instanceof RSAPublicKey) { 671 return RSAKey.parse(cert); 672 } else if (cert.getPublicKey() instanceof ECPublicKey) { 673 return ECKey.parse(cert); 674 } else { 675 throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm()); 676 } 677 } 678 679 680 /** 681 * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the 682 * specified PEM-encoded X.509 certificate. Requires BouncyCastle. 683 * 684 * <p><strong>Important:</strong> The X.509 certificate is not 685 * validated! 686 * 687 * <p>Sets the following JWK parameters: 688 * 689 * <ul> 690 * <li>For an EC key the curve is obtained from the subject public 691 * key info algorithm parameters. 692 * <li>The JWK use inferred by {@link KeyUse#from}. 693 * <li>The JWK ID from the X.509 serial number (in base 10). 694 * <li>The JWK X.509 certificate chain (this certificate only). 695 * <li>The JWK X.509 certificate SHA-256 thumbprint. 696 * </ul> 697 * 698 * @param pemEncodedCert The PEM-encoded X.509 certificate. Must not be 699 * {@code null}. 700 * 701 * @return The public RSA or EC JWK. 702 * 703 * @throws JOSEException If parsing failed. 704 */ 705 public static JWK parseFromPEMEncodedX509Cert(final String pemEncodedCert) 706 throws JOSEException { 707 708 X509Certificate cert = X509CertUtils.parse(pemEncodedCert); 709 710 if (cert == null) { 711 throw new JOSEException("Couldn't parse PEM-encoded X.509 certificate"); 712 } 713 714 return parse(cert); 715 } 716 717 718 /** 719 * Loads a JWK from the specified JCE key store. The JWK can be a 720 * public / private {@link RSAKey RSA key}, a public / private 721 * {@link ECKey EC key}, or a {@link OctetSequenceKey secret key}. 722 * Requires BouncyCastle. 723 * 724 * <p><strong>Important:</strong> The X.509 certificate is not 725 * validated! 726 * 727 * @param keyStore The key store. Must not be {@code null}. 728 * @param alias The alias. Must not be {@code null}. 729 * @param pin The pin to unlock the private key if any, empty or 730 * {@code null} if not required. 731 * 732 * @return The public / private RSA or EC JWK, or secret JWK, or 733 * {@code null} if no key with the specified alias was found. 734 * 735 * @throws KeyStoreException On a key store exception. 736 * @throws JOSEException If RSA or EC key loading failed. 737 */ 738 public static JWK load(final KeyStore keyStore, final String alias, final char[] pin) 739 throws KeyStoreException, JOSEException { 740 741 java.security.cert.Certificate cert = keyStore.getCertificate(alias); 742 743 if (cert == null) { 744 // Try secret key 745 return OctetSequenceKey.load(keyStore, alias, pin); 746 } 747 748 if (cert.getPublicKey() instanceof RSAPublicKey) { 749 return RSAKey.load(keyStore, alias, pin); 750 } else if (cert.getPublicKey() instanceof ECPublicKey) { 751 return ECKey.load(keyStore, alias, pin); 752 } else { 753 throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm()); 754 } 755 } 756 757 /** 758 * Parses an RSA or EC JWK from the specified string of one or more 759 * PEM-encoded object(s): 760 * 761 * <ul> 762 * <li>X.509 certificate (PEM header: BEGIN CERTIFICATE) 763 * <li>PKCS#1 RSAPublicKey (PEM header: BEGIN RSA PUBLIC KEY) 764 * <li>X.509 SubjectPublicKeyInfo (PEM header: BEGIN PUBLIC KEY) 765 * <li>PKCS#1 RSAPrivateKey (PEM header: BEGIN RSA PRIVATE KEY) 766 * <li>PKCS#8 PrivateKeyInfo (PEM header: BEGIN PRIVATE KEY) 767 * <li>matching pair of the above 768 * </ul> 769 * 770 * <p>Requires BouncyCastle. 771 * 772 * @param pemEncodedObjects The string of PEM-encoded object(s). 773 * 774 * @return The public / (private) RSA or EC JWK. 775 * 776 * @throws JOSEException If RSA or EC key parsing failed. 777 */ 778 public static JWK parseFromPEMEncodedObjects(final String pemEncodedObjects) 779 throws JOSEException { 780 781 final List<KeyPair> keys = PEMEncodedKeyParser.parseKeys(pemEncodedObjects); 782 if (keys.isEmpty()) { 783 throw new JOSEException("No PEM-encoded keys found"); 784 } 785 786 final KeyPair pair = mergeKeyPairs(keys); 787 788 final PublicKey publicKey = pair.getPublic(); 789 final PrivateKey privateKey = pair.getPrivate(); 790 791 if (publicKey == null) { 792 // For EC keys, for RSA the public can be reconstructed 793 throw new JOSEException("Missing PEM-encoded public key to construct JWK"); 794 } 795 796 if (publicKey instanceof ECPublicKey) { 797 final ECPublicKey ecPubKey = (ECPublicKey) publicKey; 798 final ECParameterSpec pubParams = ecPubKey.getParams(); 799 800 if (privateKey instanceof ECPrivateKey) { 801 validateEcCurves(ecPubKey, (ECPrivateKey) privateKey); 802 } 803 if (privateKey != null && !(privateKey instanceof ECPrivateKey)) { 804 throw new JOSEException("Unsupported " + KeyType.EC.getValue() + " private key type: " + privateKey); 805 } 806 807 final Curve curve = Curve.forECParameterSpec(pubParams); 808 final ECKey.Builder builder = new ECKey.Builder(curve, (ECPublicKey) publicKey); 809 810 if (privateKey != null) { 811 builder.privateKey((ECPrivateKey) privateKey); 812 } 813 return builder.build(); 814 } 815 816 if (publicKey instanceof RSAPublicKey) { 817 final RSAKey.Builder builder = new RSAKey.Builder((RSAPublicKey) publicKey); 818 if (privateKey instanceof RSAPrivateKey) { 819 builder.privateKey((RSAPrivateKey) privateKey); 820 } else if (privateKey != null) { 821 throw new JOSEException("Unsupported " + KeyType.RSA.getValue() + " private key type: " + privateKey); 822 } 823 return builder.build(); 824 } 825 826 throw new JOSEException("Unsupported algorithm of PEM-encoded key: " + publicKey.getAlgorithm()); 827 } 828 829 830 private static void validateEcCurves(ECPublicKey publicKey, ECPrivateKey privateKey) throws JOSEException { 831 final ECParameterSpec pubParams = publicKey.getParams(); 832 final ECParameterSpec privParams = privateKey.getParams(); 833 if (!pubParams.getCurve().equals(privParams.getCurve())) { 834 throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key curve mismatch: " + publicKey); 835 } 836 if (pubParams.getCofactor() != privParams.getCofactor()) { 837 throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key cofactor mismatch: " + publicKey); 838 } 839 if (!pubParams.getGenerator().equals(privParams.getGenerator())) { 840 throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key generator mismatch: " + publicKey); 841 } 842 if (!pubParams.getOrder().equals(privParams.getOrder())) { 843 throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key order mismatch: " + publicKey); 844 } 845 } 846 847 848 private static KeyPair mergeKeyPairs(final List<KeyPair> keys) throws JOSEException { 849 final KeyPair pair; 850 if (keys.size() == 1) { 851 // Assume public key, or private key easy to convert to public, 852 // otherwise not representable as a JWK 853 pair = keys.get(0); 854 } else if (keys.size() == 2) { 855 // If two keys, assume public + private keys separated 856 pair = twoKeysToKeyPair(keys); 857 } else { 858 throw new JOSEException("Expected key or pair of PEM-encoded keys"); 859 } 860 return pair; 861 } 862 863 864 private static KeyPair twoKeysToKeyPair(final List<? extends KeyPair> keys) throws JOSEException { 865 final KeyPair key1 = keys.get(0); 866 final KeyPair key2 = keys.get(1); 867 if (key1.getPublic() != null && key2.getPrivate() != null) { 868 return new KeyPair(key1.getPublic(), key2.getPrivate()); 869 } else if (key1.getPrivate() != null && key2.getPublic() != null) { 870 return new KeyPair(key2.getPublic(), key1.getPrivate()); 871 } else { 872 throw new JOSEException("Not a public/private key pair"); 873 } 874 } 875 876 877 @Override 878 public boolean equals(Object o) { 879 if (this == o) return true; 880 if (!(o instanceof JWK)) return false; 881 JWK jwk = (JWK) o; 882 return Objects.equals(kty, jwk.kty) && 883 Objects.equals(use, jwk.use) && 884 Objects.equals(ops, jwk.ops) && 885 Objects.equals(alg, jwk.alg) && 886 Objects.equals(kid, jwk.kid) && 887 Objects.equals(x5u, jwk.x5u) && 888 Objects.equals(x5t, jwk.x5t) && 889 Objects.equals(x5t256, jwk.x5t256) && 890 Objects.equals(x5c, jwk.x5c) && 891 Objects.equals(keyStore, jwk.keyStore); 892 } 893 894 895 @Override 896 public int hashCode() { 897 return Objects.hash(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, keyStore); 898 } 899}