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 com.nimbusds.jose.Algorithm;
022import com.nimbusds.jose.JOSEException;
023import com.nimbusds.jose.util.Base64;
024import com.nimbusds.jose.util.*;
025import com.nimbusds.jwt.util.DateUtils;
026
027import java.io.Serializable;
028import java.net.URI;
029import java.security.*;
030import java.security.cert.X509Certificate;
031import java.security.interfaces.ECPrivateKey;
032import java.security.interfaces.ECPublicKey;
033import java.security.interfaces.RSAPrivateKey;
034import java.security.interfaces.RSAPublicKey;
035import java.security.spec.ECParameterSpec;
036import java.text.ParseException;
037import java.util.*;
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 *     <li>{@link #getX509CertURL()  x5u} (optional)
052 *     <li>{@link #getX509CertThumbprint()  x5t} (optional)
053 *     <li>{@link #getX509CertSHA256Thumbprint()  x5t#S256} (optional)
054 *     <li>{@link #getX509CertChain() x5c} (optional)
055 *     <li>{@link #getExpirationTime() exp} (optional)
056 *     <li>{@link #getNotBeforeTime() nbf} (optional)
057 *     <li>{@link #getIssueTime() iat} (optional)
058 *     <li>{@link #getKeyRevocation() revoked} (optional)
059 *     <li>{@link #getKeyStore()}
060 * </ul>
061 *
062 * <p>Example JWK (of the Elliptic Curve type):
063 *
064 * <pre>
065 * {
066 *   "kty" : "EC",
067 *   "crv" : "P-256",
068 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
069 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
070 *   "use" : "enc",
071 *   "kid" : "1"
072 * }
073 * </pre>
074 *
075 * @author Vladimir Dzhuvinov
076 * @author Justin Richer
077 * @author Stefan Larsson
078 * @version 2024-04-27
079 */
080public abstract class JWK implements Serializable {
081
082
083        private static final long serialVersionUID = 1L;
084
085
086        /**
087         * The MIME type of JWK objects: 
088         * {@code application/jwk+json; charset=UTF-8}
089         */
090        public static final String MIME_TYPE = "application/jwk+json; charset=UTF-8";
091
092
093        /**
094         * The key type, required.
095         */
096        private final KeyType kty;
097
098
099        /**
100         * The key use, optional.
101         */
102        private final KeyUse use;
103
104
105        /**
106         * The key operations, optional.
107         */
108        private final Set<KeyOperation> ops;
109
110
111        /**
112         * The intended JOSE algorithm for the key, optional.
113         */
114        private final Algorithm alg;
115
116
117        /**
118         * The key ID, optional.
119         */
120        private final String kid;
121
122
123        /**
124         * X.509 certificate URL, optional.
125         */
126        private final URI x5u;
127
128
129        /**
130         * X.509 certificate SHA-1 thumbprint, optional.
131         */
132        @Deprecated
133        private final Base64URL x5t;
134        
135        
136        /**
137         * X.509 certificate SHA-256 thumbprint, optional.
138         */
139        private final Base64URL x5t256;
140
141
142        /**
143         * The X.509 certificate chain, optional.
144         */
145        private final List<Base64> x5c;
146        
147        
148        /**
149         * The key expiration time, optional.
150         */
151        private final Date exp;
152        
153        
154        /**
155         * The key not-before time, optional.
156         */
157        private final Date nbf;
158        
159        
160        /**
161         * The key issued-at time, optional.
162         */
163        private final Date iat;
164
165
166        /**
167         * The key revocation, optional.
168         */
169        private final KeyRevocation revocation;
170        
171        
172        /**
173         * The parsed X.509 certificate chain, optional.
174         */
175        private final List<X509Certificate> parsedX5c;
176        
177        
178        /**
179         * Reference to the underlying key store, {@code null} if none.
180         */
181        private final KeyStore keyStore;
182
183
184        /**
185         * Creates a new JSON Web Key (JWK).
186         *
187         * @param kty    The key type. Must not be {@code null}.
188         * @param use    The key use, {@code null} if not specified or if the
189         *               key is intended for signing as well as encryption.
190         * @param ops    The key operations, {@code null} if not specified.
191         * @param alg    The intended JOSE algorithm for the key, {@code null}
192         *               if not specified.
193         * @param kid    The key ID, {@code null} if not specified.
194         * @param x5u    The X.509 certificate URL, {@code null} if not
195         *               specified.
196         * @param x5t    The X.509 certificate thumbprint, {@code null} if not
197         *               specified.
198         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
199         *               if not specified.
200         * @param x5c    The X.509 certificate chain, {@code null} if not
201         *               specified.
202         * @param ks     Reference to the underlying key store, {@code null} if
203         *               none.
204         */
205        @Deprecated
206        protected JWK(final KeyType kty,
207                      final KeyUse use,
208                      final Set<KeyOperation> ops,
209                      final Algorithm alg,
210                      final String kid,
211                      final URI x5u,
212                      final Base64URL x5t,
213                      final Base64URL x5t256,
214                      final List<Base64> x5c,
215                      final KeyStore ks) {
216
217                this(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, null, null, null, ks);
218        }
219
220
221        /**
222         * Creates a new JSON Web Key (JWK).
223         *
224         * @param kty    The key type. Must not be {@code null}.
225         * @param use    The key use, {@code null} if not specified or if the
226         *               key is intended for signing as well as encryption.
227         * @param ops    The key operations, {@code null} if not specified.
228         * @param alg    The intended JOSE algorithm for the key, {@code null}
229         *               if not specified.
230         * @param kid    The key ID, {@code null} if not specified.
231         * @param x5u    The X.509 certificate URL, {@code null} if not
232         *               specified.
233         * @param x5t    The X.509 certificate thumbprint, {@code null} if not
234         *               specified.
235         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
236         *               if not specified.
237         * @param x5c    The X.509 certificate chain, {@code null} if not
238         *               specified.
239         * @param exp    The key expiration time, {@code null} if not
240         *               specified.
241         * @param nbf    The key not-before time, {@code null} if not
242         *               specified.
243         * @param iat    The key issued-at time, {@code null} if not specified.
244         * @param ks     Reference to the underlying key store, {@code null} if
245         *               none.
246         */
247        @Deprecated
248        protected JWK(final KeyType kty,
249                      final KeyUse use,
250                      final Set<KeyOperation> ops,
251                      final Algorithm alg,
252                      final String kid,
253                      final URI x5u,
254                      final Base64URL x5t,
255                      final Base64URL x5t256,
256                      final List<Base64> x5c,
257                      final Date exp,
258                      final Date nbf,
259                      final Date iat,
260                      final KeyStore ks) {
261
262                this(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, null, ks);
263        }
264
265
266        /**
267         * Creates a new JSON Web Key (JWK).
268         *
269         * @param kty        The key type. Must not be {@code null}.
270         * @param use        The key use, {@code null} if not specified or if
271         *                   the key is intended for signing as well as
272         *                   encryption.
273         * @param ops        The key operations, {@code null} if not specified.
274         * @param alg        The intended JOSE algorithm for the key,
275         *                   {@code null} if not specified.
276         * @param kid        The key ID, {@code null} if not specified.
277         * @param x5u        The X.509 certificate URL, {@code null} if not
278         *                   specified.
279         * @param x5t        The X.509 certificate thumbprint, {@code null} if
280         *                   not specified.
281         * @param x5t256     The X.509 certificate SHA-256 thumbprint,
282         *                   {@code null} if not specified.
283         * @param x5c        The X.509 certificate chain, {@code null} if not
284         *                   specified.
285         * @param exp        The key expiration time, {@code null} if not
286         *                   specified.
287         * @param nbf        The key not-before time, {@code null} if not
288         *                   specified.
289         * @param iat        The key issued-at time, {@code null} if not
290         *                   specified.
291         * @param revocation The key revocation, {@code null} if not specified.
292         * @param ks         Reference to the underlying key store,
293         *                   {@code null} if none.
294         */
295        protected JWK(final KeyType kty,
296                      final KeyUse use,
297                      final Set<KeyOperation> ops,
298                      final Algorithm alg,
299                      final String kid,
300                      final URI x5u,
301                      final Base64URL x5t,
302                      final Base64URL x5t256,
303                      final List<Base64> x5c,
304                      final Date exp,
305                      final Date nbf,
306                      final Date iat,
307                      final KeyRevocation revocation,
308                      final KeyStore ks) {
309
310                this.kty = Objects.requireNonNull(kty, "The key type \"" + JWKParameterNames.KEY_TYPE + "\" parameter must not be null");
311
312                if (! KeyUseAndOpsConsistency.areConsistent(use, ops)) {
313                        throw new IllegalArgumentException("The key use \"" + JWKParameterNames.PUBLIC_KEY_USE + "\" and key options \"" + JWKParameterNames.KEY_OPS + "\" parameters are not consistent, " +
314                                "see RFC 7517, section 4.3");
315                }
316
317                this.use = use;
318                this.ops = ops;
319
320                this.alg = alg;
321                this.kid = kid;
322
323                this.x5u = x5u;
324                this.x5t = x5t;
325                this.x5t256 = x5t256;
326
327                if (x5c != null && x5c.isEmpty()) {
328                        throw new IllegalArgumentException("The X.509 certificate chain \"" + JWKParameterNames.X_509_CERT_CHAIN + "\" must not be empty");
329                }
330                this.x5c = x5c;
331
332                try {
333                        parsedX5c = X509CertChainUtils.parse(x5c);
334                } catch (ParseException e) {
335                        throw new IllegalArgumentException("Invalid X.509 certificate chain \"" + JWKParameterNames.X_509_CERT_CHAIN + "\": " + e.getMessage(), e);
336                }
337
338                this.exp = exp;
339                this.nbf = nbf;
340                this.iat = iat;
341                this.revocation = revocation;
342
343                this.keyStore = ks;
344        }
345
346
347        /**
348         * Returns the type ({@code kty}) of this JWK.
349         *
350         * @return The key type.
351         */
352        public KeyType getKeyType() {
353
354                return kty;
355        }
356
357
358        /**
359         * Returns the use ({@code use}) of this JWK.
360         *
361         * @return The key use, {@code null} if not specified or if the key is
362         *         intended for signing as well as encryption.
363         */
364        public KeyUse getKeyUse() {
365
366                return use;
367        }
368
369
370        /**
371         * Returns the operations ({@code key_ops}) for this JWK.
372         *
373         * @return The key operations, {@code null} if not specified.
374         */
375        public Set<KeyOperation> getKeyOperations() {
376
377                return ops;
378        }
379
380
381        /**
382         * Returns the intended JOSE algorithm ({@code alg}) for this JWK.
383         *
384         * @return The intended JOSE algorithm, {@code null} if not specified.
385         */
386        public Algorithm getAlgorithm() {
387
388                return alg;
389        }
390
391
392        /**
393         * Returns the ID ({@code kid}) of this JWK. The key ID can be used to
394         * match a specific key. This can be used, for instance, to choose a 
395         * key within a {@link JWKSet} during key rollover. The key ID may also 
396         * correspond to a JWS/JWE {@code kid} header parameter value.
397         *
398         * @return The key ID, {@code null} if not specified.
399         */
400        public String getKeyID() {
401
402                return kid;
403        }
404
405
406        /**
407         * Returns the X.509 certificate URL ({@code x5u}) of this JWK.
408         *
409         * @return The X.509 certificate URL, {@code null} if not specified.
410         */
411        public URI getX509CertURL() {
412
413                return x5u;
414        }
415
416
417        /**
418         * Returns the X.509 certificate SHA-1 thumbprint ({@code x5t}) of this
419         * JWK.
420         *
421         * @return The X.509 certificate SHA-1 thumbprint, {@code null} if not
422         *         specified.
423         */
424        @Deprecated
425        public Base64URL getX509CertThumbprint() {
426
427                return x5t;
428        }
429        
430        
431        /**
432         * Returns the X.509 certificate SHA-256 thumbprint ({@code x5t#S256})
433         * of this JWK.
434         *
435         * @return The X.509 certificate SHA-256 thumbprint, {@code null} if
436         *         not specified.
437         */
438        public Base64URL getX509CertSHA256Thumbprint() {
439                
440                return x5t256;
441        }
442
443
444        /**
445         * Returns the X.509 certificate chain ({@code x5c}) of this JWK.
446         *
447         * @return The X.509 certificate chain as a unmodifiable list,
448         *         {@code null} if not specified.
449         */
450        public List<Base64> getX509CertChain() {
451
452                if (x5c == null) {
453                        return null;
454                }
455
456                return Collections.unmodifiableList(x5c);
457        }
458        
459        
460        /**
461         * Returns the parsed X.509 certificate chain ({@code x5c}) of this
462         * JWK.
463         *
464         * @return The X.509 certificate chain as a unmodifiable list,
465         *         {@code null} if not specified.
466         */
467        public List<X509Certificate> getParsedX509CertChain() {
468                
469                if (parsedX5c == null) {
470                        return null;
471                }
472                
473                return Collections.unmodifiableList(parsedX5c);
474        }
475        
476        
477        /**
478         * Returns the expiration time ({@code exp}) if this JWK.
479         *
480         * @return The expiration time, {@code null} if not specified.
481         */
482        public Date getExpirationTime() {
483                
484                return exp;
485        }
486        
487        
488        /**
489         * Returns the not-before ({@code nbf}) of this JWK.
490         *
491         * @return The not-before time, {@code null} if not specified.
492         */
493        public Date getNotBeforeTime() {
494                
495                return nbf;
496        }
497        
498        
499        /**
500         * Returns the issued-at ({@code iat}) time of this JWK.
501         *
502         * @return The issued-at time, {@code null} if not specified.
503         */
504        public Date getIssueTime() {
505                
506                return iat;
507        }
508
509
510        /**
511         * Returns the key revocation ({@code revoked}) of this JWK.
512         *
513         * @return The key revocation, {@code null} if not specified.
514         */
515        public KeyRevocation getKeyRevocation() {
516                return revocation;
517        }
518
519
520        /**
521         * Returns a reference to the underlying key store.
522         *
523         * @return The underlying key store, {@code null} if none.
524         */
525        public KeyStore getKeyStore() {
526                
527                return keyStore;
528        }
529
530
531        /**
532         * Returns the required JWK parameters. Intended as input for JWK
533         * thumbprint computation. See RFC 7638 for more information.
534         *
535         * @return The required JWK parameters, sorted alphanumerically by key
536         *         name and ready for JSON serialisation.
537         */
538        public abstract LinkedHashMap<String,?> getRequiredParams();
539
540
541        /**
542         * Computes the SHA-256 thumbprint of this JWK. See RFC 7638 for more
543         * information.
544         *
545         * @return The SHA-256 thumbprint.
546         *
547         * @throws JOSEException If the SHA-256 hash algorithm is not
548         *                       supported.
549         */
550        public Base64URL computeThumbprint()
551                throws JOSEException {
552
553                return computeThumbprint("SHA-256");
554                
555        }
556        
557        
558        /**
559         * Computes the thumbprint of this JWK using the specified hash
560         * algorithm. See RFC 7638 for more information.
561         *
562         * @param hashAlg The hash algorithm. Must not be {@code null}.
563         *
564         * @return The SHA-256 thumbprint.
565         *
566         * @throws JOSEException If the hash algorithm is not supported.
567         */
568        public Base64URL computeThumbprint(final String hashAlg)
569                throws JOSEException {
570
571                return ThumbprintUtils.compute(hashAlg, this);
572        }
573        
574        
575        /**
576         * Computes the SHA-256 thumbprint URI of this JWK. See RFC 7638 and
577         * draft-ietf-oauth-jwk-thumbprint-uri for more information.
578         *
579         * @return The SHA-256 thumbprint URI.
580         *
581         * @throws JOSEException If the SHA-256 hash algorithm is not
582         *                       supported.
583         */
584        public ThumbprintURI computeThumbprintURI()
585                throws JOSEException {
586                
587                return new ThumbprintURI("sha-256", computeThumbprint("SHA-256"));
588        }
589
590
591        /**
592         * Returns {@code true} if this JWK contains private or sensitive
593         * (non-public) parameters.
594         *
595         * @return {@code true} if this JWK contains private parameters, else
596         *         {@code false}.
597         */
598        public abstract boolean isPrivate();
599
600
601        /**
602         * Creates a copy of this JWK with all private or sensitive parameters 
603         * removed.
604         * 
605         * @return The newly created public JWK, or {@code null} if none can be
606         *         created.
607         */
608        public abstract JWK toPublicJWK();
609
610
611        /**
612         * Returns the size of this JWK.
613         *
614         * @return The JWK size, in bits.
615         */
616        public abstract int size();
617        
618        
619        /**
620         * Casts this JWK to an RSA JWK.
621         *
622         * @return The RSA JWK.
623         */
624        public RSAKey toRSAKey() {
625                return (RSAKey)this;
626        }
627        
628        
629        /**
630         * Casts this JWK to an EC JWK.
631         *
632         * @return The EC JWK.
633         */
634        public ECKey toECKey() {
635                return (ECKey)this;
636        }
637        
638        
639        /**
640         * Casts this JWK to an octet sequence JWK.
641         *
642         * @return The octet sequence JWK.
643         */
644        public OctetSequenceKey toOctetSequenceKey() {
645                return (OctetSequenceKey)this;
646        }
647        
648        
649        /**
650         * Casts this JWK to an octet key pair JWK.
651         *
652         * @return The octet key pair JWK.
653         */
654        public OctetKeyPair toOctetKeyPair() {
655                return (OctetKeyPair)this;
656        }
657
658
659        /**
660         * Returns a JSON object representation of this JWK. This method is 
661         * intended to be called from extending classes.
662         *
663         * <p>Example:
664         *
665         * <pre>
666         * {
667         *   "kty" : "RSA",
668         *   "use" : "sig",
669         *   "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b"
670         * }
671         * </pre>
672         *
673         * @return The JSON object representation.
674         */
675        public Map<String, Object> toJSONObject() {
676
677                Map<String, Object> o = JSONObjectUtils.newJSONObject();
678
679                o.put(JWKParameterNames.KEY_TYPE, kty.getValue());
680
681                if (use != null) {
682                        o.put(JWKParameterNames.PUBLIC_KEY_USE, use.identifier());
683                }
684
685                if (ops != null) {
686                        List<Object> stringValues = JSONArrayUtils.newJSONArray();
687                        for (KeyOperation op: ops) {
688                                stringValues.add(op.identifier());
689                        }
690                        o.put(JWKParameterNames.KEY_OPS, stringValues);
691                }
692
693                if (alg != null) {
694                        o.put(JWKParameterNames.ALGORITHM, alg.getName());
695                }
696
697                if (kid != null) {
698                        o.put(JWKParameterNames.KEY_ID, kid);
699                }
700
701                if (x5u != null) {
702                        o.put(JWKParameterNames.X_509_CERT_URL, x5u.toString());
703                }
704
705                if (x5t != null) {
706                        o.put(JWKParameterNames.X_509_CERT_SHA_1_THUMBPRINT, x5t.toString());
707                }
708                
709                if (x5t256 != null) {
710                        o.put(JWKParameterNames.X_509_CERT_SHA_256_THUMBPRINT, x5t256.toString());
711                }
712
713                if (x5c != null) {
714                        List<Object> stringValues = JSONArrayUtils.newJSONArray();
715                        for (Base64 base64: x5c) {
716                                stringValues.add(base64.toString());
717                        }
718                        o.put(JWKParameterNames.X_509_CERT_CHAIN, stringValues);
719                }
720                
721                if (exp != null) {
722                        o.put(JWKParameterNames.EXPIRATION_TIME, DateUtils.toSecondsSinceEpoch(exp));
723                }
724                
725                if (nbf != null) {
726                        o.put(JWKParameterNames.NOT_BEFORE, DateUtils.toSecondsSinceEpoch(nbf));
727                }
728                
729                if (iat != null) {
730                        o.put(JWKParameterNames.ISSUED_AT, DateUtils.toSecondsSinceEpoch(iat));
731                }
732
733                if (revocation != null) {
734                        o.put(JWKParameterNames.REVOKED, revocation.toJSONObject());
735                }
736
737                return o;
738        }
739
740
741        /**
742         * Returns the JSON object string representation of this JWK.
743         *
744         * @return The JSON object string representation.
745         */
746        public String toJSONString() {
747                return JSONObjectUtils.toJSONString(toJSONObject());
748        }
749
750
751        /**
752         * @see #toJSONString
753         */
754        @Override
755        public String toString() {
756
757                return JSONObjectUtils.toJSONString(toJSONObject());
758        }
759
760
761        /**
762         * Parses a JWK from the specified JSON object string representation. 
763         * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 
764         * {@link OctetSequenceKey}.
765         *
766         * @param s The JSON object string to parse. Must not be {@code null}.
767         *
768         * @return The JWK.
769         *
770         * @throws ParseException If the string couldn't be parsed to a
771         *                        supported JWK.
772         */
773        public static JWK parse(final String s)
774                throws ParseException {
775
776                return parse(JSONObjectUtils.parse(s));
777        }
778
779
780        /**
781         * Parses a JWK from the specified JSON object representation. The JWK 
782         * must be an {@link ECKey}, an {@link RSAKey}, or a 
783         * {@link OctetSequenceKey}.
784         *
785         * @param jsonObject The JSON object to parse. Must not be 
786         *                   {@code null}.
787         *
788         * @return The JWK.
789         *
790         * @throws ParseException If the JSON object couldn't be parsed to a 
791         *                        supported JWK.
792         */
793        public static JWK parse(final Map<String, Object> jsonObject)
794                throws ParseException {
795                
796                String ktyString = JSONObjectUtils.getString(jsonObject, JWKParameterNames.KEY_TYPE);
797                
798                if (ktyString == null) {
799                        throw new ParseException("Missing key type \"kty\" parameter", 0);
800                }
801
802                KeyType kty = KeyType.parse(ktyString);
803
804                if (kty == KeyType.EC) {
805                        
806                        return ECKey.parse(jsonObject);
807
808                } else if (kty == KeyType.RSA) {
809                        
810                        return RSAKey.parse(jsonObject);
811
812                } else if (kty == KeyType.OCT) {
813                        
814                        return OctetSequenceKey.parse(jsonObject);
815                        
816                } else if (kty == KeyType.OKP) {
817                        
818                        return OctetKeyPair.parse(jsonObject);
819
820                } else {
821
822                        throw new ParseException("Unsupported key type \"" + JWKParameterNames.KEY_TYPE + "\" parameter: " + kty, 0);
823                }
824        }
825        
826        
827        /**
828         * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the
829         * specified X.509 certificate. Requires BouncyCastle.
830         *
831         * <p><strong>Important:</strong> The X.509 certificate is not
832         * validated!
833         *
834         * <p>Sets the following JWK parameters:
835         *
836         * <ul>
837         *     <li>For an EC key the curve is obtained from the subject public
838         *         key info algorithm parameters.
839         *     <li>The JWK use inferred by {@link KeyUse#from}.
840         *     <li>The JWK ID from the X.509 serial number (in base 10).
841         *     <li>The JWK X.509 certificate chain (this certificate only).
842         *     <li>The JWK X.509 certificate SHA-256 thumbprint.
843         * </ul>
844         *
845         * @param cert The X.509 certificate. Must not be {@code null}.
846         *
847         * @return The public RSA or EC JWK.
848         *
849         * @throws JOSEException If parsing failed.
850         */
851        public static JWK parse(final X509Certificate cert)
852                throws JOSEException {
853                
854                if (cert.getPublicKey() instanceof RSAPublicKey) {
855                        return RSAKey.parse(cert);
856                } else if (cert.getPublicKey() instanceof ECPublicKey) {
857                        return ECKey.parse(cert);
858                } else {
859                        throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm());
860                }
861        }
862        
863        
864        /**
865         * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the
866         * specified PEM-encoded X.509 certificate. Requires BouncyCastle.
867         *
868         * <p><strong>Important:</strong> The X.509 certificate is not
869         * validated!
870         *
871         * <p>Sets the following JWK parameters:
872         *
873         * <ul>
874         *     <li>For an EC key the curve is obtained from the subject public
875         *         key info algorithm parameters.
876         *     <li>The JWK use inferred by {@link KeyUse#from}.
877         *     <li>The JWK ID from the X.509 serial number (in base 10).
878         *     <li>The JWK X.509 certificate chain (this certificate only).
879         *     <li>The JWK X.509 certificate SHA-256 thumbprint.
880         * </ul>
881         *
882         * @param pemEncodedCert The PEM-encoded X.509 certificate. Must not be
883         *                       {@code null}.
884         *
885         * @return The public RSA or EC JWK.
886         *
887         * @throws JOSEException If parsing failed.
888         */
889        public static JWK parseFromPEMEncodedX509Cert(final String pemEncodedCert)
890                throws JOSEException {
891                
892                X509Certificate cert = X509CertUtils.parse(pemEncodedCert);
893                
894                if (cert == null) {
895                        throw new JOSEException("Couldn't parse PEM-encoded X.509 certificate");
896                }
897                
898                return parse(cert);
899        }
900        
901        
902        /**
903         * Loads a JWK from the specified JCE key store. The JWK can be a
904         * public / private {@link RSAKey RSA key}, a public / private
905         * {@link ECKey EC key}, or a {@link OctetSequenceKey secret key}.
906         * Requires BouncyCastle.
907         *
908         * <p><strong>Important:</strong> The X.509 certificate is not
909         * validated!
910         *
911         * @param keyStore The key store. Must not be {@code null}.
912         * @param alias    The alias. Must not be {@code null}.
913         * @param pin      The pin to unlock the private key if any, empty or
914         *                 {@code null} if not required.
915         *
916         * @return The public / private RSA or EC JWK, or secret JWK, or
917         *         {@code null} if no key with the specified alias was found.
918         *
919         * @throws KeyStoreException On a key store exception.
920         * @throws JOSEException     If RSA or EC key loading failed.
921         */
922        public static JWK load(final KeyStore keyStore, final String alias, final char[] pin)
923                throws KeyStoreException, JOSEException {
924                
925                java.security.cert.Certificate cert = keyStore.getCertificate(alias);
926                
927                if (cert == null) {
928                        // Try secret key
929                        return OctetSequenceKey.load(keyStore, alias, pin);
930                }
931                
932                if (cert.getPublicKey() instanceof RSAPublicKey) {
933                        return RSAKey.load(keyStore, alias, pin);
934                } else if (cert.getPublicKey() instanceof ECPublicKey) {
935                        return ECKey.load(keyStore, alias, pin);
936                } else {
937                        throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm());
938                }
939        }
940
941        /**
942         * Parses an RSA or EC JWK from the specified string of one or more
943         * PEM-encoded object(s):
944         *
945         * <ul>
946         *     <li>X.509 certificate (PEM header: BEGIN CERTIFICATE)
947         *     <li>PKCS#1 RSAPublicKey (PEM header: BEGIN RSA PUBLIC KEY)
948         *     <li>X.509 SubjectPublicKeyInfo (PEM header: BEGIN PUBLIC KEY)
949         *     <li>PKCS#1 RSAPrivateKey (PEM header: BEGIN RSA PRIVATE KEY)
950         *     <li>PKCS#8 PrivateKeyInfo (PEM header: BEGIN PRIVATE KEY)
951         *     <li>matching pair of the above
952         * </ul>
953         *
954         * <p>Requires BouncyCastle.
955         *
956         * @param pemEncodedObjects The string of PEM-encoded object(s).
957         *
958         * @return The public / (private) RSA or EC JWK.
959         *
960         * @throws JOSEException If RSA or EC key parsing failed.
961         */
962        public static JWK parseFromPEMEncodedObjects(final String pemEncodedObjects)
963                throws JOSEException {
964                
965                final List<KeyPair> keys = PEMEncodedKeyParser.parseKeys(pemEncodedObjects);
966                if (keys.isEmpty()) {
967                        throw new JOSEException("No PEM-encoded keys found");
968                }
969
970                final KeyPair pair = mergeKeyPairs(keys);
971
972                final PublicKey publicKey = pair.getPublic();
973                final PrivateKey privateKey = pair.getPrivate();
974                
975                if (publicKey == null) {
976                        // For EC keys, for RSA the public can be reconstructed
977                        throw new JOSEException("Missing PEM-encoded public key to construct JWK");
978                }
979
980                if (publicKey instanceof ECPublicKey) {
981                        final ECPublicKey ecPubKey = (ECPublicKey) publicKey;
982                        final ECParameterSpec pubParams = ecPubKey.getParams();
983
984                        if (privateKey instanceof ECPrivateKey) {
985                                validateEcCurves(ecPubKey, (ECPrivateKey) privateKey);
986                        }
987                        if (privateKey != null && !(privateKey instanceof ECPrivateKey)) {
988                                throw new JOSEException("Unsupported " + KeyType.EC.getValue() + " private key type: " + privateKey);
989                        }
990
991                        final Curve curve = Curve.forECParameterSpec(pubParams);
992                        final ECKey.Builder builder = new ECKey.Builder(curve, (ECPublicKey) publicKey);
993
994                        if (privateKey != null) {
995                                builder.privateKey((ECPrivateKey) privateKey);
996                        }
997                        return builder.build();
998                }
999
1000                if (publicKey instanceof RSAPublicKey) {
1001                        final RSAKey.Builder builder = new RSAKey.Builder((RSAPublicKey) publicKey);
1002                        if (privateKey instanceof RSAPrivateKey) {
1003                                builder.privateKey((RSAPrivateKey) privateKey);
1004                        } else if (privateKey != null) {
1005                                throw new JOSEException("Unsupported " + KeyType.RSA.getValue() + " private key type: " + privateKey);
1006                        }
1007                        return builder.build();
1008                }
1009
1010                throw new JOSEException("Unsupported algorithm of PEM-encoded key: " + publicKey.getAlgorithm());
1011        }
1012        
1013
1014        private static void validateEcCurves(ECPublicKey publicKey, ECPrivateKey privateKey) throws JOSEException {
1015                final ECParameterSpec pubParams = publicKey.getParams();
1016                final ECParameterSpec privParams = privateKey.getParams();
1017                if (!pubParams.getCurve().equals(privParams.getCurve())) {
1018                        throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key curve mismatch: " + publicKey);
1019                }
1020                if (pubParams.getCofactor() != privParams.getCofactor()) {
1021                        throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key cofactor mismatch: " + publicKey);
1022                }
1023                if (!pubParams.getGenerator().equals(privParams.getGenerator())) {
1024                        throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key generator mismatch: " + publicKey);
1025                }
1026                if (!pubParams.getOrder().equals(privParams.getOrder())) {
1027                        throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key order mismatch: " + publicKey);
1028                }
1029        }
1030
1031        
1032        private static KeyPair mergeKeyPairs(final List<KeyPair> keys) throws JOSEException {
1033                final KeyPair pair;
1034                if (keys.size() == 1) {
1035                        // Assume public key, or private key easy to convert to public,
1036                        // otherwise not representable as a JWK
1037                        pair = keys.get(0);
1038                } else if (keys.size() == 2) {
1039                        // If two keys, assume public + private keys separated
1040                        pair = twoKeysToKeyPair(keys);
1041                } else {
1042                        throw new JOSEException("Expected key or pair of PEM-encoded keys");
1043                }
1044                return pair;
1045        }
1046
1047        
1048        private static KeyPair twoKeysToKeyPair(final List<? extends KeyPair> keys) throws JOSEException {
1049                final KeyPair key1 = keys.get(0);
1050                final KeyPair key2 = keys.get(1);
1051                if (key1.getPublic() != null && key2.getPrivate() != null) {
1052                        return new KeyPair(key1.getPublic(), key2.getPrivate());
1053                } else if (key1.getPrivate() != null && key2.getPublic() != null) {
1054                        return new KeyPair(key2.getPublic(), key1.getPrivate());
1055                } else {
1056                        throw new JOSEException("Not a public/private key pair");
1057                }
1058        }
1059
1060        
1061        @Override
1062        public boolean equals(Object o) {
1063                if (this == o) return true;
1064                if (!(o instanceof JWK)) return false;
1065                JWK jwk = (JWK) o;
1066                return Objects.equals(kty, jwk.kty) &&
1067                                Objects.equals(use, jwk.use) &&
1068                                Objects.equals(ops, jwk.ops) &&
1069                                Objects.equals(alg, jwk.alg) &&
1070                                Objects.equals(kid, jwk.kid) &&
1071                                Objects.equals(x5u, jwk.x5u) &&
1072                                Objects.equals(x5t, jwk.x5t) &&
1073                                Objects.equals(x5t256, jwk.x5t256) &&
1074                                Objects.equals(x5c, jwk.x5c) &&
1075                                Objects.equals(exp, jwk.exp) &&
1076                                Objects.equals(nbf, jwk.nbf) &&
1077                                Objects.equals(iat, jwk.iat) &&
1078                                Objects.equals(revocation, jwk.revocation) &&
1079                                Objects.equals(keyStore, jwk.keyStore);
1080        }
1081
1082        
1083        @Override
1084        public int hashCode() {
1085                return Objects.hash(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, revocation, keyStore);
1086        }
1087}