001package com.nimbusds.jose.jwk; 002 003 004import java.net.URI; 005import java.text.ParseException; 006import java.util.ArrayList; 007import java.util.Collections; 008import java.util.List; 009import java.util.Set; 010 011import com.nimbusds.jose.JOSEException; 012import net.minidev.json.JSONAware; 013import net.minidev.json.JSONObject; 014 015import com.nimbusds.jose.Algorithm; 016import com.nimbusds.jose.util.Base64; 017import com.nimbusds.jose.util.Base64URL; 018import com.nimbusds.jose.util.JSONObjectUtils; 019 020 021/** 022 * The base abstract class for JSON Web Keys (JWKs). It serialises to a JSON 023 * object. 024 * 025 * <p>The following JSON object members are common to all JWK types: 026 * 027 * <ul> 028 * <li>{@link #getKeyType kty} (required) 029 * <li>{@link #getKeyUse use} (optional) 030 * <li>{@link #getKeyOperations key_ops} (optional) 031 * <li>{@link #getKeyID kid} (optional) 032 * </ul> 033 * 034 * <p>Example JWK (of the Elliptic Curve type): 035 * 036 * <pre> 037 * { 038 * "kty" : "EC", 039 * "crv" : "P-256", 040 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", 041 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 042 * "use" : "enc", 043 * "kid" : "1" 044 * } 045 * </pre> 046 * 047 * @author Vladimir Dzhuvinov 048 * @author Justin Richer 049 * @version 2015-09-21 050 */ 051public abstract class JWK implements JSONAware { 052 053 054 /** 055 * The MIME type of JWK objects: 056 * {@code application/jwk+json; charset=UTF-8} 057 */ 058 public static final String MIME_TYPE = "application/jwk+json; charset=UTF-8"; 059 060 061 /** 062 * The key type, required. 063 */ 064 private final KeyType kty; 065 066 067 /** 068 * The key use, optional. 069 */ 070 private final KeyUse use; 071 072 073 /** 074 * The key operations, optional. 075 */ 076 private final Set<KeyOperation> ops; 077 078 079 /** 080 * The intended JOSE algorithm for the key, optional. 081 */ 082 private final Algorithm alg; 083 084 085 /** 086 * The key ID, optional. 087 */ 088 private final String kid; 089 090 091 /** 092 * X.509 certificate URL, optional. 093 */ 094 private final URI x5u; 095 096 097 /** 098 * X.509 certificate thumbprint, optional. 099 */ 100 private final Base64URL x5t; 101 102 103 /** 104 * The X.509 certificate chain, optional. 105 */ 106 private final List<Base64> x5c; 107 108 109 /** 110 * Creates a new JSON Web Key (JWK). 111 * 112 * @param kty The key type. Must not be {@code null}. 113 * @param use The key use, {@code null} if not specified or if the key 114 * is intended for signing as well as encryption. 115 * @param ops The key operations, {@code null} if not specified. 116 * @param alg The intended JOSE algorithm for the key, {@code null} if 117 * not specified. 118 * @param kid The key ID, {@code null} if not specified. 119 * @param x5u The X.509 certificate URL, {@code null} if not specified. 120 * @param x5t The X.509 certificate thumbprint, {@code null} if not 121 * specified. 122 * @param x5c The X.509 certificate chain, {@code null} if not 123 * specified. 124 */ 125 public JWK(final KeyType kty, 126 final KeyUse use, 127 final Set<KeyOperation> ops, 128 final Algorithm alg, 129 final String kid, 130 final URI x5u, 131 final Base64URL x5t, 132 final List<Base64> x5c) { 133 134 if (kty == null) { 135 throw new IllegalArgumentException("The key type \"kty\" parameter must not be null"); 136 } 137 138 this.kty = kty; 139 140 if (use != null && ops != null) { 141 throw new IllegalArgumentException("They key use \"use\" and key options \"key_opts\" parameters cannot be set together"); 142 } 143 144 this.use = use; 145 this.ops = ops; 146 147 this.alg = alg; 148 this.kid = kid; 149 150 this.x5u = x5u; 151 this.x5t = x5t; 152 this.x5c = x5c; 153 } 154 155 156 /** 157 * Gets the type ({@code kty}) of this JWK. 158 * 159 * @return The key type. 160 */ 161 public KeyType getKeyType() { 162 163 return kty; 164 } 165 166 167 /** 168 * Gets the use ({@code use}) of this JWK. 169 * 170 * @return The key use, {@code null} if not specified or if the key is 171 * intended for signing as well as encryption. 172 */ 173 public KeyUse getKeyUse() { 174 175 return use; 176 } 177 178 179 /** 180 * Gets the operations ({@code key_ops}) for this JWK. 181 * 182 * @return The key operations, {@code null} if not specified. 183 */ 184 public Set<KeyOperation> getKeyOperations() { 185 186 return ops; 187 } 188 189 190 /** 191 * Gets the intended JOSE algorithm ({@code alg}) for this JWK. 192 * 193 * @return The intended JOSE algorithm, {@code null} if not specified. 194 */ 195 public Algorithm getAlgorithm() { 196 197 return alg; 198 } 199 200 201 /** 202 * Gets the ID ({@code kid}) of this JWK. The key ID can be used to 203 * match a specific key. This can be used, for instance, to choose a 204 * key within a {@link JWKSet} during key rollover. The key ID may also 205 * correspond to a JWS/JWE {@code kid} header parameter value. 206 * 207 * @return The key ID, {@code null} if not specified. 208 */ 209 public String getKeyID() { 210 211 return kid; 212 } 213 214 215 /** 216 * Gets the X.509 certificate URL ({@code x5u}) of this JWK. 217 * 218 * @return The X.509 certificate URL, {@code null} if not specified. 219 */ 220 public URI getX509CertURL() { 221 222 return x5u; 223 } 224 225 226 /** 227 * Gets the X.509 certificate thumbprint ({@code x5t}) of this JWK. 228 * 229 * @return The X.509 certificate thumbprint, {@code null} if not 230 * specified. 231 */ 232 public Base64URL getX509CertThumbprint() { 233 234 return x5t; 235 } 236 237 238 /** 239 * Gets the X.509 certificate chain ({@code x5c}) of this JWK. 240 * 241 * @return The X.509 certificate chain as a unmodifiable list, 242 * {@code null} if not specified. 243 */ 244 public List<Base64> getX509CertChain() { 245 246 if (x5c == null) { 247 return null; 248 } 249 250 return Collections.unmodifiableList(x5c); 251 } 252 253 254 /** 255 * Computes the SHA-256 thumbprint of this JWK. See RFC 7638 for more 256 * information. 257 * 258 * @return The SHA-256 thumbprint. 259 * 260 * @throws JOSEException If the SHA-256 hash algorithm is not 261 * supported. 262 */ 263 public Base64URL computeThumbprint() 264 throws JOSEException { 265 266 return computeThumbprint("SHA-256"); 267 } 268 269 270 /** 271 * Computes the thumbprint of this JWK using the specified hash 272 * algorithm. See RFC 7638 for more information. 273 * 274 * @param hashAlg The hash algorithm. Must not be {@code null}. 275 * 276 * @return The SHA-256 thumbprint. 277 * 278 * @throws JOSEException If the hash algorithm is not supported. 279 */ 280 public abstract Base64URL computeThumbprint(final String hashAlg) 281 throws JOSEException; 282 283 284 /** 285 * Returns {@code true} if this JWK contains private or sensitive 286 * (non-public) parameters. 287 * 288 * @return {@code true} if this JWK contains private parameters, else 289 * {@code false}. 290 */ 291 public abstract boolean isPrivate(); 292 293 294 /** 295 * Creates a copy of this JWK with all private or sensitive parameters 296 * removed. 297 * 298 * @return The newly created public JWK, or {@code null} if none can be 299 * created. 300 */ 301 public abstract JWK toPublicJWK(); 302 303 304 /** 305 * Returns a JSON object representation of this JWK. This method is 306 * intended to be called from extending classes. 307 * 308 * <p>Example: 309 * 310 * <pre> 311 * { 312 * "kty" : "RSA", 313 * "use" : "sig", 314 * "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b" 315 * } 316 * </pre> 317 * 318 * @return The JSON object representation. 319 */ 320 public JSONObject toJSONObject() { 321 322 JSONObject o = new JSONObject(); 323 324 o.put("kty", kty.getValue()); 325 326 if (use != null) { 327 o.put("use", use.identifier()); 328 } 329 330 if (ops != null) { 331 332 List<String> sl = new ArrayList<>(ops.size()); 333 334 for (KeyOperation op: ops) { 335 sl.add(op.identifier()); 336 } 337 338 o.put("key_ops", sl); 339 } 340 341 if (alg != null) { 342 o.put("alg", alg.getName()); 343 } 344 345 if (kid != null) { 346 o.put("kid", kid); 347 } 348 349 if (x5u != null) { 350 o.put("x5u", x5u.toString()); 351 } 352 353 if (x5t != null) { 354 o.put("x5t", x5t.toString()); 355 } 356 357 if (x5c != null) { 358 o.put("x5c", x5c); 359 } 360 361 return o; 362 } 363 364 365 /** 366 * Returns the JSON object string representation of this JWK. 367 * 368 * @return The JSON object string representation. 369 */ 370 @Override 371 public String toJSONString() { 372 373 return toJSONObject().toString(); 374 } 375 376 377 /** 378 * @see #toJSONString 379 */ 380 @Override 381 public String toString() { 382 383 return toJSONObject().toString(); 384 } 385 386 387 /** 388 * Parses a JWK from the specified JSON object string representation. 389 * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 390 * {@link OctetSequenceKey}. 391 * 392 * @param s The JSON object string to parse. Must not be {@code null}. 393 * 394 * @return The JWK. 395 * 396 * @throws ParseException If the string couldn't be parsed to a 397 * supported JWK. 398 */ 399 public static JWK parse(final String s) 400 throws ParseException { 401 402 return parse(JSONObjectUtils.parseJSONObject(s)); 403 } 404 405 406 /** 407 * Parses a JWK from the specified JSON object representation. The JWK 408 * must be an {@link ECKey}, an {@link RSAKey}, or a 409 * {@link OctetSequenceKey}. 410 * 411 * @param jsonObject The JSON object to parse. Must not be 412 * {@code null}. 413 * 414 * @return The JWK. 415 * 416 * @throws ParseException If the JSON object couldn't be parsed to a 417 * supported JWK. 418 */ 419 public static JWK parse(final JSONObject jsonObject) 420 throws ParseException { 421 422 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 423 424 if (kty == KeyType.EC) { 425 426 return ECKey.parse(jsonObject); 427 428 } else if (kty == KeyType.RSA) { 429 430 return RSAKey.parse(jsonObject); 431 432 } else if (kty == KeyType.OCT) { 433 434 return OctetSequenceKey.parse(jsonObject); 435 436 } else { 437 438 throw new ParseException("Unsupported key type \"kty\" parameter: " + kty, 0); 439 } 440 } 441}