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