001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2024, 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 2024-04-27 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 * The key expiration time, optional. 189 */ 190 private Date exp; 191 192 193 /** 194 * The key not-before time, optional. 195 */ 196 private Date nbf; 197 198 199 /** 200 * The key issued-at time, optional. 201 */ 202 private Date iat; 203 204 205 /** 206 * The key revocation, optional. 207 */ 208 private KeyRevocation revocation; 209 210 211 /** 212 * Reference to the underlying key store, {@code null} if none. 213 */ 214 private KeyStore ks; 215 216 217 /** 218 * Creates a new Octet Key Pair JWK builder. 219 * 220 * @param crv The cryptographic curve. Must not be 221 * {@code null}. 222 * @param x The public 'x' parameter. Must not be 223 * {@code null}. 224 */ 225 public Builder(final Curve crv, final Base64URL x) { 226 227 this.crv = Objects.requireNonNull(crv, "The curve must not be null"); 228 this.x = Objects.requireNonNull(x, "The x coordinate must not be null"); 229 } 230 231 232 /** 233 * Creates a new Octet Key Pair JWK builder. 234 * 235 * @param okpJWK The Octet Key Pair to start with. Must not be 236 * {@code null}. 237 */ 238 public Builder(final OctetKeyPair okpJWK) { 239 240 crv = okpJWK.crv; 241 x = okpJWK.x; 242 d = okpJWK.d; 243 use = okpJWK.getKeyUse(); 244 ops = okpJWK.getKeyOperations(); 245 alg = okpJWK.getAlgorithm(); 246 kid = okpJWK.getKeyID(); 247 x5u = okpJWK.getX509CertURL(); 248 x5t = okpJWK.getX509CertThumbprint(); 249 x5t256 = okpJWK.getX509CertSHA256Thumbprint(); 250 x5c = okpJWK.getX509CertChain(); 251 exp = okpJWK.getExpirationTime(); 252 nbf = okpJWK.getNotBeforeTime(); 253 iat = okpJWK.getIssueTime(); 254 revocation = okpJWK.getKeyRevocation(); 255 ks = okpJWK.getKeyStore(); 256 } 257 258 259 /** 260 * Sets the private 'd' parameter. 261 * 262 * @param d The private 'd' parameter, {@code null} if not 263 * specified (for a public key). 264 * 265 * @return This builder. 266 */ 267 public Builder d(final Base64URL d) { 268 269 this.d = d; 270 return this; 271 } 272 273 274 /** 275 * Sets the use ({@code use}) of the JWK. 276 * 277 * @param use The key use, {@code null} if not specified or if 278 * the key is intended for signing as well as 279 * encryption. 280 * 281 * @return This builder. 282 */ 283 public Builder keyUse(final KeyUse use) { 284 285 this.use = use; 286 return this; 287 } 288 289 290 /** 291 * Sets the operations ({@code key_ops}) of the JWK. 292 * 293 * @param ops The key operations, {@code null} if not 294 * specified. 295 * 296 * @return This builder. 297 */ 298 public Builder keyOperations(final Set<KeyOperation> ops) { 299 300 this.ops = ops; 301 return this; 302 } 303 304 305 /** 306 * Sets the intended JOSE algorithm ({@code alg}) for the JWK. 307 * 308 * @param alg The intended JOSE algorithm, {@code null} if not 309 * specified. 310 * 311 * @return This builder. 312 */ 313 public Builder algorithm(final Algorithm alg) { 314 315 this.alg = alg; 316 return this; 317 } 318 319 /** 320 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 321 * to match a specific key. This can be used, for instance, to 322 * choose a key within a {@link JWKSet} during key rollover. 323 * The key ID may also correspond to a JWS/JWE {@code kid} 324 * header parameter value. 325 * 326 * @param kid The key ID, {@code null} if not specified. 327 * 328 * @return This builder. 329 */ 330 public Builder keyID(final String kid) { 331 332 this.kid = kid; 333 return this; 334 } 335 336 337 /** 338 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK 339 * thumbprint (RFC 7638). The key ID can be used to match a 340 * specific key. This can be used, for instance, to choose a 341 * key within a {@link JWKSet} during key rollover. The key ID 342 * may also correspond to a JWS/JWE {@code kid} header 343 * parameter value. 344 * 345 * @return This builder. 346 * 347 * @throws JOSEException If the SHA-256 hash algorithm is not 348 * supported. 349 */ 350 public Builder keyIDFromThumbprint() 351 throws JOSEException { 352 353 return keyIDFromThumbprint("SHA-256"); 354 } 355 356 357 /** 358 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint 359 * (RFC 7638). The key ID can be used to match a specific key. 360 * This can be used, for instance, to choose a key within a 361 * {@link JWKSet} during key rollover. The key ID may also 362 * correspond to a JWS/JWE {@code kid} header parameter value. 363 * 364 * @param hashAlg The hash algorithm for the JWK thumbprint 365 * computation. Must not be {@code null}. 366 * 367 * @return This builder. 368 * 369 * @throws JOSEException If the hash algorithm is not 370 * supported. 371 */ 372 public Builder keyIDFromThumbprint(final String hashAlg) 373 throws JOSEException { 374 375 // Put mandatory params in sorted order 376 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 377 requiredParams.put(JWKParameterNames.OKP_SUBTYPE, crv.toString()); 378 requiredParams.put(JWKParameterNames.KEY_TYPE, KeyType.OKP.getValue()); 379 requiredParams.put(JWKParameterNames.OKP_PUBLIC_KEY, x.toString()); 380 this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString(); 381 return this; 382 } 383 384 385 /** 386 * Sets the X.509 certificate URL ({@code x5u}) of the JWK. 387 * 388 * @param x5u The X.509 certificate URL, {@code null} if not 389 * specified. 390 * 391 * @return This builder. 392 */ 393 public Builder x509CertURL(final URI x5u) { 394 395 this.x5u = x5u; 396 return this; 397 } 398 399 400 /** 401 * Sets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of 402 * the JWK. 403 * 404 * @param x5t The X.509 certificate SHA-1 thumbprint, 405 * {@code null} if not specified. 406 * 407 * @return This builder. 408 */ 409 @Deprecated 410 public Builder x509CertThumbprint(final Base64URL x5t) { 411 412 this.x5t = x5t; 413 return this; 414 } 415 416 417 /** 418 * Sets the X.509 certificate SHA-256 thumbprint 419 * ({@code x5t#S256}) of the JWK. 420 * 421 * @param x5t256 The X.509 certificate SHA-256 thumbprint, 422 * {@code null} if not specified. 423 * 424 * @return This builder. 425 */ 426 public Builder x509CertSHA256Thumbprint(final Base64URL x5t256) { 427 428 this.x5t256 = x5t256; 429 return this; 430 } 431 432 433 /** 434 * Sets the X.509 certificate chain ({@code x5c}) of the JWK. 435 * 436 * @param x5c The X.509 certificate chain as a unmodifiable 437 * list, {@code null} if not specified. 438 * 439 * @return This builder. 440 */ 441 public Builder x509CertChain(final List<Base64> x5c) { 442 443 this.x5c = x5c; 444 return this; 445 } 446 447 448 /** 449 * Sets the expiration time ({@code exp}) of the JWK. 450 * 451 * @param exp The expiration time, {@code null} if not 452 * specified. 453 * 454 * @return This builder. 455 */ 456 public Builder expirationTime(final Date exp) { 457 458 this.exp = exp; 459 return this; 460 } 461 462 463 /** 464 * Sets the not-before time ({@code nbf}) of the JWK. 465 * 466 * @param nbf The not-before time, {@code null} if not 467 * specified. 468 * 469 * @return This builder. 470 */ 471 public Builder notBeforeTime(final Date nbf) { 472 473 this.nbf = nbf; 474 return this; 475 } 476 477 478 /** 479 * Sets the issued-at time ({@code iat}) of the JWK. 480 * 481 * @param iat The issued-at time, {@code null} if not 482 * specified. 483 * 484 * @return This builder. 485 */ 486 public Builder issueTime(final Date iat) { 487 488 this.iat = iat; 489 return this; 490 } 491 492 493 /** 494 * Sets the revocation ({@code revoked}) of the JWK. 495 * 496 * @param revocation The key revocation, {@code null} if not 497 * specified. 498 * 499 * @return This builder. 500 */ 501 public Builder keyRevocation(final KeyRevocation revocation) { 502 503 this.revocation = revocation; 504 return this; 505 } 506 507 508 /** 509 * Sets the underlying key store. 510 * 511 * @param keyStore Reference to the underlying key store, 512 * {@code null} if none. 513 * 514 * @return This builder. 515 */ 516 public Builder keyStore(final KeyStore keyStore) { 517 518 this.ks = keyStore; 519 return this; 520 } 521 522 523 /** 524 * Builds a new Octet Key Pair JWK. 525 * 526 * @return The Octet Key Pair JWK. 527 * 528 * @throws IllegalStateException If the JWK parameters were 529 * inconsistently specified. 530 */ 531 public OctetKeyPair build() { 532 533 try { 534 if (d == null) { 535 // Public key 536 return new OctetKeyPair(crv, x, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, revocation, ks); 537 } 538 539 // Public / private key pair with 'd' 540 return new OctetKeyPair(crv, x, d, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, revocation, ks); 541 542 } catch (IllegalArgumentException e) { 543 throw new IllegalStateException(e.getMessage(), e); 544 } 545 } 546 } 547 548 549 /** 550 * The curve name. 551 */ 552 private final Curve crv; 553 554 555 /** 556 * The public 'x' parameter. 557 */ 558 private final Base64URL x; 559 560 561 /** 562 * The public 'x' parameter, decoded from Base64. 563 * Cached for performance and to reduce the risk of side channel attacks 564 * against the Base64 decoding procedure. 565 */ 566 private final byte[] decodedX; 567 568 569 /** 570 * The private 'd' parameter. 571 */ 572 private final Base64URL d; 573 574 575 /** 576 * The private 'd' parameter, decoded from Base64. 577 * Cached for performance and to reduce the risk of side channel attacks 578 * against the Base64 decoding procedure. 579 */ 580 private final byte[] decodedD; 581 582 583 /** 584 * Creates a new public Octet Key Pair JSON Web Key (JWK) with the 585 * specified parameters. 586 * 587 * @param crv The cryptographic curve. Must not be {@code null}. 588 * @param x The public 'x' parameter. Must not be {@code null}. 589 * @param use The key use, {@code null} if not specified or if the 590 * key is intended for signing as well as encryption. 591 * @param ops The key operations, {@code null} if not specified. 592 * @param alg The intended JOSE algorithm for the key, {@code null} 593 * if not specified. 594 * @param kid The key ID, {@code null} if not specified. 595 * @param x5u The X.509 certificate URL, {@code null} if not 596 * specified. 597 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 598 * if not specified. 599 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 600 * if not specified. 601 * @param x5c The X.509 certificate chain, {@code null} if not 602 * specified. 603 * @param ks Reference to the underlying key store, {@code null} if 604 * not specified. 605 */ 606 @Deprecated 607 public OctetKeyPair(final Curve crv, final Base64URL x, 608 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 609 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 610 final KeyStore ks) { 611 612 this(crv, x, use, ops, alg, kid, x5u, x5t, x5t256, x5c, null, null, null, ks); 613 } 614 615 616 /** 617 * Creates a new public / private Octet Key Pair JSON Web Key (JWK) 618 * with the specified parameters. 619 * 620 * @param crv The cryptographic curve. Must not be {@code null}. 621 * @param x The public 'x' parameter. Must not be {@code null}. 622 * @param d The private 'd' parameter. Must not be {@code null}. 623 * @param use The key use, {@code null} if not specified or if the 624 * key is intended for signing as well as encryption. 625 * @param ops The key operations, {@code null} if not specified. 626 * @param alg The intended JOSE algorithm for the key, {@code null} 627 * if not specified. 628 * @param kid The key ID, {@code null} if not specified. 629 * @param x5u The X.509 certificate URL, {@code null} if not 630 * specified. 631 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 632 * if not specified. 633 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 634 * if not specified. 635 * @param x5c The X.509 certificate chain, {@code null} if not 636 * specified. 637 * @param ks Reference to the underlying key store, {@code null} if 638 * not specified. 639 */ 640 @Deprecated 641 public OctetKeyPair(final Curve crv, final Base64URL x, final Base64URL d, 642 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 643 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 644 final KeyStore ks) { 645 646 this(crv, x, d, use, ops, alg, kid, x5u, x5t, x5t256, x5c, null, null, null, ks); 647 } 648 649 650 /** 651 * Creates a new public Octet Key Pair JSON Web Key (JWK) with the 652 * specified parameters. 653 * 654 * @param crv The cryptographic curve. Must not be {@code null}. 655 * @param x The public 'x' parameter. Must not be {@code null}. 656 * @param use The key use, {@code null} if not specified or if the 657 * key is intended for signing as well as encryption. 658 * @param ops The key operations, {@code null} if not specified. 659 * @param alg The intended JOSE algorithm for the key, {@code null} 660 * if not specified. 661 * @param kid The key ID, {@code null} if not specified. 662 * @param x5u The X.509 certificate URL, {@code null} if not 663 * specified. 664 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 665 * if not specified. 666 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 667 * if not specified. 668 * @param x5c The X.509 certificate chain, {@code null} if not 669 * specified. 670 * @param exp The key expiration time, {@code null} if not 671 * specified. 672 * @param nbf The key not-before time, {@code null} if not 673 * specified. 674 * @param iat The key issued-at time, {@code null} if not specified. 675 * @param ks Reference to the underlying key store, {@code null} if 676 * not specified. 677 */ 678 @Deprecated 679 public OctetKeyPair(final Curve crv, final Base64URL x, 680 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 681 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 682 final Date exp, final Date nbf, final Date iat, 683 final KeyStore ks) { 684 685 this(crv, x, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, null, ks); 686 } 687 688 689 // JWK public 690 /** 691 * Creates a new public Octet Key Pair JSON Web Key (JWK) with the 692 * specified parameters. 693 * 694 * @param crv The cryptographic curve. Must not be {@code null}. 695 * @param x The public 'x' parameter. Must not be 696 * {@code null}. 697 * @param use The key use, {@code null} if not specified or if 698 * the key is intended for signing as well as 699 * encryption. 700 * @param ops The key operations, {@code null} if not specified. 701 * @param alg The intended JOSE algorithm for the key, 702 * {@code null} if not specified. 703 * @param kid The key ID, {@code null} if not specified. 704 * @param x5u The X.509 certificate URL, {@code null} if not 705 * specified. 706 * @param x5t The X.509 certificate SHA-1 thumbprint, 707 * {@code null} if not specified. 708 * @param x5t256 The X.509 certificate SHA-256 thumbprint, 709 * {@code null} if not specified. 710 * @param x5c The X.509 certificate chain, {@code null} if not 711 * specified. 712 * @param exp The key expiration time, {@code null} if not 713 * specified. 714 * @param nbf The key not-before time, {@code null} if not 715 * specified. 716 * @param iat The key issued-at time, {@code null} if not 717 * specified. 718 * @param revocation The key revocation, {@code null} if not specified. 719 * @param ks Reference to the underlying key store, 720 * {@code null} if not specified. 721 */ 722 public OctetKeyPair(final Curve crv, final Base64URL x, 723 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 724 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 725 final Date exp, final Date nbf, final Date iat, final KeyRevocation revocation, 726 final KeyStore ks) { 727 728 super(KeyType.OKP, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, revocation, ks); 729 730 Objects.requireNonNull(crv, "The curve must not be null"); 731 if (! SUPPORTED_CURVES.contains(crv)) { 732 throw new IllegalArgumentException("Unknown / unsupported curve: " + crv); 733 } 734 735 this.crv = crv; 736 737 this.x = Objects.requireNonNull(x, "The " + JWKParameterNames.OKP_PUBLIC_KEY + " parameter must not be null"); 738 decodedX = x.decode(); 739 740 d = null; 741 decodedD = null; 742 } 743 744 745 /** 746 * Creates a new public / private Octet Key Pair JSON Web Key (JWK) 747 * with the specified parameters. 748 * 749 * @param crv The cryptographic curve. Must not be {@code null}. 750 * @param x The public 'x' parameter. Must not be {@code null}. 751 * @param d The private 'd' parameter. Must not be {@code null}. 752 * @param use The key use, {@code null} if not specified or if the 753 * key is intended for signing as well as encryption. 754 * @param ops The key operations, {@code null} if not specified. 755 * @param alg The intended JOSE algorithm for the key, {@code null} 756 * if not specified. 757 * @param kid The key ID, {@code null} if not specified. 758 * @param x5u The X.509 certificate URL, {@code null} if not 759 * specified. 760 * @param x5t The X.509 certificate SHA-1 thumbprint, {@code null} 761 * if not specified. 762 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 763 * if not specified. 764 * @param x5c The X.509 certificate chain, {@code null} if not 765 * specified. 766 * @param exp The key expiration time, {@code null} if not 767 * specified. 768 * @param nbf The key not-before time, {@code null} if not 769 * specified. 770 * @param iat The key issued-at time, {@code null} if not specified. 771 * @param ks Reference to the underlying key store, {@code null} if 772 * not specified. 773 */ 774 @Deprecated 775 public OctetKeyPair(final Curve crv, final Base64URL x, final Base64URL d, 776 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 777 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 778 final Date exp, final Date nbf, final Date iat, 779 final KeyStore ks) { 780 781 this(crv, x, d, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, null, ks); 782 } 783 784 785 // JWK public + private 786 /** 787 * Creates a new public / private Octet Key Pair JSON Web Key (JWK) 788 * with the specified parameters. 789 * 790 * @param crv The cryptographic curve. Must not be {@code null}. 791 * @param x The public 'x' parameter. Must not be 792 * {@code null}. 793 * @param d The private 'd' parameter. Must not be 794 * {@code null}. 795 * @param use The key use, {@code null} if not specified or if 796 * the key is intended for signing as well as 797 * encryption. 798 * @param ops The key operations, {@code null} if not specified. 799 * @param alg The intended JOSE algorithm for the key, 800 * {@code null} if not specified. 801 * @param kid The key ID, {@code null} if not specified. 802 * @param x5u The X.509 certificate URL, {@code null} if not 803 * specified. 804 * @param x5t The X.509 certificate SHA-1 thumbprint, 805 * {@code null} if not specified. 806 * @param x5t256 The X.509 certificate SHA-256 thumbprint, 807 * {@code null} if not specified. 808 * @param x5c The X.509 certificate chain, {@code null} if not 809 * specified. 810 * @param exp The key expiration time, {@code null} if not 811 * specified. 812 * @param nbf The key not-before time, {@code null} if not 813 * specified. 814 * @param iat The key issued-at time, {@code null} if not 815 * specified. 816 * @param revocation The key revocation, {@code null} if not specified. 817 * @param ks Reference to the underlying key store, 818 * {@code null} if not specified. 819 */ 820 public OctetKeyPair(final Curve crv, final Base64URL x, final Base64URL d, 821 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 822 final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c, 823 final Date exp, final Date nbf, final Date iat, final KeyRevocation revocation, 824 final KeyStore ks) { 825 826 super(KeyType.OKP, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, revocation, ks); 827 828 Objects.requireNonNull(crv, "The curve must not be null"); 829 if (! SUPPORTED_CURVES.contains(crv)) { 830 throw new IllegalArgumentException("Unknown / unsupported curve: " + crv); 831 } 832 this.crv = crv; 833 834 this.x = Objects.requireNonNull(x, "The " + JWKParameterNames.OKP_PUBLIC_KEY + " parameter must not be null"); 835 decodedX = x.decode(); 836 837 this.d = Objects.requireNonNull(d, "The " + JWKParameterNames.OKP_PRIVATE_KEY + " parameter must not be null"); 838 decodedD = d.decode(); 839 } 840 841 842 @Override 843 public Curve getCurve() { 844 845 return crv; 846 } 847 848 849 /** 850 * Gets the public 'x' parameter. 851 * 852 * @return The public 'x' parameter. 853 */ 854 public Base64URL getX() { 855 856 return x; 857 } 858 859 860 /** 861 * Gets the public 'x' parameter, decoded from Base64. 862 * 863 * @return The public 'x' parameter in bytes. 864 */ 865 public byte[] getDecodedX() { 866 867 return decodedX.clone(); 868 } 869 870 871 /** 872 * Gets the private 'd' parameter. 873 * 874 * @return The private 'd' coordinate, {@code null} if not specified 875 * (for a public key). 876 */ 877 public Base64URL getD() { 878 879 return d; 880 } 881 882 883 /** 884 * Gets the private 'd' parameter, decoded from Base64. 885 * 886 * @return The private 'd' coordinate in bytes, {@code null} if not specified 887 * (for a public key). 888 */ 889 public byte[] getDecodedD() { 890 891 return decodedD == null ? null : decodedD.clone(); 892 } 893 894 895 @Override 896 public PublicKey toPublicKey() 897 throws JOSEException { 898 899 throw new JOSEException("Export to java.security.PublicKey not supported"); 900 } 901 902 903 @Override 904 public PrivateKey toPrivateKey() 905 throws JOSEException { 906 907 throw new JOSEException("Export to java.security.PrivateKey not supported"); 908 } 909 910 911 @Override 912 public KeyPair toKeyPair() 913 throws JOSEException { 914 915 throw new JOSEException("Export to java.security.KeyPair not supported"); 916 } 917 918 919 @Override 920 public boolean matches(final X509Certificate cert) { 921 // X.509 certs don't support OKP yet 922 return false; 923 } 924 925 926 @Override 927 public LinkedHashMap<String,?> getRequiredParams() { 928 929 // Put mandatory params in sorted order 930 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 931 requiredParams.put(JWKParameterNames.OKP_SUBTYPE, crv.toString()); 932 requiredParams.put(JWKParameterNames.KEY_TYPE, getKeyType().getValue()); 933 requiredParams.put(JWKParameterNames.OKP_PUBLIC_KEY, x.toString()); 934 return requiredParams; 935 } 936 937 938 @Override 939 public boolean isPrivate() { 940 941 return d != null; 942 } 943 944 945 /** 946 * Returns a copy of this Octet Key Pair JWK with any private values 947 * removed. 948 * 949 * @return The copied public Octet Key Pair JWK. 950 */ 951 @Override 952 public OctetKeyPair toPublicJWK() { 953 954 return new OctetKeyPair( 955 getCurve(), getX(), 956 getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(), 957 getX509CertURL(), getX509CertThumbprint(), getX509CertSHA256Thumbprint(), getX509CertChain(), 958 getExpirationTime(), getNotBeforeTime(), getIssueTime(), getKeyRevocation(), 959 getKeyStore()); 960 } 961 962 963 @Override 964 public Map<String, Object> toJSONObject() { 965 966 Map<String, Object> o = super.toJSONObject(); 967 968 // Append OKP specific attributes 969 o.put(JWKParameterNames.OKP_SUBTYPE, crv.toString()); 970 o.put(JWKParameterNames.OKP_PUBLIC_KEY, x.toString()); 971 972 if (d != null) { 973 o.put(JWKParameterNames.OKP_PRIVATE_KEY, d.toString()); 974 } 975 976 return o; 977 } 978 979 980 @Override 981 public int size() { 982 983 return ByteUtils.bitLength(x.decode()); 984 } 985 986 987 /** 988 * Parses a public / private Octet Key Pair JWK from the specified JSON 989 * object string representation. 990 * 991 * @param s The JSON object string to parse. Must not be {@code null}. 992 * 993 * @return The public / private Octet Key Pair JWK. 994 * 995 * @throws ParseException If the string couldn't be parsed to an Octet 996 * Key Pair JWK. 997 */ 998 public static OctetKeyPair parse(final String s) 999 throws ParseException { 1000 1001 return parse(JSONObjectUtils.parse(s)); 1002 } 1003 1004 1005 /** 1006 * Parses a public / private Octet Key Pair JWK from the specified JSON 1007 * object representation. 1008 * 1009 * @param jsonObject The JSON object to parse. Must not be 1010 * {@code null}. 1011 * 1012 * @return The public / private Octet Key Pair JWK. 1013 * 1014 * @throws ParseException If the JSON object couldn't be parsed to an 1015 * Octet Key Pair JWK. 1016 */ 1017 public static OctetKeyPair parse(final Map<String, Object> jsonObject) 1018 throws ParseException { 1019 1020 // Check the key type 1021 if (! KeyType.OKP.equals(JWKMetadata.parseKeyType(jsonObject))) { 1022 throw new ParseException("The key type " + JWKParameterNames.KEY_TYPE + " must be " + KeyType.OKP.getValue(), 0); 1023 } 1024 1025 // Parse the mandatory parameters 1026 Curve crv; 1027 try { 1028 crv = Curve.parse(JSONObjectUtils.getString(jsonObject, JWKParameterNames.OKP_SUBTYPE)); 1029 } catch (IllegalArgumentException e) { 1030 throw new ParseException(e.getMessage(), 0); 1031 } 1032 1033 Base64URL x = JSONObjectUtils.getBase64URL(jsonObject, JWKParameterNames.OKP_PUBLIC_KEY); 1034 1035 // Get the optional private key 1036 Base64URL d = JSONObjectUtils.getBase64URL(jsonObject, JWKParameterNames.OKP_PRIVATE_KEY); 1037 1038 try { 1039 if (d == null) { 1040 // Public key 1041 return new OctetKeyPair(crv, x, 1042 JWKMetadata.parseKeyUse(jsonObject), 1043 JWKMetadata.parseKeyOperations(jsonObject), 1044 JWKMetadata.parseAlgorithm(jsonObject), 1045 JWKMetadata.parseKeyID(jsonObject), 1046 JWKMetadata.parseX509CertURL(jsonObject), 1047 JWKMetadata.parseX509CertThumbprint(jsonObject), 1048 JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject), 1049 JWKMetadata.parseX509CertChain(jsonObject), 1050 JWKMetadata.parseExpirationTime(jsonObject), 1051 JWKMetadata.parseNotBeforeTime(jsonObject), 1052 JWKMetadata.parseIssueTime(jsonObject), 1053 JWKMetadata.parseKeyRevocation(jsonObject), 1054 null); 1055 1056 } else { 1057 // Key pair 1058 return new OctetKeyPair(crv, x, d, 1059 JWKMetadata.parseKeyUse(jsonObject), 1060 JWKMetadata.parseKeyOperations(jsonObject), 1061 JWKMetadata.parseAlgorithm(jsonObject), 1062 JWKMetadata.parseKeyID(jsonObject), 1063 JWKMetadata.parseX509CertURL(jsonObject), 1064 JWKMetadata.parseX509CertThumbprint(jsonObject), 1065 JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject), 1066 JWKMetadata.parseX509CertChain(jsonObject), 1067 JWKMetadata.parseExpirationTime(jsonObject), 1068 JWKMetadata.parseNotBeforeTime(jsonObject), 1069 JWKMetadata.parseIssueTime(jsonObject), 1070 JWKMetadata.parseKeyRevocation(jsonObject), 1071 null); 1072 } 1073 1074 } catch (Exception ex) { 1075 1076 // Conflicting 'use' and 'key_ops' 1077 throw new ParseException(ex.getMessage(), 0); 1078 } 1079 } 1080 1081 1082 @Override 1083 public boolean equals(Object o) { 1084 if (this == o) return true; 1085 if (!(o instanceof OctetKeyPair)) return false; 1086 if (!super.equals(o)) return false; 1087 OctetKeyPair that = (OctetKeyPair) o; 1088 return Objects.equals(crv, that.crv) && 1089 Objects.equals(x, that.x) && 1090 Arrays.equals(decodedX, that.decodedX) && 1091 Objects.equals(d, that.d) && 1092 Arrays.equals(decodedD, that.decodedD); 1093 } 1094 1095 1096 @Override 1097 public int hashCode() { 1098 int result = Objects.hash(super.hashCode(), crv, x, d); 1099 result = 31 * result + Arrays.hashCode(decodedX); 1100 result = 31 * result + Arrays.hashCode(decodedD); 1101 return result; 1102 } 1103}