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