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