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-09 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 \"kty\" 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 \"use\" and key options \"key_opts\" 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 \"x5c\" 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 \"x5c\": " + 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 * Computes the thumbprint of this JWK using the specified hash 391 * algorithm. See RFC 7638 for more information. 392 * 393 * @param hashAlg The hash algorithm. Must not be {@code null}. 394 * 395 * @return The SHA-256 thumbprint. 396 * 397 * @throws JOSEException If the hash algorithm is not supported. 398 */ 399 public Base64URL computeThumbprint(final String hashAlg) 400 throws JOSEException { 401 402 return ThumbprintUtils.compute(hashAlg, this); 403 } 404 405 406 /** 407 * Returns {@code true} if this JWK contains private or sensitive 408 * (non-public) parameters. 409 * 410 * @return {@code true} if this JWK contains private parameters, else 411 * {@code false}. 412 */ 413 public abstract boolean isPrivate(); 414 415 416 /** 417 * Creates a copy of this JWK with all private or sensitive parameters 418 * removed. 419 * 420 * @return The newly created public JWK, or {@code null} if none can be 421 * created. 422 */ 423 public abstract JWK toPublicJWK(); 424 425 426 /** 427 * Returns the size of this JWK. 428 * 429 * @return The JWK size, in bits. 430 */ 431 public abstract int size(); 432 433 434 /** 435 * Casts this JWK to an RSA JWK. 436 * 437 * @return The RSA JWK. 438 */ 439 public RSAKey toRSAKey() { 440 return (RSAKey)this; 441 } 442 443 444 /** 445 * Casts this JWK to an EC JWK. 446 * 447 * @return The EC JWK. 448 */ 449 public ECKey toECKey() { 450 return (ECKey)this; 451 } 452 453 454 /** 455 * Casts this JWK to an octet sequence JWK. 456 * 457 * @return The octet sequence JWK. 458 */ 459 public OctetSequenceKey toOctetSequenceKey() { 460 return (OctetSequenceKey)this; 461 } 462 463 464 /** 465 * Casts this JWK to an octet key pair JWK. 466 * 467 * @return The octet key pair JWK. 468 */ 469 public OctetKeyPair toOctetKeyPair() { 470 return (OctetKeyPair)this; 471 } 472 473 474 /** 475 * Returns a JSON object representation of this JWK. This method is 476 * intended to be called from extending classes. 477 * 478 * <p>Example: 479 * 480 * <pre> 481 * { 482 * "kty" : "RSA", 483 * "use" : "sig", 484 * "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b" 485 * } 486 * </pre> 487 * 488 * @return The JSON object representation. 489 */ 490 public Map<String, Object> toJSONObject() { 491 492 Map<String, Object> o = JSONObjectUtils.newJSONObject(); 493 494 o.put("kty", kty.getValue()); 495 496 if (use != null) { 497 o.put("use", use.identifier()); 498 } 499 500 if (ops != null) { 501 List<Object> stringValues = JSONArrayUtils.newJSONArray(); 502 for (KeyOperation op: ops) { 503 stringValues.add(op.identifier()); 504 } 505 o.put("key_ops", stringValues); 506 } 507 508 if (alg != null) { 509 o.put("alg", alg.getName()); 510 } 511 512 if (kid != null) { 513 o.put("kid", kid); 514 } 515 516 if (x5u != null) { 517 o.put("x5u", x5u.toString()); 518 } 519 520 if (x5t != null) { 521 o.put("x5t", x5t.toString()); 522 } 523 524 if (x5t256 != null) { 525 o.put("x5t#S256", x5t256.toString()); 526 } 527 528 if (x5c != null) { 529 List<Object> stringValues = JSONArrayUtils.newJSONArray(); 530 for (Base64 base64: x5c) { 531 stringValues.add(base64.toString()); 532 } 533 o.put("x5c", stringValues); 534 } 535 536 return o; 537 } 538 539 540 /** 541 * Returns the JSON object string representation of this JWK. 542 * 543 * @return The JSON object string representation. 544 */ 545 public String toJSONString() { 546 return JSONObjectUtils.toJSONString(toJSONObject()); 547 } 548 549 550 /** 551 * @see #toJSONString 552 */ 553 @Override 554 public String toString() { 555 556 return JSONObjectUtils.toJSONString(toJSONObject()); 557 } 558 559 560 /** 561 * Parses a JWK from the specified JSON object string representation. 562 * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 563 * {@link OctetSequenceKey}. 564 * 565 * @param s The JSON object string to parse. Must not be {@code null}. 566 * 567 * @return The JWK. 568 * 569 * @throws ParseException If the string couldn't be parsed to a 570 * supported JWK. 571 */ 572 public static JWK parse(final String s) 573 throws ParseException { 574 575 return parse(JSONObjectUtils.parse(s)); 576 } 577 578 579 /** 580 * Parses a JWK from the specified JSON object representation. The JWK 581 * must be an {@link ECKey}, an {@link RSAKey}, or a 582 * {@link OctetSequenceKey}. 583 * 584 * @param jsonObject The JSON object to parse. Must not be 585 * {@code null}. 586 * 587 * @return The JWK. 588 * 589 * @throws ParseException If the JSON object couldn't be parsed to a 590 * supported JWK. 591 */ 592 public static JWK parse(final Map<String, Object> jsonObject) 593 throws ParseException { 594 595 String ktyString = JSONObjectUtils.getString(jsonObject, "kty"); 596 597 if (ktyString == null) { 598 throw new ParseException("Missing key type \"kty\" parameter", 0); 599 } 600 601 KeyType kty = KeyType.parse(ktyString); 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}