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