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 2020-02-09 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 * Casts this JWK to an RSA JWK. 440 * 441 * @return The RSA JWK. 442 */ 443 public RSAKey toRSAKey() { 444 return (RSAKey)this; 445 } 446 447 448 /** 449 * Casts this JWK to an EC JWK. 450 * 451 * @return The EC JWK. 452 */ 453 public ECKey toECKey() { 454 return (ECKey)this; 455 } 456 457 458 /** 459 * Casts this JWK to an octet sequence JWK. 460 * 461 * @return The octet sequence JWK. 462 */ 463 public OctetSequenceKey toOctetSequenceKey() { 464 return (OctetSequenceKey)this; 465 } 466 467 468 /** 469 * Casts this JWK to an octet key pair JWK. 470 * 471 * @return The octet key pair JWK. 472 */ 473 public OctetKeyPair toOctetKeyPair() { 474 return (OctetKeyPair)this; 475 } 476 477 478 /** 479 * Returns a JSON object representation of this JWK. This method is 480 * intended to be called from extending classes. 481 * 482 * <p>Example: 483 * 484 * <pre> 485 * { 486 * "kty" : "RSA", 487 * "use" : "sig", 488 * "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b" 489 * } 490 * </pre> 491 * 492 * @return The JSON object representation. 493 */ 494 public JSONObject toJSONObject() { 495 496 JSONObject o = new JSONObject(); 497 498 o.put("kty", kty.getValue()); 499 500 if (use != null) { 501 o.put("use", use.identifier()); 502 } 503 504 if (ops != null) { 505 JSONArray stringValues = new JSONArray(); 506 for (KeyOperation op: ops) { 507 stringValues.add(op.identifier()); 508 } 509 o.put("key_ops", stringValues); 510 } 511 512 if (alg != null) { 513 o.put("alg", alg.getName()); 514 } 515 516 if (kid != null) { 517 o.put("kid", kid); 518 } 519 520 if (x5u != null) { 521 o.put("x5u", x5u.toString()); 522 } 523 524 if (x5t != null) { 525 o.put("x5t", x5t.toString()); 526 } 527 528 if (x5t256 != null) { 529 o.put("x5t#S256", x5t256.toString()); 530 } 531 532 if (x5c != null) { 533 JSONArray stringValues = new JSONArray(); 534 for (Base64 base64: x5c) { 535 stringValues.add(base64.toString()); 536 } 537 o.put("x5c", stringValues); 538 } 539 540 return o; 541 } 542 543 544 /** 545 * Returns the JSON object string representation of this JWK. 546 * 547 * @return The JSON object string representation. 548 */ 549 @Override 550 public String toJSONString() { 551 552 return toJSONObject().toString(); 553 } 554 555 556 /** 557 * @see #toJSONString 558 */ 559 @Override 560 public String toString() { 561 562 return toJSONObject().toString(); 563 } 564 565 566 /** 567 * Parses a JWK from the specified JSON object string representation. 568 * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 569 * {@link OctetSequenceKey}. 570 * 571 * @param s The JSON object string to parse. Must not be {@code null}. 572 * 573 * @return The JWK. 574 * 575 * @throws ParseException If the string couldn't be parsed to a 576 * supported JWK. 577 */ 578 public static JWK parse(final String s) 579 throws ParseException { 580 581 return parse(JSONObjectUtils.parse(s)); 582 } 583 584 585 /** 586 * Parses a JWK from the specified JSON object representation. The JWK 587 * must be an {@link ECKey}, an {@link RSAKey}, or a 588 * {@link OctetSequenceKey}. 589 * 590 * @param jsonObject The JSON object to parse. Must not be 591 * {@code null}. 592 * 593 * @return The JWK. 594 * 595 * @throws ParseException If the JSON object couldn't be parsed to a 596 * supported JWK. 597 */ 598 public static JWK parse(final JSONObject jsonObject) 599 throws ParseException { 600 601 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 602 603 if (kty == KeyType.EC) { 604 605 return ECKey.parse(jsonObject); 606 607 } else if (kty == KeyType.RSA) { 608 609 return RSAKey.parse(jsonObject); 610 611 } else if (kty == KeyType.OCT) { 612 613 return OctetSequenceKey.parse(jsonObject); 614 615 } else if (kty == KeyType.OKP) { 616 617 return OctetKeyPair.parse(jsonObject); 618 619 } else { 620 621 throw new ParseException("Unsupported key type \"kty\" parameter: " + kty, 0); 622 } 623 } 624 625 626 /** 627 * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the 628 * specified 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 cert The X.509 certificate. Must not be {@code null}. 645 * 646 * @return The public RSA or EC JWK. 647 * 648 * @throws JOSEException If parsing failed. 649 */ 650 public static JWK parse(final X509Certificate cert) 651 throws JOSEException { 652 653 if (cert.getPublicKey() instanceof RSAPublicKey) { 654 return RSAKey.parse(cert); 655 } else if (cert.getPublicKey() instanceof ECPublicKey) { 656 return ECKey.parse(cert); 657 } else { 658 throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm()); 659 } 660 } 661 662 663 /** 664 * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the 665 * specified PEM-encoded X.509 certificate. Requires BouncyCastle. 666 * 667 * <p><strong>Important:</strong> The X.509 certificate is not 668 * validated! 669 * 670 * <p>Sets the following JWK parameters: 671 * 672 * <ul> 673 * <li>For an EC key the curve is obtained from the subject public 674 * key info algorithm parameters. 675 * <li>The JWK use inferred by {@link KeyUse#from}. 676 * <li>The JWK ID from the X.509 serial number (in base 10). 677 * <li>The JWK X.509 certificate chain (this certificate only). 678 * <li>The JWK X.509 certificate SHA-256 thumbprint. 679 * </ul> 680 * 681 * @param pemEncodedCert The PEM-encoded X.509 certificate. Must not be 682 * {@code null}. 683 * 684 * @return The public RSA or EC JWK. 685 * 686 * @throws JOSEException If parsing failed. 687 */ 688 public static JWK parseFromPEMEncodedX509Cert(final String pemEncodedCert) 689 throws JOSEException { 690 691 X509Certificate cert = X509CertUtils.parse(pemEncodedCert); 692 693 if (cert == null) { 694 throw new JOSEException("Couldn't parse PEM-encoded X.509 certificate"); 695 } 696 697 return parse(cert); 698 } 699 700 701 /** 702 * Loads a JWK from the specified JCE key store. The JWK can be a 703 * public / private {@link RSAKey RSA key}, a public / private 704 * {@link ECKey EC key}, or a {@link OctetSequenceKey secret key}. 705 * Requires BouncyCastle. 706 * 707 * <p><strong>Important:</strong> The X.509 certificate is not 708 * validated! 709 * 710 * @param keyStore The key store. Must not be {@code null}. 711 * @param alias The alias. Must not be {@code null}. 712 * @param pin The pin to unlock the private key if any, empty or 713 * {@code null} if not required. 714 * 715 * @return The public / private RSA or EC JWK, or secret JWK, or 716 * {@code null} if no key with the specified alias was found. 717 * 718 * @throws KeyStoreException On a key store exception. 719 * @throws JOSEException If RSA or EC key loading failed. 720 */ 721 public static JWK load(final KeyStore keyStore, final String alias, final char[] pin) 722 throws KeyStoreException, JOSEException { 723 724 java.security.cert.Certificate cert = keyStore.getCertificate(alias); 725 726 if (cert == null) { 727 // Try secret key 728 return OctetSequenceKey.load(keyStore, alias, pin); 729 } 730 731 if (cert.getPublicKey() instanceof RSAPublicKey) { 732 return RSAKey.load(keyStore, alias, pin); 733 } else if (cert.getPublicKey() instanceof ECPublicKey) { 734 return ECKey.load(keyStore, alias, pin); 735 } else { 736 throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm()); 737 } 738 } 739 740 /** 741 * Parses an RSA or EC JWK from the specified string of one or more 742 * PEM-encoded object(s): 743 * 744 * <ul> 745 * <li>X.509 certificate (PEM header: BEGIN CERTIFICATE) 746 * <li>PKCS#1 RSAPublicKey (PEM header: BEGIN RSA PUBLIC KEY) 747 * <li>X.509 SubjectPublicKeyInfo (PEM header: BEGIN PUBLIC KEY) 748 * <li>PKCS#1 RSAPrivateKey (PEM header: BEGIN RSA PRIVATE KEY) 749 * <li>PKCS#8 PrivateKeyInfo (PEM header: BEGIN PRIVATE KEY) 750 * <li>matching pair of the above 751 * </ul> 752 * 753 * <p>Requires BouncyCastle. 754 * 755 * @param pemEncodedObjects The string of PEM-encoded object(s). 756 * 757 * @return The public / (private) RSA or EC JWK. 758 * 759 * @throws JOSEException If RSA or EC key parsing failed. 760 */ 761 public static JWK parseFromPEMEncodedObjects(final String pemEncodedObjects) 762 throws JOSEException { 763 764 final List<KeyPair> keys = PEMEncodedKeyParser.parseKeys(pemEncodedObjects); 765 if (keys.isEmpty()) { 766 throw new JOSEException("No PEM-encoded keys found"); 767 } 768 769 final KeyPair pair = mergeKeyPairs(keys); 770 771 final PublicKey publicKey = pair.getPublic(); 772 final PrivateKey privateKey = pair.getPrivate(); 773 774 if (publicKey == null) { 775 // For EC keys, for RSA the public can be reconstructed 776 throw new JOSEException("Missing PEM-encoded public key to construct JWK"); 777 } 778 779 if (publicKey instanceof ECPublicKey) { 780 final ECPublicKey ecPubKey = (ECPublicKey) publicKey; 781 final ECParameterSpec pubParams = ecPubKey.getParams(); 782 783 if (privateKey instanceof ECPrivateKey) { 784 validateEcCurves(ecPubKey, (ECPrivateKey) privateKey); 785 } 786 if (privateKey != null && !(privateKey instanceof ECPrivateKey)) { 787 throw new JOSEException("Unsupported EC private key type: " + privateKey); 788 } 789 790 final Curve curve = Curve.forECParameterSpec(pubParams); 791 final ECKey.Builder builder = new ECKey.Builder(curve, (ECPublicKey) publicKey); 792 793 if (privateKey != null) { 794 builder.privateKey((ECPrivateKey) privateKey); 795 } 796 return builder.build(); 797 } 798 799 if (publicKey instanceof RSAPublicKey) { 800 final RSAKey.Builder builder = new RSAKey.Builder((RSAPublicKey) publicKey); 801 if (privateKey instanceof RSAPrivateKey) { 802 builder.privateKey((RSAPrivateKey) privateKey); 803 } else if (privateKey != null) { 804 throw new JOSEException("Unsupported RSA private key type: " + privateKey); 805 } 806 return builder.build(); 807 } 808 809 throw new JOSEException("Unsupported algorithm of PEM-encoded key: " + publicKey.getAlgorithm()); 810 } 811 812 813 private static void validateEcCurves(ECPublicKey publicKey, ECPrivateKey privateKey) throws JOSEException { 814 final ECParameterSpec pubParams = publicKey.getParams(); 815 final ECParameterSpec privParams = privateKey.getParams(); 816 if (!pubParams.getCurve().equals(privParams.getCurve())) { 817 throw new JOSEException("Public/private EC key curve mismatch: " + publicKey); 818 } 819 if (pubParams.getCofactor() != privParams.getCofactor()) { 820 throw new JOSEException("Public/private EC key cofactor mismatch: " + publicKey); 821 } 822 if (!pubParams.getGenerator().equals(privParams.getGenerator())) { 823 throw new JOSEException("Public/private EC key generator mismatch: " + publicKey); 824 } 825 if (!pubParams.getOrder().equals(privParams.getOrder())) { 826 throw new JOSEException("Public/private EC key order mismatch: " + publicKey); 827 } 828 } 829 830 831 private static KeyPair mergeKeyPairs(final List<KeyPair> keys) throws JOSEException { 832 final KeyPair pair; 833 if (keys.size() == 1) { 834 // Assume public key, or private key easy to convert to public, 835 // otherwise not representable as a JWK 836 pair = keys.get(0); 837 } else if (keys.size() == 2) { 838 // If two keys, assume public + private keys separated 839 pair = twoKeysToKeyPair(keys); 840 } else { 841 throw new JOSEException("Expected key or pair of PEM-encoded keys"); 842 } 843 return pair; 844 } 845 846 847 private static KeyPair twoKeysToKeyPair(final List<? extends KeyPair> keys) throws JOSEException { 848 final KeyPair key1 = keys.get(0); 849 final KeyPair key2 = keys.get(1); 850 if (key1.getPublic() != null && key2.getPrivate() != null) { 851 return new KeyPair(key1.getPublic(), key2.getPrivate()); 852 } else if (key1.getPrivate() != null && key2.getPublic() != null) { 853 return new KeyPair(key2.getPublic(), key1.getPrivate()); 854 } else { 855 throw new JOSEException("Not a public/private key pair"); 856 } 857 } 858 859 860 @Override 861 public boolean equals(Object o) { 862 if (this == o) return true; 863 if (!(o instanceof JWK)) return false; 864 JWK jwk = (JWK) o; 865 return Objects.equals(kty, jwk.kty) && 866 Objects.equals(use, jwk.use) && 867 Objects.equals(ops, jwk.ops) && 868 Objects.equals(alg, jwk.alg) && 869 Objects.equals(kid, jwk.kid) && 870 Objects.equals(x5u, jwk.x5u) && 871 Objects.equals(x5t, jwk.x5t) && 872 Objects.equals(x5t256, jwk.x5t256) && 873 Objects.equals(x5c, jwk.x5c) && 874 Objects.equals(keyStore, jwk.keyStore); 875 } 876 877 878 @Override 879 public int hashCode() { 880 return Objects.hash(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, keyStore); 881 } 882}