001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.jwk;
019
020
021import java.io.Serializable;
022import java.net.URI;
023import java.text.ParseException;
024import java.util.*;
025
026import com.nimbusds.jose.Algorithm;
027import com.nimbusds.jose.JOSEException;
028import com.nimbusds.jose.util.Base64;
029import com.nimbusds.jose.util.Base64URL;
030import com.nimbusds.jose.util.JSONObjectUtils;
031import net.minidev.json.JSONAware;
032import net.minidev.json.JSONObject;
033
034
035/**
036 * The base abstract class for JSON Web Keys (JWKs). It serialises to a JSON
037 * object.
038 *
039 * <p>The following JSON object members are common to all JWK types:
040 *
041 * <ul>
042 *     <li>{@link #getKeyType kty} (required)
043 *     <li>{@link #getKeyUse use} (optional)
044 *     <li>{@link #getKeyOperations key_ops} (optional)
045 *     <li>{@link #getKeyID kid} (optional)
046 * </ul>
047 *
048 * <p>Example JWK (of the Elliptic Curve type):
049 *
050 * <pre>
051 * {
052 *   "kty" : "EC",
053 *   "crv" : "P-256",
054 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
055 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
056 *   "use" : "enc",
057 *   "kid" : "1"
058 * }
059 * </pre>
060 *
061 * @author Vladimir Dzhuvinov
062 * @author Justin Richer
063 * @version 2016-07-03
064 */
065public abstract class JWK implements JSONAware, Serializable {
066
067
068        private static final long serialVersionUID = 1L;
069
070
071        /**
072         * The MIME type of JWK objects: 
073         * {@code application/jwk+json; charset=UTF-8}
074         */
075        public static final String MIME_TYPE = "application/jwk+json; charset=UTF-8";
076
077
078        /**
079         * The key type, required.
080         */
081        private final KeyType kty;
082
083
084        /**
085         * The key use, optional.
086         */
087        private final KeyUse use;
088
089
090        /**
091         * The key operations, optional.
092         */
093        private final Set<KeyOperation> ops;
094
095
096        /**
097         * The intended JOSE algorithm for the key, optional.
098         */
099        private final Algorithm alg;
100
101
102        /**
103         * The key ID, optional.
104         */
105        private final String kid;
106
107
108        /**
109         * X.509 certificate URL, optional.
110         */
111        private final URI x5u;
112
113
114        /**
115         * X.509 certificate thumbprint, optional.
116         */
117        private final Base64URL x5t;
118
119
120        /**
121         * The X.509 certificate chain, optional.
122         */
123        private final List<Base64> x5c;
124
125
126        /**
127         * Creates a new JSON Web Key (JWK).
128         *
129         * @param kty The key type. Must not be {@code null}.
130         * @param use The key use, {@code null} if not specified or if the key
131         *            is intended for signing as well as encryption.
132         * @param ops The key operations, {@code null} if not specified.
133         * @param alg The intended JOSE algorithm for the key, {@code null} if
134         *            not specified.
135         * @param kid The key ID, {@code null} if not specified.
136         * @param x5u The X.509 certificate URL, {@code null} if not specified.
137         * @param x5t The X.509 certificate thumbprint, {@code null} if not
138         *            specified.
139         * @param x5c The X.509 certificate chain, {@code null} if not 
140         *            specified.
141         */
142        public JWK(final KeyType kty,
143                   final KeyUse use,
144                   final Set<KeyOperation> ops,
145                   final Algorithm alg,
146                   final String kid,
147                   final URI x5u,
148                   final Base64URL x5t,
149                   final List<Base64> x5c) {
150
151                if (kty == null) {
152                        throw new IllegalArgumentException("The key type \"kty\" parameter must not be null");
153                }
154
155                this.kty = kty;
156
157                if (use != null && ops != null) {
158                        throw new IllegalArgumentException("They key use \"use\" and key options \"key_opts\" parameters cannot be set together");
159                }
160
161                this.use = use;
162                this.ops = ops;
163
164                this.alg = alg;
165                this.kid = kid;
166
167                this.x5u = x5u;
168                this.x5t = x5t;
169                this.x5c = x5c;
170        }
171
172
173        /**
174         * Gets the type ({@code kty}) of this JWK.
175         *
176         * @return The key type.
177         */
178        public KeyType getKeyType() {
179
180                return kty;
181        }
182
183
184        /**
185         * Gets the use ({@code use}) of this JWK.
186         *
187         * @return The key use, {@code null} if not specified or if the key is
188         *         intended for signing as well as encryption.
189         */
190        public KeyUse getKeyUse() {
191
192                return use;
193        }
194
195
196        /**
197         * Gets the operations ({@code key_ops}) for this JWK.
198         *
199         * @return The key operations, {@code null} if not specified.
200         */
201        public Set<KeyOperation> getKeyOperations() {
202
203                return ops;
204        }
205
206
207        /**
208         * Gets the intended JOSE algorithm ({@code alg}) for this JWK.
209         *
210         * @return The intended JOSE algorithm, {@code null} if not specified.
211         */
212        public Algorithm getAlgorithm() {
213
214                return alg;
215        }
216
217
218        /**
219         * Gets the ID ({@code kid}) of this JWK. The key ID can be used to 
220         * match a specific key. This can be used, for instance, to choose a 
221         * key within a {@link JWKSet} during key rollover. The key ID may also 
222         * correspond to a JWS/JWE {@code kid} header parameter value.
223         *
224         * @return The key ID, {@code null} if not specified.
225         */
226        public String getKeyID() {
227
228                return kid;
229        }
230
231
232        /**
233         * Gets the X.509 certificate URL ({@code x5u}) of this JWK.
234         *
235         * @return The X.509 certificate URL, {@code null} if not specified.
236         */
237        public URI getX509CertURL() {
238
239                return x5u;
240        }
241
242
243        /**
244         * Gets the X.509 certificate thumbprint ({@code x5t}) of this JWK.
245         *
246         * @return The X.509 certificate thumbprint, {@code null} if not
247         *         specified.
248         */
249        public Base64URL getX509CertThumbprint() {
250
251                return x5t;
252        }
253
254
255        /**
256         * Gets the X.509 certificate chain ({@code x5c}) of this JWK.
257         *
258         * @return The X.509 certificate chain as a unmodifiable list,
259         *         {@code null} if not specified.
260         */
261        public List<Base64> getX509CertChain() {
262
263                if (x5c == null) {
264                        return null;
265                }
266
267                return Collections.unmodifiableList(x5c);
268        }
269
270
271        /**
272         * Returns the required JWK parameters. Intended as input for JWK
273         * thumbprint computation. See RFC 7638 for more information.
274         *
275         * @return The required JWK parameters, sorted alphanumerically by key
276         *         name and ready for JSON serialisation.
277         */
278        public abstract LinkedHashMap<String,?> getRequiredParams();
279
280
281        /**
282         * Computes the SHA-256 thumbprint of this JWK. See RFC 7638 for more
283         * information.
284         *
285         * @return The SHA-256 thumbprint.
286         *
287         * @throws JOSEException If the SHA-256 hash algorithm is not
288         *                       supported.
289         */
290        public Base64URL computeThumbprint()
291                throws JOSEException {
292
293                return computeThumbprint("SHA-256");
294        }
295
296
297        /**
298         * Computes the thumbprint of this JWK using the specified hash
299         * algorithm. See RFC 7638 for more information.
300         *
301         * @param hashAlg The hash algorithm. Must not be {@code null}.
302         *
303         * @return The SHA-256 thumbprint.
304         *
305         * @throws JOSEException If the hash algorithm is not supported.
306         */
307        public Base64URL computeThumbprint(final String hashAlg)
308                throws JOSEException {
309
310                return ThumbprintUtils.compute(hashAlg, this);
311        }
312
313
314        /**
315         * Returns {@code true} if this JWK contains private or sensitive
316         * (non-public) parameters.
317         *
318         * @return {@code true} if this JWK contains private parameters, else
319         *         {@code false}.
320         */
321        public abstract boolean isPrivate();
322
323
324        /**
325         * Creates a copy of this JWK with all private or sensitive parameters 
326         * removed.
327         * 
328         * @return The newly created public JWK, or {@code null} if none can be
329         *         created.
330         */
331        public abstract JWK toPublicJWK();
332
333
334        /**
335         * Returns the size of this JWK.
336         *
337         * @return The JWK size, in bits.
338         */
339        public abstract int size();
340
341
342        /**
343         * Returns a JSON object representation of this JWK. This method is 
344         * intended to be called from extending classes.
345         *
346         * <p>Example:
347         *
348         * <pre>
349         * {
350         *   "kty" : "RSA",
351         *   "use" : "sig",
352         *   "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b"
353         * }
354         * </pre>
355         *
356         * @return The JSON object representation.
357         */
358        public JSONObject toJSONObject() {
359
360                JSONObject o = new JSONObject();
361
362                o.put("kty", kty.getValue());
363
364                if (use != null) {
365                        o.put("use", use.identifier());
366                }
367
368                if (ops != null) {
369
370                        List<String> sl = new ArrayList<>(ops.size());
371
372                        for (KeyOperation op: ops) {
373                                sl.add(op.identifier());
374                        }
375
376                        o.put("key_ops", sl);
377                }
378
379                if (alg != null) {
380                        o.put("alg", alg.getName());
381                }
382
383                if (kid != null) {
384                        o.put("kid", kid);
385                }
386
387                if (x5u != null) {
388                        o.put("x5u", x5u.toString());
389                }
390
391                if (x5t != null) {
392                        o.put("x5t", x5t.toString());
393                }
394
395                if (x5c != null) {
396                        o.put("x5c", x5c);
397                }
398
399                return o;
400        }
401
402
403        /**
404         * Returns the JSON object string representation of this JWK.
405         *
406         * @return The JSON object string representation.
407         */
408        @Override
409        public String toJSONString() {
410
411                return toJSONObject().toString();
412        }
413
414
415        /**
416         * @see #toJSONString
417         */
418        @Override
419        public String toString() {
420
421                return toJSONObject().toString();
422        }
423
424
425        /**
426         * Parses a JWK from the specified JSON object string representation. 
427         * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 
428         * {@link OctetSequenceKey}.
429         *
430         * @param s The JSON object string to parse. Must not be {@code null}.
431         *
432         * @return The JWK.
433         *
434         * @throws ParseException If the string couldn't be parsed to a
435         *                        supported JWK.
436         */
437        public static JWK parse(final String s)
438                throws ParseException {
439
440                return parse(JSONObjectUtils.parse(s));
441        }
442
443
444        /**
445         * Parses a JWK from the specified JSON object representation. The JWK 
446         * must be an {@link ECKey}, an {@link RSAKey}, or a 
447         * {@link OctetSequenceKey}.
448         *
449         * @param jsonObject The JSON object to parse. Must not be 
450         *                   {@code null}.
451         *
452         * @return The JWK.
453         *
454         * @throws ParseException If the JSON object couldn't be parsed to a 
455         *                        supported JWK.
456         */
457        public static JWK parse(final JSONObject jsonObject)
458                throws ParseException {
459
460                KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
461
462                if (kty == KeyType.EC) {
463                        
464                        return ECKey.parse(jsonObject);
465
466                } else if (kty == KeyType.RSA) {
467                        
468                        return RSAKey.parse(jsonObject);
469
470                } else if (kty == KeyType.OCT) {
471                        
472                        return OctetSequenceKey.parse(jsonObject);
473
474                } else {
475
476                        throw new ParseException("Unsupported key type \"kty\" parameter: " + kty, 0);
477                }
478        }
479}