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