001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 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 com.nimbusds.jose.Algorithm; 022import com.nimbusds.jose.JOSEException; 023import com.nimbusds.jose.util.Base64; 024import com.nimbusds.jose.util.Base64URL; 025import com.nimbusds.jose.util.ByteUtils; 026import com.nimbusds.jose.util.JSONObjectUtils; 027import net.jcip.annotations.Immutable; 028 029import java.net.URI; 030import java.security.KeyPair; 031import java.security.KeyStore; 032import java.security.PrivateKey; 033import java.security.PublicKey; 034import java.security.cert.X509Certificate; 035import java.text.ParseException; 036import java.util.*; 037 038 039/** 040 * {@link KeyType#OKP Octet key pair} JSON Web Key (JWK), used to represent 041 * Edwards-curve keys. This class is immutable. 042 * 043 * <p>Supported curves: 044 * 045 * <ul> 046 * <li>{@link Curve#Ed25519 Ed25519} 047 * <li>{@link Curve#Ed448 Ed448} 048 * <li>{@link Curve#X25519 X25519} 049 * <li>{@link Curve#X448 X448} 050 * </ul> 051 * 052 * <p>Example JSON object representation of a public OKP JWK: 053 * 054 * <pre> 055 * { 056 * "kty" : "OKP", 057 * "crv" : "Ed25519", 058 * "x" : "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", 059 * "use" : "sig", 060 * "kid" : "1" 061 * } 062 * </pre> 063 * 064 * <p>Example JSON object representation of a private OKP JWK: 065 * 066 * <pre> 067 * { 068 * "kty" : "OKP", 069 * "crv" : "Ed25519", 070 * "x" : "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", 071 * "d" : "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", 072 * "use" : "sig", 073 * "kid" : "1" 074 * } 075 * </pre> 076 * 077 * <p>Use the builder to create a new OKP JWK: 078 * 079 * <pre> 080 * OctetKeyPair key = new OctetKeyPair.Builder(Curve.Ed25519, x) 081 * .keyUse(KeyUse.SIGNATURE) 082 * .keyID("1") 083 * .build(); 084 * </pre> 085 * 086 * @author Vladimir Dzhuvinov 087 * @version 2020-06-03 088 */ 089@Immutable 090public class OctetKeyPair extends JWK implements AsymmetricJWK, CurveBasedJWK { 091 092 093 private static final long serialVersionUID = 1L; 094 095 096 /** 097 * Supported Edwards curves. 098 */ 099 public static final Set<Curve> SUPPORTED_CURVES = Collections.unmodifiableSet( 100 new HashSet<>(Arrays.asList(Curve.Ed25519, Curve.Ed448, Curve.X25519, Curve.X448)) 101 ); 102 103 104 /** 105 * Builder for constructing Octet Key Pair JWKs. 106 * 107 * <p>Example usage: 108 * 109 * <pre> 110 * OctetKeyPair key = new OctetKeyPair.Builder(Curve.Ed25519, x) 111 * .d(d) 112 * .algorithm(JWSAlgorithm.EdDSA) 113 * .keyID("1") 114 * .build(); 115 * </pre> 116 */ 117 public static class Builder { 118 119 120 /** 121 * The curve name. 122 */ 123 private final Curve crv; 124 125 126 /** 127 * The public 'x' parameter. 128 */ 129 private final Base64URL x; 130 131 132 /** 133 * The private 'd' parameter, optional. 134 */ 135 private Base64URL d; 136 137 138 /** 139 * The key use, optional. 140 */ 141 private KeyUse use; 142 143 144 /** 145 * The key operations, optional. 146 */ 147 private Set<KeyOperation> ops; 148 149 150 /** 151 * The intended JOSE algorithm for the key, optional. 152 */ 153 private Algorithm alg; 154 155 156 /** 157 * The key ID, optional. 158 */ 159 private String kid; 160 161 162 /** 163 * X.509 certificate URL, optional. 164 */ 165 private URI x5u; 166 167 168 /** 169 * X.509 certificate SHA-1 thumbprint, optional. 170 */ 171 @Deprecated 172 private Base64URL x5t; 173 174 175 /** 176 * X.509 certificate SHA-256 thumbprint, optional. 177 */ 178 private Base64URL x5t256; 179 180 181 /** 182 * The X.509 certificate chain, optional. 183 */ 184 private List<Base64> x5c; 185 186 187 /** 188 * Reference to the underlying key store, {@code null} if none. 189 */ 190 private KeyStore ks; 191 192 193 /** 194 * Creates a new Octet Key Pair JWK builder. 195 * 196 * @param crv The cryptographic curve. Must not be 197 * {@code null}. 198 * @param x The public 'x' parameter. Must not be 199 * {@code null}. 200 */ 201 public Builder(final Curve crv, final Base64URL x) { 202 203 if (crv == null) { 204 throw new IllegalArgumentException("The curve must not be null"); 205 } 206 207 this.crv = crv; 208 209 if (x == null) { 210 throw new IllegalArgumentException("The 'x' coordinate must not be null"); 211 } 212 213 this.x = x; 214 } 215 216 217 /** 218 * Creates a new Octet Key Pair JWK builder. 219 * 220 * @param okpJWK The Octet Key Pair to start with. Must not be 221 * {@code null}. 222 */ 223 public Builder(final OctetKeyPair okpJWK) { 224 225 crv = okpJWK.crv; 226 x = okpJWK.x; 227 d = okpJWK.d; 228 use = okpJWK.getKeyUse(); 229 ops = okpJWK.getKeyOperations(); 230 alg = okpJWK.getAlgorithm(); 231 kid = okpJWK.getKeyID(); 232 x5u = okpJWK.getX509CertURL(); 233 x5t = okpJWK.getX509CertThumbprint(); 234 x5t256 = okpJWK.getX509CertSHA256Thumbprint(); 235 x5c = okpJWK.getX509CertChain(); 236 ks = okpJWK.getKeyStore(); 237 } 238 239 240 /** 241 * Sets the private 'd' parameter. 242 * 243 * @param d The private 'd' parameter, {@code null} if not 244 * specified (for a public key). 245 * 246 * @return This builder. 247 */ 248 public OctetKeyPair.Builder d(final Base64URL d) { 249 250 this.d = d; 251 return this; 252 } 253 254 255 /** 256 * Sets the use ({@code use}) of the JWK. 257 * 258 * @param use The key use, {@code null} if not specified or if 259 * the key is intended for signing as well as 260 * encryption. 261 * 262 * @return This builder. 263 */ 264 public OctetKeyPair.Builder keyUse(final KeyUse use) { 265 266 this.use = use; 267 return this; 268 } 269 270 271 /** 272 * Sets the operations ({@code key_ops}) of the JWK. 273 * 274 * @param ops The key operations, {@code null} if not 275 * specified. 276 * 277 * @return This builder. 278 */ 279 public OctetKeyPair.Builder keyOperations(final Set<KeyOperation> ops) { 280 281 this.ops = ops; 282 return this; 283 } 284 285 286 /** 287 * Sets the intended JOSE algorithm ({@code alg}) for the JWK. 288 * 289 * @param alg The intended JOSE algorithm, {@code null} if not 290 * specified. 291 * 292 * @return This builder. 293 */ 294 public OctetKeyPair.Builder algorithm(final Algorithm alg) { 295 296 this.alg = alg; 297 return this; 298 } 299 300 /** 301 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 302 * to match a specific key. This can be used, for instance, to 303 * choose a key within a {@link JWKSet} during key rollover. 304 * The key ID may also correspond to a JWS/JWE {@code kid} 305 * header parameter value. 306 * 307 * @param kid The key ID, {@code null} if not specified. 308 * 309 * @return This builder. 310 */ 311 public OctetKeyPair.Builder keyID(final String kid) { 312 313 this.kid = kid; 314 return this; 315 } 316 317 318 /** 319 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK 320 * thumbprint (RFC 7638). The key ID can be used to match a 321 * specific key. This can be used, for instance, to choose a 322 * key within a {@link JWKSet} during key rollover. The key ID 323 * may also correspond to a JWS/JWE {@code kid} header 324 * parameter value. 325 * 326 * @return This builder. 327 * 328 * @throws JOSEException If the SHA-256 hash algorithm is not 329 * supported. 330 */ 331 public OctetKeyPair.Builder keyIDFromThumbprint() 332 throws JOSEException { 333 334 return keyIDFromThumbprint("SHA-256"); 335 } 336 337 338 /** 339 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint 340 * (RFC 7638). The key ID can be used to match a specific key. 341 * This can be used, for instance, to choose a key within a 342 * {@link JWKSet} during key rollover. The key ID may also 343 * correspond to a JWS/JWE {@code kid} header parameter value. 344 * 345 * @param hashAlg The hash algorithm for the JWK thumbprint 346 * computation. Must not be {@code null}. 347 * 348 * @return This builder. 349 * 350 * @throws JOSEException If the hash algorithm is not 351 * supported. 352 */ 353 public OctetKeyPair.Builder keyIDFromThumbprint(final String hashAlg) 354 throws JOSEException { 355 356 // Put mandatory params in sorted order 357 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 358 requiredParams.put(JWKParameterNames.OKP_SUBTYPE, crv.toString()); 359 requiredParams.put(JWKParameterNames.KEY_TYPE, KeyType.OKP.getValue()); 360 requiredParams.put(JWKParameterNames.OKP_PUBLIC_KEY, x.toString()); 361 this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString(); 362 return this; 363 } 364 365 366 /** 367 * Sets the X.509 certificate URL ({@code x5u}) of the JWK. 368 * 369 * @param x5u The X.509 certificate URL, {@code null} if not 370 * specified. 371 * 372 * @return This builder. 373 */ 374 public OctetKeyPair.Builder x509CertURL(final URI x5u) { 375 376 this.x5u = x5u; 377 return this; 378 } 379 380 381 /** 382 * Sets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of 383 * the JWK. 384 * 385 * @param x5t The X.509 certificate SHA-1 thumbprint, 386 * {@code null} if not specified. 387 * 388 * @return This builder. 389 */ 390 @Deprecated 391 public OctetKeyPair.Builder x509CertThumbprint(final Base64URL x5t) { 392 393 this.x5t = x5t; 394 return this; 395 } 396 397 398 /** 399 * Sets the X.509 certificate SHA-256 thumbprint 400 * ({@code x5t#S256}) of the JWK. 401 * 402 * @param x5t256 The X.509 certificate SHA-256 thumbprint, 403 * {@code null} if not specified. 404 * 405 * @return This builder. 406 */ 407 public OctetKeyPair.Builder x509CertSHA256Thumbprint(final Base64URL x5t256) { 408 409 this.x5t256 = x5t256; 410 return this; 411 } 412 413 414 /** 415 * Sets the X.509 certificate chain ({@code x5c}) of the JWK. 416 * 417 * @param x5c The X.509 certificate chain as a unmodifiable 418 * list, {@code null} if not specified. 419 * 420 * @return This builder. 421 */ 422 public OctetKeyPair.Builder x509CertChain(final List<Base64> x5c) { 423 424 this.x5c = x5c; 425 return this; 426 } 427 428 429 /** 430 * Sets the underlying key store. 431 * 432 * @param keyStore Reference to the underlying key store, 433 * {@code null} if none. 434 * 435 * @return This builder. 436 */ 437 public OctetKeyPair.Builder keyStore(final KeyStore keyStore) { 438 439 this.ks = keyStore; 440 return this; 441 } 442 443 444 /** 445 * Builds a new Octet Key Pair JWK. 446 * 447 * @return The Octet Key Pair JWK. 448 * 449 * @throws IllegalStateException If the JWK parameters were 450 * inconsistently specified. 451 */ 452 public OctetKeyPair build() { 453 454 try { 455 if (d == null) { 456 // Public key 457 return new OctetKeyPair(crv, x, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 458 } 459 460 // Public / private key pair with 'd' 461 return new OctetKeyPair(crv, x, d, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 462 463 } catch (IllegalArgumentException e) { 464 throw new IllegalStateException(e.getMessage(), e); 465 } 466 } 467 } 468 469 470 /** 471 * The curve name. 472 */ 473 private final Curve crv; 474 475 476 /** 477 * The public 'x' parameter. 478 */ 479 private final Base64URL x; 480 481 482 /** 483 * The public 'x' parameter, decoded from Base64. 484 * Cached for performance and to reduce the risk of side channel attacks 485 * against the Base64 decoding procedure. 486 */ 487 private final byte[] decodedX; 488 489 490 /** 491 * The private 'd' parameter. 492 */ 493 private final Base64URL d; 494 495 496 /** 497 * The private 'd' parameter, decoded from Base64. 498 * Cached for performance and to reduce the risk of side channel attacks 499 * against the Base64 decoding procedure. 500 */ 501 private final byte[] decodedD; 502 503 504 /** 505 * Creates a new public Octet Key Pair JSON Web Key (JWK) with the 506 * specified parameters. 507 * 508 * @param crv The cryptographic curve. Must not be {@code null}. 509 * @param x The public 'x' parameter. Must not be {@code null}. 510 * @param use The key use, {@code null} if not specified or if the 511 * key is intended for signing as well as encryption. 512 * @param ops The key operations, {@code null} if not specified. 513 * @param alg The intended JOSE algorithm for the key, {@code null} 514 * if not specified. 515 * @param kid The key ID, {@code null} if not specified. 516 * @param x5u The X.509 certificate URL, {@code null} if not 517 * specified. 518 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 519 * if not specified. 520 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 521 * if not specified. 522 * @param x5c The X.509 certificate chain, {@code null} if not 523 * specified. 524 * @param ks Reference to the underlying key store, {@code null} if 525 * not specified. 526 */ 527 public OctetKeyPair(final Curve crv, final Base64URL x, 528 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 529 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 530 final KeyStore ks) { 531 532 super(KeyType.OKP, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 533 534 if (crv == null) { 535 throw new IllegalArgumentException("The curve must not be null"); 536 } 537 538 if (! SUPPORTED_CURVES.contains(crv)) { 539 throw new IllegalArgumentException("Unknown / unsupported curve: " + crv); 540 } 541 542 this.crv = crv; 543 544 if (x == null) { 545 throw new IllegalArgumentException("The '" + JWKParameterNames.OKP_PUBLIC_KEY + "' parameter must not be null"); 546 } 547 548 this.x = x; 549 decodedX = x.decode(); 550 551 d = null; 552 decodedD = null; 553 } 554 555 556 /** 557 * Creates a new public / private Octet Key Pair JSON Web Key (JWK) 558 * with the specified parameters. 559 * 560 * @param crv The cryptographic curve. Must not be {@code null}. 561 * @param x The public 'x' parameter. Must not be {@code null}. 562 * @param d The private 'd' parameter. Must not be {@code null}. 563 * @param use The key use, {@code null} if not specified or if the 564 * key is intended for signing as well as encryption. 565 * @param ops The key operations, {@code null} if not specified. 566 * @param alg The intended JOSE algorithm for the key, {@code null} 567 * if not specified. 568 * @param kid The key ID, {@code null} if not specified. 569 * @param x5u The X.509 certificate URL, {@code null} if not 570 * specified. 571 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 572 * if not specified. 573 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 574 * if not specified. 575 * @param x5c The X.509 certificate chain, {@code null} if not 576 * specified. 577 * @param ks Reference to the underlying key store, {@code null} if 578 * not specified. 579 */ 580 public OctetKeyPair(final Curve crv, final Base64URL x, final Base64URL d, 581 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 582 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 583 final KeyStore ks) { 584 585 super(KeyType.OKP, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks); 586 587 if (crv == null) { 588 throw new IllegalArgumentException("The curve must not be null"); 589 } 590 591 if (! SUPPORTED_CURVES.contains(crv)) { 592 throw new IllegalArgumentException("Unknown / unsupported curve: " + crv); 593 } 594 595 this.crv = crv; 596 597 if (x == null) { 598 throw new IllegalArgumentException("The '" + JWKParameterNames.OKP_PUBLIC_KEY + "' parameter must not be null"); 599 } 600 601 this.x = x; 602 decodedX = x.decode(); 603 604 if (d == null) { 605 throw new IllegalArgumentException("The '" + JWKParameterNames.OKP_PRIVATE_KEY + "' parameter must not be null"); 606 } 607 608 this.d = d; 609 decodedD = d.decode(); 610 } 611 612 613 @Override 614 public Curve getCurve() { 615 616 return crv; 617 } 618 619 620 /** 621 * Gets the public 'x' parameter. 622 * 623 * @return The public 'x' parameter. 624 */ 625 public Base64URL getX() { 626 627 return x; 628 } 629 630 631 /** 632 * Gets the public 'x' parameter, decoded from Base64. 633 * 634 * @return The public 'x' parameter in bytes. 635 */ 636 public byte[] getDecodedX() { 637 638 return decodedX.clone(); 639 } 640 641 642 /** 643 * Gets the private 'd' parameter. 644 * 645 * @return The private 'd' coordinate, {@code null} if not specified 646 * (for a public key). 647 */ 648 public Base64URL getD() { 649 650 return d; 651 } 652 653 654 /** 655 * Gets the private 'd' parameter, decoded from Base64. 656 * 657 * @return The private 'd' coordinate in bytes, {@code null} if not specified 658 * (for a public key). 659 */ 660 public byte[] getDecodedD() { 661 662 return decodedD == null ? null : decodedD.clone(); 663 } 664 665 666 @Override 667 public PublicKey toPublicKey() 668 throws JOSEException { 669 670 throw new JOSEException("Export to java.security.PublicKey not supported"); 671 } 672 673 674 @Override 675 public PrivateKey toPrivateKey() 676 throws JOSEException { 677 678 throw new JOSEException("Export to java.security.PrivateKey not supported"); 679 } 680 681 682 @Override 683 public KeyPair toKeyPair() 684 throws JOSEException { 685 686 throw new JOSEException("Export to java.security.KeyPair not supported"); 687 } 688 689 690 @Override 691 public boolean matches(final X509Certificate cert) { 692 // X.509 certs don't support OKP yet 693 return false; 694 } 695 696 697 @Override 698 public LinkedHashMap<String,?> getRequiredParams() { 699 700 // Put mandatory params in sorted order 701 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 702 requiredParams.put(JWKParameterNames.OKP_SUBTYPE, crv.toString()); 703 requiredParams.put(JWKParameterNames.KEY_TYPE, getKeyType().getValue()); 704 requiredParams.put(JWKParameterNames.OKP_PUBLIC_KEY, x.toString()); 705 return requiredParams; 706 } 707 708 709 @Override 710 public boolean isPrivate() { 711 712 return d != null; 713 } 714 715 716 /** 717 * Returns a copy of this Octet Key Pair JWK with any private values 718 * removed. 719 * 720 * @return The copied public Octet Key Pair JWK. 721 */ 722 @Override 723 public OctetKeyPair toPublicJWK() { 724 725 return new OctetKeyPair( 726 getCurve(), getX(), 727 getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(), 728 getX509CertURL(), getX509CertThumbprint(), getX509CertSHA256Thumbprint(), getX509CertChain(), 729 getKeyStore()); 730 } 731 732 733 @Override 734 public Map<String, Object> toJSONObject() { 735 736 Map<String, Object> o = super.toJSONObject(); 737 738 // Append OKP specific attributes 739 o.put(JWKParameterNames.OKP_SUBTYPE, crv.toString()); 740 o.put(JWKParameterNames.OKP_PUBLIC_KEY, x.toString()); 741 742 if (d != null) { 743 o.put(JWKParameterNames.OKP_PRIVATE_KEY, d.toString()); 744 } 745 746 return o; 747 } 748 749 750 @Override 751 public int size() { 752 753 return ByteUtils.bitLength(x.decode()); 754 } 755 756 757 /** 758 * Parses a public / private Octet Key Pair JWK from the specified JSON 759 * object string representation. 760 * 761 * @param s The JSON object string to parse. Must not be {@code null}. 762 * 763 * @return The public / private Octet Key Pair JWK. 764 * 765 * @throws ParseException If the string couldn't be parsed to an Octet 766 * Key Pair JWK. 767 */ 768 public static OctetKeyPair parse(final String s) 769 throws ParseException { 770 771 return parse(JSONObjectUtils.parse(s)); 772 } 773 774 775 /** 776 * Parses a public / private Octet Key Pair JWK from the specified JSON 777 * object representation. 778 * 779 * @param jsonObject The JSON object to parse. Must not be 780 * {@code null}. 781 * 782 * @return The public / private Octet Key Pair JWK. 783 * 784 * @throws ParseException If the JSON object couldn't be parsed to an 785 * Octet Key Pair JWK. 786 */ 787 public static OctetKeyPair parse(final Map<String, Object> jsonObject) 788 throws ParseException { 789 790 // Check the key type 791 if (! KeyType.OKP.equals(JWKMetadata.parseKeyType(jsonObject))) { 792 throw new ParseException("The key type \"" + JWKParameterNames.KEY_TYPE + "\" must be " + KeyType.OKP.getValue(), 0); 793 } 794 795 // Parse the mandatory parameters 796 Curve crv; 797 try { 798 crv = Curve.parse(JSONObjectUtils.getString(jsonObject, JWKParameterNames.OKP_SUBTYPE)); 799 } catch (IllegalArgumentException e) { 800 throw new ParseException(e.getMessage(), 0); 801 } 802 803 Base64URL x = JSONObjectUtils.getBase64URL(jsonObject, JWKParameterNames.OKP_PUBLIC_KEY); 804 805 // Get the optional private key 806 Base64URL d = JSONObjectUtils.getBase64URL(jsonObject, JWKParameterNames.OKP_PRIVATE_KEY); 807 808 try { 809 if (d == null) { 810 // Public key 811 return new OctetKeyPair(crv, x, 812 JWKMetadata.parseKeyUse(jsonObject), 813 JWKMetadata.parseKeyOperations(jsonObject), 814 JWKMetadata.parseAlgorithm(jsonObject), 815 JWKMetadata.parseKeyID(jsonObject), 816 JWKMetadata.parseX509CertURL(jsonObject), 817 JWKMetadata.parseX509CertThumbprint(jsonObject), 818 JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject), 819 JWKMetadata.parseX509CertChain(jsonObject), 820 null); 821 822 } else { 823 // Key pair 824 return new OctetKeyPair(crv, x, d, 825 JWKMetadata.parseKeyUse(jsonObject), 826 JWKMetadata.parseKeyOperations(jsonObject), 827 JWKMetadata.parseAlgorithm(jsonObject), 828 JWKMetadata.parseKeyID(jsonObject), 829 JWKMetadata.parseX509CertURL(jsonObject), 830 JWKMetadata.parseX509CertThumbprint(jsonObject), 831 JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject), 832 JWKMetadata.parseX509CertChain(jsonObject), 833 null); 834 } 835 836 } catch (IllegalArgumentException ex) { 837 838 // Conflicting 'use' and 'key_ops' 839 throw new ParseException(ex.getMessage(), 0); 840 } 841 } 842 843 844 @Override 845 public boolean equals(Object o) { 846 if (this == o) return true; 847 if (!(o instanceof OctetKeyPair)) return false; 848 if (!super.equals(o)) return false; 849 OctetKeyPair that = (OctetKeyPair) o; 850 return Objects.equals(crv, that.crv) && 851 Objects.equals(x, that.x) && 852 Arrays.equals(decodedX, that.decodedX) && 853 Objects.equals(d, that.d) && 854 Arrays.equals(decodedD, that.decodedD); 855 } 856 857 858 @Override 859 public int hashCode() { 860 int result = Objects.hash(super.hashCode(), crv, x, d); 861 result = 31 * result + Arrays.hashCode(decodedX); 862 result = 31 * result + Arrays.hashCode(decodedD); 863 return result; 864 } 865}