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}