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}