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