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