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