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.security.KeyStore;
024import java.security.KeyStoreException;
025import java.security.cert.X509Certificate;
026import java.security.interfaces.ECPublicKey;
027import java.security.interfaces.RSAPublicKey;
028import java.text.ParseException;
029import java.util.*;
030
031import com.nimbusds.jose.Algorithm;
032import com.nimbusds.jose.JOSEException;
033import com.nimbusds.jose.util.Base64;
034import com.nimbusds.jose.util.Base64URL;
035import com.nimbusds.jose.util.JSONObjectUtils;
036import net.minidev.json.JSONAware;
037import net.minidev.json.JSONObject;
038
039
040/**
041 * The base abstract class for JSON Web Keys (JWKs). It serialises to a JSON
042 * object.
043 *
044 * <p>The following JSON object members are common to all JWK types:
045 *
046 * <ul>
047 *     <li>{@link #getKeyType kty} (required)
048 *     <li>{@link #getKeyUse use} (optional)
049 *     <li>{@link #getKeyOperations key_ops} (optional)
050 *     <li>{@link #getKeyID kid} (optional)
051 * </ul>
052 *
053 * <p>Example JWK (of the Elliptic Curve type):
054 *
055 * <pre>
056 * {
057 *   "kty" : "EC",
058 *   "crv" : "P-256",
059 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
060 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
061 *   "use" : "enc",
062 *   "kid" : "1"
063 * }
064 * </pre>
065 *
066 * @author Vladimir Dzhuvinov
067 * @author Justin Richer
068 * @version 2017-04-08
069 */
070public abstract class JWK implements JSONAware, Serializable {
071
072
073        private static final long serialVersionUID = 1L;
074
075
076        /**
077         * The MIME type of JWK objects: 
078         * {@code application/jwk+json; charset=UTF-8}
079         */
080        public static final String MIME_TYPE = "application/jwk+json; charset=UTF-8";
081
082
083        /**
084         * The key type, required.
085         */
086        private final KeyType kty;
087
088
089        /**
090         * The key use, optional.
091         */
092        private final KeyUse use;
093
094
095        /**
096         * The key operations, optional.
097         */
098        private final Set<KeyOperation> ops;
099
100
101        /**
102         * The intended JOSE algorithm for the key, optional.
103         */
104        private final Algorithm alg;
105
106
107        /**
108         * The key ID, optional.
109         */
110        private final String kid;
111
112
113        /**
114         * X.509 certificate URL, optional.
115         */
116        private final URI x5u;
117
118
119        /**
120         * X.509 certificate SHA-1 thumbprint, optional.
121         */
122        @Deprecated
123        private final Base64URL x5t;
124        
125        
126        /**
127         * X.509 certificate SHA-256 thumbprint, optional.
128         */
129        private Base64URL x5t256;
130
131
132        /**
133         * The X.509 certificate chain, optional.
134         */
135        private final List<Base64> x5c;
136        
137        
138        /**
139         * Reference to the underlying key store, {@code null} if none.
140         */
141        private final KeyStore keyStore;
142
143
144        /**
145         * Creates a new JSON Web Key (JWK).
146         *
147         * @param kty    The key type. Must not be {@code null}.
148         * @param use    The key use, {@code null} if not specified or if the
149         *               key is intended for signing as well as encryption.
150         * @param ops    The key operations, {@code null} if not specified.
151         * @param alg    The intended JOSE algorithm for the key, {@code null}
152         *               if not specified.
153         * @param kid    The key ID, {@code null} if not specified.
154         * @param x5u    The X.509 certificate URL, {@code null} if not
155         *               specified.
156         * @param x5t    The X.509 certificate thumbprint, {@code null} if not
157         *               specified.
158         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
159         *               if not specified.
160         * @param x5c    The X.509 certificate chain, {@code null} if not
161         *               specified.
162         * @param ks     Reference to the underlying key store, {@code null} if
163         *               none.
164         */
165        protected JWK(final KeyType kty,
166                      final KeyUse use,
167                      final Set<KeyOperation> ops,
168                      final Algorithm alg,
169                      final String kid,
170                      final URI x5u,
171                      final Base64URL x5t,
172                      final Base64URL x5t256,
173                      final List<Base64> x5c,
174                      final KeyStore ks) {
175
176                if (kty == null) {
177                        throw new IllegalArgumentException("The key type \"kty\" parameter must not be null");
178                }
179
180                this.kty = kty;
181
182                if (use != null && ops != null) {
183                        throw new IllegalArgumentException("They key use \"use\" and key options \"key_opts\" parameters cannot be set together");
184                }
185
186                this.use = use;
187                this.ops = ops;
188
189                this.alg = alg;
190                this.kid = kid;
191
192                this.x5u = x5u;
193                this.x5t = x5t;
194                this.x5t256 = x5t256;
195                this.x5c = x5c;
196                
197                this.keyStore = ks;
198        }
199
200
201        /**
202         * Gets the type ({@code kty}) of this JWK.
203         *
204         * @return The key type.
205         */
206        public KeyType getKeyType() {
207
208                return kty;
209        }
210
211
212        /**
213         * Gets the use ({@code use}) of this JWK.
214         *
215         * @return The key use, {@code null} if not specified or if the key is
216         *         intended for signing as well as encryption.
217         */
218        public KeyUse getKeyUse() {
219
220                return use;
221        }
222
223
224        /**
225         * Gets the operations ({@code key_ops}) for this JWK.
226         *
227         * @return The key operations, {@code null} if not specified.
228         */
229        public Set<KeyOperation> getKeyOperations() {
230
231                return ops;
232        }
233
234
235        /**
236         * Gets the intended JOSE algorithm ({@code alg}) for this JWK.
237         *
238         * @return The intended JOSE algorithm, {@code null} if not specified.
239         */
240        public Algorithm getAlgorithm() {
241
242                return alg;
243        }
244
245
246        /**
247         * Gets the ID ({@code kid}) of this JWK. The key ID can be used to 
248         * match a specific key. This can be used, for instance, to choose a 
249         * key within a {@link JWKSet} during key rollover. The key ID may also 
250         * correspond to a JWS/JWE {@code kid} header parameter value.
251         *
252         * @return The key ID, {@code null} if not specified.
253         */
254        public String getKeyID() {
255
256                return kid;
257        }
258
259
260        /**
261         * Gets the X.509 certificate URL ({@code x5u}) of this JWK.
262         *
263         * @return The X.509 certificate URL, {@code null} if not specified.
264         */
265        public URI getX509CertURL() {
266
267                return x5u;
268        }
269
270
271        /**
272         * Gets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of this
273         * JWK.
274         *
275         * @return The X.509 certificate SHA-1 thumbprint, {@code null} if not
276         *         specified.
277         */
278        @Deprecated
279        public Base64URL getX509CertThumbprint() {
280
281                return x5t;
282        }
283        
284        
285        /**
286         * Gets the X.509 certificate SHA-256 thumbprint ({@code x5t#S256}) of
287         * this JWK.
288         *
289         * @return The X.509 certificate SHA-256 thumbprint, {@code null} if
290         *         not specified.
291         */
292        public Base64URL getX509CertSHA256Thumbprint() {
293                
294                return x5t256;
295        }
296
297
298        /**
299         * Gets the X.509 certificate chain ({@code x5c}) of this JWK.
300         *
301         * @return The X.509 certificate chain as a unmodifiable list,
302         *         {@code null} if not specified.
303         */
304        public List<Base64> getX509CertChain() {
305
306                if (x5c == null) {
307                        return null;
308                }
309
310                return Collections.unmodifiableList(x5c);
311        }
312        
313        
314        /**
315         * Returns a reference to the underlying key store.
316         *
317         * @return The underlying key store, {@code null} if none.
318         */
319        public KeyStore getKeyStore() {
320                
321                return keyStore;
322        }
323
324
325        /**
326         * Returns the required JWK parameters. Intended as input for JWK
327         * thumbprint computation. See RFC 7638 for more information.
328         *
329         * @return The required JWK parameters, sorted alphanumerically by key
330         *         name and ready for JSON serialisation.
331         */
332        public abstract LinkedHashMap<String,?> getRequiredParams();
333
334
335        /**
336         * Computes the SHA-256 thumbprint of this JWK. See RFC 7638 for more
337         * information.
338         *
339         * @return The SHA-256 thumbprint.
340         *
341         * @throws JOSEException If the SHA-256 hash algorithm is not
342         *                       supported.
343         */
344        public Base64URL computeThumbprint()
345                throws JOSEException {
346
347                return computeThumbprint("SHA-256");
348        }
349
350
351        /**
352         * Computes the thumbprint of this JWK using the specified hash
353         * algorithm. See RFC 7638 for more information.
354         *
355         * @param hashAlg The hash algorithm. Must not be {@code null}.
356         *
357         * @return The SHA-256 thumbprint.
358         *
359         * @throws JOSEException If the hash algorithm is not supported.
360         */
361        public Base64URL computeThumbprint(final String hashAlg)
362                throws JOSEException {
363
364                return ThumbprintUtils.compute(hashAlg, this);
365        }
366
367
368        /**
369         * Returns {@code true} if this JWK contains private or sensitive
370         * (non-public) parameters.
371         *
372         * @return {@code true} if this JWK contains private parameters, else
373         *         {@code false}.
374         */
375        public abstract boolean isPrivate();
376
377
378        /**
379         * Creates a copy of this JWK with all private or sensitive parameters 
380         * removed.
381         * 
382         * @return The newly created public JWK, or {@code null} if none can be
383         *         created.
384         */
385        public abstract JWK toPublicJWK();
386
387
388        /**
389         * Returns the size of this JWK.
390         *
391         * @return The JWK size, in bits.
392         */
393        public abstract int size();
394
395
396        /**
397         * Returns a JSON object representation of this JWK. This method is 
398         * intended to be called from extending classes.
399         *
400         * <p>Example:
401         *
402         * <pre>
403         * {
404         *   "kty" : "RSA",
405         *   "use" : "sig",
406         *   "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b"
407         * }
408         * </pre>
409         *
410         * @return The JSON object representation.
411         */
412        public JSONObject toJSONObject() {
413
414                JSONObject o = new JSONObject();
415
416                o.put("kty", kty.getValue());
417
418                if (use != null) {
419                        o.put("use", use.identifier());
420                }
421
422                if (ops != null) {
423
424                        List<String> sl = new ArrayList<>(ops.size());
425
426                        for (KeyOperation op: ops) {
427                                sl.add(op.identifier());
428                        }
429
430                        o.put("key_ops", sl);
431                }
432
433                if (alg != null) {
434                        o.put("alg", alg.getName());
435                }
436
437                if (kid != null) {
438                        o.put("kid", kid);
439                }
440
441                if (x5u != null) {
442                        o.put("x5u", x5u.toString());
443                }
444
445                if (x5t != null) {
446                        o.put("x5t", x5t.toString());
447                }
448                
449                if (x5t256 != null) {
450                        o.put("x5t#S256", x5t256.toString());
451                }
452
453                if (x5c != null) {
454                        o.put("x5c", x5c);
455                }
456
457                return o;
458        }
459
460
461        /**
462         * Returns the JSON object string representation of this JWK.
463         *
464         * @return The JSON object string representation.
465         */
466        @Override
467        public String toJSONString() {
468
469                return toJSONObject().toString();
470        }
471
472
473        /**
474         * @see #toJSONString
475         */
476        @Override
477        public String toString() {
478
479                return toJSONObject().toString();
480        }
481
482
483        /**
484         * Parses a JWK from the specified JSON object string representation. 
485         * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 
486         * {@link OctetSequenceKey}.
487         *
488         * @param s The JSON object string to parse. Must not be {@code null}.
489         *
490         * @return The JWK.
491         *
492         * @throws ParseException If the string couldn't be parsed to a
493         *                        supported JWK.
494         */
495        public static JWK parse(final String s)
496                throws ParseException {
497
498                return parse(JSONObjectUtils.parse(s));
499        }
500
501
502        /**
503         * Parses a JWK from the specified JSON object representation. The JWK 
504         * must be an {@link ECKey}, an {@link RSAKey}, or a 
505         * {@link OctetSequenceKey}.
506         *
507         * @param jsonObject The JSON object to parse. Must not be 
508         *                   {@code null}.
509         *
510         * @return The JWK.
511         *
512         * @throws ParseException If the JSON object couldn't be parsed to a 
513         *                        supported JWK.
514         */
515        public static JWK parse(final JSONObject jsonObject)
516                throws ParseException {
517
518                KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
519
520                if (kty == KeyType.EC) {
521                        
522                        return ECKey.parse(jsonObject);
523
524                } else if (kty == KeyType.RSA) {
525                        
526                        return RSAKey.parse(jsonObject);
527
528                } else if (kty == KeyType.OCT) {
529                        
530                        return OctetSequenceKey.parse(jsonObject);
531
532                } else {
533
534                        throw new ParseException("Unsupported key type \"kty\" parameter: " + kty, 0);
535                }
536        }
537        
538        
539        /**
540         * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the
541         * specified X.509 certificate. Requires BouncyCastle.
542         *
543         * <p><strong>Important:</strong> The X.509 certificate is not
544         * validated!
545         *
546         * <p>Sets the following JWK parameters:
547         *
548         * <ul>
549         *     <li>For an EC key the curve is obtained from the subject public
550         *         key info algorithm parameters.
551         *     <li>The JWK use inferred by {@link KeyUse#from}.
552         *     <li>The JWK ID from the X.509 serial number (in base 10).
553         *     <li>The JWK X.509 certificate chain (this certificate only).
554         *     <li>The JWK X.509 certificate SHA-256 thumbprint.
555         * </ul>
556         *
557         * @param cert The X.509 certificate. Must not be {@code null}.
558         *
559         * @return The public RSA or EC JWK.
560         *
561         * @throws JOSEException If parsing failed.
562         */
563        public static JWK parse(final X509Certificate cert)
564                throws JOSEException {
565                
566                if (cert.getPublicKey() instanceof RSAPublicKey) {
567                        return RSAKey.parse(cert);
568                } else if (cert.getPublicKey() instanceof ECPublicKey) {
569                        return ECKey.parse(cert);
570                } else {
571                        throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm());
572                }
573        }
574        
575        
576        /**
577         * Loads a JWK from the specified JCE key store. The JWK can be a
578         * public / private {@link RSAKey RSA key}, a public / private
579         * {@link ECKey EC key}, or a {@link OctetSequenceKey secret key}.
580         * Requires BouncyCastle.
581         *
582         * <p><strong>Important:</strong> The X.509 certificate is not
583         * validated!
584         *
585         * @param keyStore The key store. Must not be {@code null}.
586         * @param alias    The alias. Must not be {@code null}.
587         * @param pin      The pin to unlock the private key if any, empty or
588         *                 {@code null} if not required.
589         *
590         * @return The public / private RSA or EC JWK, or secret JWK, or
591         *         {@code null} if no key with the specified alias was found.
592         *
593         * @throws KeyStoreException On a key store exception.
594         * @throws JOSEException     If RSA or EC key loading failed.
595         */
596        public static JWK load(final KeyStore keyStore, final String alias, final char[] pin)
597                throws KeyStoreException, JOSEException {
598                
599                java.security.cert.Certificate cert = keyStore.getCertificate(alias);
600                
601                if (cert == null) {
602                        // Try secret key
603                        return OctetSequenceKey.load(keyStore, alias, pin);
604                }
605                
606                if (cert.getPublicKey() instanceof RSAPublicKey) {
607                        return RSAKey.load(keyStore, alias, pin);
608                } else if (cert.getPublicKey() instanceof ECPublicKey) {
609                        return ECKey.load(keyStore, alias, pin);
610                } else {
611                        throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm());
612                }
613        }
614}