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