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.KeyStore; 024import java.security.KeyStoreException; 025import java.security.cert.X509Certificate; 026import java.security.interfaces.ECPublicKey; 027import java.security.interfaces.RSAPublicKey; 028import java.text.ParseException; 029import java.util.*; 030 031import com.nimbusds.jose.Algorithm; 032import com.nimbusds.jose.JOSEException; 033import com.nimbusds.jose.util.Base64; 034import com.nimbusds.jose.util.Base64URL; 035import com.nimbusds.jose.util.JSONObjectUtils; 036import net.minidev.json.JSONAware; 037import net.minidev.json.JSONObject; 038 039 040/** 041 * The base abstract class for JSON Web Keys (JWKs). It serialises to a JSON 042 * object. 043 * 044 * <p>The following JSON object members are common to all JWK types: 045 * 046 * <ul> 047 * <li>{@link #getKeyType kty} (required) 048 * <li>{@link #getKeyUse use} (optional) 049 * <li>{@link #getKeyOperations key_ops} (optional) 050 * <li>{@link #getKeyID kid} (optional) 051 * <li>{@link #getX509CertURL()} x5u} (optional) 052 * <li>{@link #getX509CertThumbprint()} x5t} (optional) 053 * <li>{@link #getX509CertSHA256Thumbprint()} x5t#S256} (optional) 054 * <li>{@link #getX509CertChain() x5c} (optional) 055 * <li>{@link #getKeyStore()} 056 * </ul> 057 * 058 * <p>Example JWK (of the Elliptic Curve type): 059 * 060 * <pre> 061 * { 062 * "kty" : "EC", 063 * "crv" : "P-256", 064 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 065 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 066 * "use" : "enc", 067 * "kid" : "1" 068 * } 069 * </pre> 070 * 071 * @author Vladimir Dzhuvinov 072 * @author Justin Richer 073 * @version 2017-06-20 074 */ 075public abstract class JWK implements JSONAware, 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 Base64URL x5t256; 135 136 137 /** 138 * The X.509 certificate chain, optional. 139 */ 140 private final List<Base64> x5c; 141 142 143 /** 144 * Reference to the underlying key store, {@code null} if none. 145 */ 146 private final KeyStore keyStore; 147 148 149 /** 150 * Creates a new JSON Web Key (JWK). 151 * 152 * @param kty The key type. Must not be {@code null}. 153 * @param use The key use, {@code null} if not specified or if the 154 * key is intended for signing as well as encryption. 155 * @param ops The key operations, {@code null} if not specified. 156 * @param alg The intended JOSE algorithm for the key, {@code null} 157 * if not specified. 158 * @param kid The key ID, {@code null} if not specified. 159 * @param x5u The X.509 certificate URL, {@code null} if not 160 * specified. 161 * @param x5t The X.509 certificate thumbprint, {@code null} if not 162 * specified. 163 * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} 164 * if not specified. 165 * @param x5c The X.509 certificate chain, {@code null} if not 166 * specified. 167 * @param ks Reference to the underlying key store, {@code null} if 168 * none. 169 */ 170 protected JWK(final KeyType kty, 171 final KeyUse use, 172 final Set<KeyOperation> ops, 173 final Algorithm alg, 174 final String kid, 175 final URI x5u, 176 final Base64URL x5t, 177 final Base64URL x5t256, 178 final List<Base64> x5c, 179 final KeyStore ks) { 180 181 if (kty == null) { 182 throw new IllegalArgumentException("The key type \"kty\" parameter must not be null"); 183 } 184 185 this.kty = kty; 186 187 if (! KeyUseAndOpsConsistency.areConsistent(use, ops)) { 188 throw new IllegalArgumentException("The key use \"use\" and key options \"key_opts\" parameters are not consistent, " + 189 "see RFC 7517, section 4.3"); 190 } 191 192 this.use = use; 193 this.ops = ops; 194 195 this.alg = alg; 196 this.kid = kid; 197 198 this.x5u = x5u; 199 this.x5t = x5t; 200 this.x5t256 = x5t256; 201 this.x5c = x5c; 202 203 this.keyStore = ks; 204 } 205 206 207 /** 208 * Gets the type ({@code kty}) of this JWK. 209 * 210 * @return The key type. 211 */ 212 public KeyType getKeyType() { 213 214 return kty; 215 } 216 217 218 /** 219 * Gets the use ({@code use}) of this JWK. 220 * 221 * @return The key use, {@code null} if not specified or if the key is 222 * intended for signing as well as encryption. 223 */ 224 public KeyUse getKeyUse() { 225 226 return use; 227 } 228 229 230 /** 231 * Gets the operations ({@code key_ops}) for this JWK. 232 * 233 * @return The key operations, {@code null} if not specified. 234 */ 235 public Set<KeyOperation> getKeyOperations() { 236 237 return ops; 238 } 239 240 241 /** 242 * Gets the intended JOSE algorithm ({@code alg}) for this JWK. 243 * 244 * @return The intended JOSE algorithm, {@code null} if not specified. 245 */ 246 public Algorithm getAlgorithm() { 247 248 return alg; 249 } 250 251 252 /** 253 * Gets the ID ({@code kid}) of this JWK. The key ID can be used to 254 * match a specific key. This can be used, for instance, to choose a 255 * key within a {@link JWKSet} during key rollover. The key ID may also 256 * correspond to a JWS/JWE {@code kid} header parameter value. 257 * 258 * @return The key ID, {@code null} if not specified. 259 */ 260 public String getKeyID() { 261 262 return kid; 263 } 264 265 266 /** 267 * Gets the X.509 certificate URL ({@code x5u}) of this JWK. 268 * 269 * @return The X.509 certificate URL, {@code null} if not specified. 270 */ 271 public URI getX509CertURL() { 272 273 return x5u; 274 } 275 276 277 /** 278 * Gets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of this 279 * JWK. 280 * 281 * @return The X.509 certificate SHA-1 thumbprint, {@code null} if not 282 * specified. 283 */ 284 @Deprecated 285 public Base64URL getX509CertThumbprint() { 286 287 return x5t; 288 } 289 290 291 /** 292 * Gets the X.509 certificate SHA-256 thumbprint ({@code x5t#S256}) of 293 * this JWK. 294 * 295 * @return The X.509 certificate SHA-256 thumbprint, {@code null} if 296 * not specified. 297 */ 298 public Base64URL getX509CertSHA256Thumbprint() { 299 300 return x5t256; 301 } 302 303 304 /** 305 * Gets the X.509 certificate chain ({@code x5c}) of this JWK. 306 * 307 * @return The X.509 certificate chain as a unmodifiable list, 308 * {@code null} if not specified. 309 */ 310 public List<Base64> getX509CertChain() { 311 312 if (x5c == null) { 313 return null; 314 } 315 316 return Collections.unmodifiableList(x5c); 317 } 318 319 320 /** 321 * Returns a reference to the underlying key store. 322 * 323 * @return The underlying key store, {@code null} if none. 324 */ 325 public KeyStore getKeyStore() { 326 327 return keyStore; 328 } 329 330 331 /** 332 * Returns the required JWK parameters. Intended as input for JWK 333 * thumbprint computation. See RFC 7638 for more information. 334 * 335 * @return The required JWK parameters, sorted alphanumerically by key 336 * name and ready for JSON serialisation. 337 */ 338 public abstract LinkedHashMap<String,?> getRequiredParams(); 339 340 341 /** 342 * Computes the SHA-256 thumbprint of this JWK. See RFC 7638 for more 343 * information. 344 * 345 * @return The SHA-256 thumbprint. 346 * 347 * @throws JOSEException If the SHA-256 hash algorithm is not 348 * supported. 349 */ 350 public Base64URL computeThumbprint() 351 throws JOSEException { 352 353 return computeThumbprint("SHA-256"); 354 } 355 356 357 /** 358 * Computes the thumbprint of this JWK using the specified hash 359 * algorithm. See RFC 7638 for more information. 360 * 361 * @param hashAlg The hash algorithm. Must not be {@code null}. 362 * 363 * @return The SHA-256 thumbprint. 364 * 365 * @throws JOSEException If the hash algorithm is not supported. 366 */ 367 public Base64URL computeThumbprint(final String hashAlg) 368 throws JOSEException { 369 370 return ThumbprintUtils.compute(hashAlg, this); 371 } 372 373 374 /** 375 * Returns {@code true} if this JWK contains private or sensitive 376 * (non-public) parameters. 377 * 378 * @return {@code true} if this JWK contains private parameters, else 379 * {@code false}. 380 */ 381 public abstract boolean isPrivate(); 382 383 384 /** 385 * Creates a copy of this JWK with all private or sensitive parameters 386 * removed. 387 * 388 * @return The newly created public JWK, or {@code null} if none can be 389 * created. 390 */ 391 public abstract JWK toPublicJWK(); 392 393 394 /** 395 * Returns the size of this JWK. 396 * 397 * @return The JWK size, in bits. 398 */ 399 public abstract int size(); 400 401 402 /** 403 * Returns a JSON object representation of this JWK. This method is 404 * intended to be called from extending classes. 405 * 406 * <p>Example: 407 * 408 * <pre> 409 * { 410 * "kty" : "RSA", 411 * "use" : "sig", 412 * "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b" 413 * } 414 * </pre> 415 * 416 * @return The JSON object representation. 417 */ 418 public JSONObject toJSONObject() { 419 420 JSONObject o = new JSONObject(); 421 422 o.put("kty", kty.getValue()); 423 424 if (use != null) { 425 o.put("use", use.identifier()); 426 } 427 428 if (ops != null) { 429 430 List<String> sl = new ArrayList<>(ops.size()); 431 432 for (KeyOperation op: ops) { 433 sl.add(op.identifier()); 434 } 435 436 o.put("key_ops", sl); 437 } 438 439 if (alg != null) { 440 o.put("alg", alg.getName()); 441 } 442 443 if (kid != null) { 444 o.put("kid", kid); 445 } 446 447 if (x5u != null) { 448 o.put("x5u", x5u.toString()); 449 } 450 451 if (x5t != null) { 452 o.put("x5t", x5t.toString()); 453 } 454 455 if (x5t256 != null) { 456 o.put("x5t#S256", x5t256.toString()); 457 } 458 459 if (x5c != null) { 460 o.put("x5c", x5c); 461 } 462 463 return o; 464 } 465 466 467 /** 468 * Returns the JSON object string representation of this JWK. 469 * 470 * @return The JSON object string representation. 471 */ 472 @Override 473 public String toJSONString() { 474 475 return toJSONObject().toString(); 476 } 477 478 479 /** 480 * @see #toJSONString 481 */ 482 @Override 483 public String toString() { 484 485 return toJSONObject().toString(); 486 } 487 488 489 /** 490 * Parses a JWK from the specified JSON object string representation. 491 * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 492 * {@link OctetSequenceKey}. 493 * 494 * @param s The JSON object string to parse. Must not be {@code null}. 495 * 496 * @return The JWK. 497 * 498 * @throws ParseException If the string couldn't be parsed to a 499 * supported JWK. 500 */ 501 public static JWK parse(final String s) 502 throws ParseException { 503 504 return parse(JSONObjectUtils.parse(s)); 505 } 506 507 508 /** 509 * Parses a JWK from the specified JSON object representation. The JWK 510 * must be an {@link ECKey}, an {@link RSAKey}, or a 511 * {@link OctetSequenceKey}. 512 * 513 * @param jsonObject The JSON object to parse. Must not be 514 * {@code null}. 515 * 516 * @return The JWK. 517 * 518 * @throws ParseException If the JSON object couldn't be parsed to a 519 * supported JWK. 520 */ 521 public static JWK parse(final JSONObject jsonObject) 522 throws ParseException { 523 524 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 525 526 if (kty == KeyType.EC) { 527 528 return ECKey.parse(jsonObject); 529 530 } else if (kty == KeyType.RSA) { 531 532 return RSAKey.parse(jsonObject); 533 534 } else if (kty == KeyType.OCT) { 535 536 return OctetSequenceKey.parse(jsonObject); 537 538 } else if (kty == KeyType.OKP) { 539 540 return OctetKeyPair.parse(jsonObject); 541 542 } else { 543 544 throw new ParseException("Unsupported key type \"kty\" parameter: " + kty, 0); 545 } 546 } 547 548 549 /** 550 * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the 551 * specified X.509 certificate. Requires BouncyCastle. 552 * 553 * <p><strong>Important:</strong> The X.509 certificate is not 554 * validated! 555 * 556 * <p>Sets the following JWK parameters: 557 * 558 * <ul> 559 * <li>For an EC key the curve is obtained from the subject public 560 * key info algorithm parameters. 561 * <li>The JWK use inferred by {@link KeyUse#from}. 562 * <li>The JWK ID from the X.509 serial number (in base 10). 563 * <li>The JWK X.509 certificate chain (this certificate only). 564 * <li>The JWK X.509 certificate SHA-256 thumbprint. 565 * </ul> 566 * 567 * @param cert The X.509 certificate. Must not be {@code null}. 568 * 569 * @return The public RSA or EC JWK. 570 * 571 * @throws JOSEException If parsing failed. 572 */ 573 public static JWK parse(final X509Certificate cert) 574 throws JOSEException { 575 576 if (cert.getPublicKey() instanceof RSAPublicKey) { 577 return RSAKey.parse(cert); 578 } else if (cert.getPublicKey() instanceof ECPublicKey) { 579 return ECKey.parse(cert); 580 } else { 581 throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm()); 582 } 583 } 584 585 586 /** 587 * Loads a JWK from the specified JCE key store. The JWK can be a 588 * public / private {@link RSAKey RSA key}, a public / private 589 * {@link ECKey EC key}, or a {@link OctetSequenceKey secret key}. 590 * Requires BouncyCastle. 591 * 592 * <p><strong>Important:</strong> The X.509 certificate is not 593 * validated! 594 * 595 * @param keyStore The key store. Must not be {@code null}. 596 * @param alias The alias. Must not be {@code null}. 597 * @param pin The pin to unlock the private key if any, empty or 598 * {@code null} if not required. 599 * 600 * @return The public / private RSA or EC JWK, or secret JWK, or 601 * {@code null} if no key with the specified alias was found. 602 * 603 * @throws KeyStoreException On a key store exception. 604 * @throws JOSEException If RSA or EC key loading failed. 605 */ 606 public static JWK load(final KeyStore keyStore, final String alias, final char[] pin) 607 throws KeyStoreException, JOSEException { 608 609 java.security.cert.Certificate cert = keyStore.getCertificate(alias); 610 611 if (cert == null) { 612 // Try secret key 613 return OctetSequenceKey.load(keyStore, alias, pin); 614 } 615 616 if (cert.getPublicKey() instanceof RSAPublicKey) { 617 return RSAKey.load(keyStore, alias, pin); 618 } else if (cert.getPublicKey() instanceof ECPublicKey) { 619 return ECKey.load(keyStore, alias, pin); 620 } else { 621 throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm()); 622 } 623 } 624}