001package com.nimbusds.jose.jwk; 002 003 004import java.net.URL; 005import java.text.ParseException; 006import java.util.Collections; 007import java.util.List; 008 009import javax.mail.internet.ContentType; 010import javax.mail.internet.ParameterList; 011 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 #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$ (2013-05-29) 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 ContentType MIME_TYPE; 058 059 060 static { 061 062 final ParameterList params = new ParameterList(); 063 params.set("charset", "UTF-8"); 064 065 MIME_TYPE = new ContentType("application", "jwk+json", params); 066 } 067 068 069 /** 070 * The key type, required. 071 */ 072 private final KeyType kty; 073 074 075 /** 076 * The key use, optional. 077 */ 078 private final Use use; 079 080 081 /** 082 * The intended JOSE algorithm for the key, optional. 083 */ 084 private final Algorithm alg; 085 086 087 /** 088 * The key ID, optional. 089 */ 090 private final String kid; 091 092 093 /** 094 * X.509 certificate URL, optional. 095 */ 096 private final URL x5u; 097 098 099 /** 100 * X.509 certificate thumbprint, optional. 101 */ 102 private final Base64URL x5t; 103 104 105 /** 106 * The X.509 certificate chain, optional. 107 */ 108 private final List<Base64> x5c; 109 110 111 /** 112 * Creates a new JSON Web Key (JWK). 113 * 114 * @param kty The key type. Must not be {@code null}. 115 * @param use The key use, {@code null} if not specified or if the key 116 * is intended for signing as well as encryption. 117 * @param alg The intended JOSE algorithm for the key, {@code null} if 118 * not specified. 119 * @param kid The key ID, {@code null} if not specified. 120 * @param x5u The X.509 certificate URL, {@code null} if not specified. 121 * @param x5t The X.509 certificate thumbprint, {@code null} if not 122 * specified. 123 * @param x5c The X.509 certificate chain, {@code null} if not 124 * specified. 125 */ 126 public JWK(final KeyType kty, final Use use, final Algorithm alg, final String kid, 127 final URL x5u, final Base64URL x5t, final List<Base64> x5c) { 128 129 if (kty == null) { 130 throw new IllegalArgumentException("The key type \"kty\" must not be null"); 131 } 132 133 this.kty = kty; 134 this.use = use; 135 this.alg = alg; 136 this.kid = kid; 137 138 this.x5u = x5u; 139 this.x5t = x5t; 140 this.x5c = x5c; 141 } 142 143 144 /** 145 * Gets the type ({@code kty}) of this JWK. 146 * 147 * @return The key type. 148 */ 149 public KeyType getKeyType() { 150 151 return kty; 152 } 153 154 155 /** 156 * Gets the use ({@code use}) of this JWK. 157 * 158 * @return The key use, {@code null} if not specified or if the key is 159 * intended for signing as well as encryption. 160 */ 161 public Use getKeyUse() { 162 163 return use; 164 } 165 166 167 /** 168 * Gets the intended JOSE algorithm ({@code alg}) for this JWK. 169 * 170 * @return The intended JOSE algorithm, {@code null} if not specified. 171 */ 172 public Algorithm getAlgorithm() { 173 174 return alg; 175 } 176 177 178 /** 179 * Gets the ID ({@code kid}) of this JWK. The key ID can be used to 180 * match a specific key. This can be used, for instance, to choose a 181 * key within a {@link JWKSet} during key rollover. The key ID may also 182 * correspond to a JWS/JWE {@code kid} header parameter value. 183 * 184 * @return The key ID, {@code null} if not specified. 185 */ 186 public String getKeyID() { 187 188 return kid; 189 } 190 191 192 /** 193 * Gets the X.509 certificate URL ({@code x5u}) of this JWK. 194 * 195 * @return The X.509 certificate URL, {@code null} if not specified. 196 */ 197 public URL getX509CertURL() { 198 199 return x5u; 200 } 201 202 203 /** 204 * Gets the X.509 certificate thumbprint ({@code x5t}) of this JWK. 205 * 206 * @return The X.509 certificate thumbprint, {@code null} if not 207 * specified. 208 */ 209 public Base64URL getX509CertThumbprint() { 210 211 return x5t; 212 } 213 214 215 /** 216 * Gets the X.509 certificate chain ({@code x5c}) of this JWK. 217 * 218 * @return The X.509 certificate chain as a unmodifiable list, 219 * {@code null} if not specified. 220 */ 221 public List<Base64> getX509CertChain() { 222 223 if (x5c == null) { 224 return null; 225 } 226 227 return Collections.unmodifiableList(x5c); 228 } 229 230 231 /** 232 * Returns {@code true} if this JWK contains private or sensitive 233 * (non-public) parameters. 234 * 235 * @return {@code true} if this JWK contains private parameters, else 236 * {@code false}. 237 */ 238 public abstract boolean isPrivate(); 239 240 241 /** 242 * Creates a copy of this JWK with all private or sensitive parameters 243 * removed. 244 * 245 * @return The newly created public JWK, or {@code null} if none can be 246 * created. 247 */ 248 public abstract JWK toPublicJWK(); 249 250 251 /** 252 * Returns a JSON object representation of this JWK. This method is 253 * intended to be called from extending classes. 254 * 255 * <p>Example: 256 * 257 * <pre> 258 * { 259 * "kty" : "RSA", 260 * "use" : "sig", 261 * "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b" 262 * } 263 * </pre> 264 * 265 * @return The JSON object representation. 266 */ 267 public JSONObject toJSONObject() { 268 269 JSONObject o = new JSONObject(); 270 271 o.put("kty", kty.getValue()); 272 273 if (use != null) { 274 275 if (use == Use.SIGNATURE) { 276 o.put("use", "sig"); 277 } 278 279 if (use == Use.ENCRYPTION) { 280 o.put("use", "enc"); 281 } 282 } 283 284 if (alg != null) { 285 o.put("alg", alg.getName()); 286 } 287 288 if (kid != null) { 289 o.put("kid", kid); 290 } 291 292 if (x5u != null) { 293 o.put("x5u", x5u.toString()); 294 } 295 296 if (x5t != null) { 297 o.put("x5t", x5t.toString()); 298 } 299 300 if (x5c != null) { 301 o.put("x5c", x5c); 302 } 303 304 return o; 305 } 306 307 308 /** 309 * Returns the JSON object string representation of this JWK. 310 * 311 * @return The JSON object string representation. 312 */ 313 @Override 314 public String toJSONString() { 315 316 return toJSONObject().toString(); 317 } 318 319 320 /** 321 * @see #toJSONString 322 */ 323 @Override 324 public String toString() { 325 326 return toJSONObject().toString(); 327 } 328 329 330 /** 331 * Parses a JWK from the specified JSON object string representation. 332 * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 333 * {@link OctetSequenceKey}. 334 * 335 * @param s The JSON object string to parse. Must not be {@code null}. 336 * 337 * @return The JWK. 338 * 339 * @throws ParseException If the string couldn't be parsed to a 340 * supported JWK. 341 */ 342 public static JWK parse(final String s) 343 throws ParseException { 344 345 return parse(JSONObjectUtils.parseJSONObject(s)); 346 } 347 348 349 /** 350 * Parses a JWK from the specified JSON object representation. The JWK 351 * must be an {@link ECKey}, an {@link RSAKey}, or a 352 * {@link OctetSequenceKey}. 353 * 354 * @param jsonObject The JSON object to parse. Must not be 355 * {@code null}. 356 * 357 * @return The JWK. 358 * 359 * @throws ParseException If the JSON object couldn't be parsed to a 360 * supported JWK. 361 */ 362 public static JWK parse(final JSONObject jsonObject) 363 throws ParseException { 364 365 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty")); 366 367 if (kty == KeyType.EC) { 368 369 return ECKey.parse(jsonObject); 370 371 } else if (kty == KeyType.RSA) { 372 373 return RSAKey.parse(jsonObject); 374 375 } else if (kty == KeyType.OCT) { 376 377 return OctetSequenceKey.parse(jsonObject); 378 379 } else { 380 381 throw new ParseException("Unsupported key type \"kty\" parameter: " + kty, 0); 382 } 383 } 384}