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-10-31
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         * Creates a copy of this JWK with the specified key revocation.
613         *
614         * @param keyRevocation The key revocation. Must not be {@code null}.
615         *
616         * @return The new JWK with the specified revocation.
617         *
618         * @throws IllegalStateException If the JWK is already revoked.
619         */
620        public abstract JWK toRevokedJWK(final KeyRevocation keyRevocation);
621
622
623        /**
624         * Returns the size of this JWK.
625         *
626         * @return The JWK size, in bits.
627         */
628        public abstract int size();
629        
630        
631        /**
632         * Casts this JWK to an RSA JWK.
633         *
634         * @return The RSA JWK.
635         */
636        public RSAKey toRSAKey() {
637                return (RSAKey)this;
638        }
639        
640        
641        /**
642         * Casts this JWK to an EC JWK.
643         *
644         * @return The EC JWK.
645         */
646        public ECKey toECKey() {
647                return (ECKey)this;
648        }
649        
650        
651        /**
652         * Casts this JWK to an octet sequence JWK.
653         *
654         * @return The octet sequence JWK.
655         */
656        public OctetSequenceKey toOctetSequenceKey() {
657                return (OctetSequenceKey)this;
658        }
659        
660        
661        /**
662         * Casts this JWK to an octet key pair JWK.
663         *
664         * @return The octet key pair JWK.
665         */
666        public OctetKeyPair toOctetKeyPair() {
667                return (OctetKeyPair)this;
668        }
669
670
671        /**
672         * Returns a JSON object representation of this JWK. This method is 
673         * intended to be called from extending classes.
674         *
675         * <p>Example:
676         *
677         * <pre>
678         * {
679         *   "kty" : "RSA",
680         *   "use" : "sig",
681         *   "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b"
682         * }
683         * </pre>
684         *
685         * @return The JSON object representation.
686         */
687        public Map<String, Object> toJSONObject() {
688
689                Map<String, Object> o = JSONObjectUtils.newJSONObject();
690
691                o.put(JWKParameterNames.KEY_TYPE, kty.getValue());
692
693                if (use != null) {
694                        o.put(JWKParameterNames.PUBLIC_KEY_USE, use.identifier());
695                }
696
697                if (ops != null) {
698                        List<Object> stringValues = JSONArrayUtils.newJSONArray();
699                        for (KeyOperation op: ops) {
700                                stringValues.add(op.identifier());
701                        }
702                        o.put(JWKParameterNames.KEY_OPS, stringValues);
703                }
704
705                if (alg != null) {
706                        o.put(JWKParameterNames.ALGORITHM, alg.getName());
707                }
708
709                if (kid != null) {
710                        o.put(JWKParameterNames.KEY_ID, kid);
711                }
712
713                if (x5u != null) {
714                        o.put(JWKParameterNames.X_509_CERT_URL, x5u.toString());
715                }
716
717                if (x5t != null) {
718                        o.put(JWKParameterNames.X_509_CERT_SHA_1_THUMBPRINT, x5t.toString());
719                }
720                
721                if (x5t256 != null) {
722                        o.put(JWKParameterNames.X_509_CERT_SHA_256_THUMBPRINT, x5t256.toString());
723                }
724
725                if (x5c != null) {
726                        List<Object> stringValues = JSONArrayUtils.newJSONArray();
727                        for (Base64 base64: x5c) {
728                                stringValues.add(base64.toString());
729                        }
730                        o.put(JWKParameterNames.X_509_CERT_CHAIN, stringValues);
731                }
732                
733                if (exp != null) {
734                        o.put(JWKParameterNames.EXPIRATION_TIME, DateUtils.toSecondsSinceEpoch(exp));
735                }
736                
737                if (nbf != null) {
738                        o.put(JWKParameterNames.NOT_BEFORE, DateUtils.toSecondsSinceEpoch(nbf));
739                }
740                
741                if (iat != null) {
742                        o.put(JWKParameterNames.ISSUED_AT, DateUtils.toSecondsSinceEpoch(iat));
743                }
744
745                if (revocation != null) {
746                        o.put(JWKParameterNames.REVOKED, revocation.toJSONObject());
747                }
748
749                return o;
750        }
751
752
753        /**
754         * Returns the JSON object string representation of this JWK.
755         *
756         * @return The JSON object string representation.
757         */
758        public String toJSONString() {
759                return JSONObjectUtils.toJSONString(toJSONObject());
760        }
761
762
763        /**
764         * @see #toJSONString
765         */
766        @Override
767        public String toString() {
768
769                return JSONObjectUtils.toJSONString(toJSONObject());
770        }
771
772
773        /**
774         * Parses a JWK from the specified JSON object string representation. 
775         * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 
776         * {@link OctetSequenceKey}.
777         *
778         * @param s The JSON object string to parse. Must not be {@code null}.
779         *
780         * @return The JWK.
781         *
782         * @throws ParseException If the string couldn't be parsed to a
783         *                        supported JWK.
784         */
785        public static JWK parse(final String s)
786                throws ParseException {
787
788                return parse(JSONObjectUtils.parse(s));
789        }
790
791
792        /**
793         * Parses a JWK from the specified JSON object representation. The JWK 
794         * must be an {@link ECKey}, an {@link RSAKey}, or a 
795         * {@link OctetSequenceKey}.
796         *
797         * @param jsonObject The JSON object to parse. Must not be 
798         *                   {@code null}.
799         *
800         * @return The JWK.
801         *
802         * @throws ParseException If the JSON object couldn't be parsed to a 
803         *                        supported JWK.
804         */
805        public static JWK parse(final Map<String, Object> jsonObject)
806                throws ParseException {
807                
808                String ktyString = JSONObjectUtils.getString(jsonObject, JWKParameterNames.KEY_TYPE);
809                
810                if (ktyString == null) {
811                        throw new ParseException("Missing key type \"kty\" parameter", 0);
812                }
813
814                KeyType kty = KeyType.parse(ktyString);
815
816                if (kty == KeyType.EC) {
817                        
818                        return ECKey.parse(jsonObject);
819
820                } else if (kty == KeyType.RSA) {
821                        
822                        return RSAKey.parse(jsonObject);
823
824                } else if (kty == KeyType.OCT) {
825                        
826                        return OctetSequenceKey.parse(jsonObject);
827                        
828                } else if (kty == KeyType.OKP) {
829                        
830                        return OctetKeyPair.parse(jsonObject);
831
832                } else {
833
834                        throw new ParseException("Unsupported key type \"" + JWKParameterNames.KEY_TYPE + "\" parameter: " + kty, 0);
835                }
836        }
837        
838        
839        /**
840         * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the
841         * specified X.509 certificate. Requires BouncyCastle.
842         *
843         * <p><strong>Important:</strong> The X.509 certificate is not
844         * validated!
845         *
846         * <p>Sets the following JWK parameters:
847         *
848         * <ul>
849         *     <li>For an EC key the curve is obtained from the subject public
850         *         key info algorithm parameters.
851         *     <li>The JWK use inferred by {@link KeyUse#from}.
852         *     <li>The JWK ID from the X.509 serial number (in base 10).
853         *     <li>The JWK X.509 certificate chain (this certificate only).
854         *     <li>The JWK X.509 certificate SHA-256 thumbprint.
855         * </ul>
856         *
857         * @param cert The X.509 certificate. Must not be {@code null}.
858         *
859         * @return The public RSA or EC JWK.
860         *
861         * @throws JOSEException If parsing failed.
862         */
863        public static JWK parse(final X509Certificate cert)
864                throws JOSEException {
865                
866                if (cert.getPublicKey() instanceof RSAPublicKey) {
867                        return RSAKey.parse(cert);
868                } else if (cert.getPublicKey() instanceof ECPublicKey) {
869                        return ECKey.parse(cert);
870                } else {
871                        throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm());
872                }
873        }
874        
875        
876        /**
877         * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the
878         * specified PEM-encoded X.509 certificate. Requires BouncyCastle.
879         *
880         * <p><strong>Important:</strong> The X.509 certificate is not
881         * validated!
882         *
883         * <p>Sets the following JWK parameters:
884         *
885         * <ul>
886         *     <li>For an EC key the curve is obtained from the subject public
887         *         key info algorithm parameters.
888         *     <li>The JWK use inferred by {@link KeyUse#from}.
889         *     <li>The JWK ID from the X.509 serial number (in base 10).
890         *     <li>The JWK X.509 certificate chain (this certificate only).
891         *     <li>The JWK X.509 certificate SHA-256 thumbprint.
892         * </ul>
893         *
894         * @param pemEncodedCert The PEM-encoded X.509 certificate. Must not be
895         *                       {@code null}.
896         *
897         * @return The public RSA or EC JWK.
898         *
899         * @throws JOSEException If parsing failed.
900         */
901        public static JWK parseFromPEMEncodedX509Cert(final String pemEncodedCert)
902                throws JOSEException {
903                
904                X509Certificate cert = X509CertUtils.parse(pemEncodedCert);
905                
906                if (cert == null) {
907                        throw new JOSEException("Couldn't parse PEM-encoded X.509 certificate");
908                }
909                
910                return parse(cert);
911        }
912        
913        
914        /**
915         * Loads a JWK from the specified JCE key store. The JWK can be a
916         * public / private {@link RSAKey RSA key}, a public / private
917         * {@link ECKey EC key}, or a {@link OctetSequenceKey secret key}.
918         * Requires BouncyCastle.
919         *
920         * <p><strong>Important:</strong> The X.509 certificate is not
921         * validated!
922         *
923         * @param keyStore The key store. Must not be {@code null}.
924         * @param alias    The alias. Must not be {@code null}.
925         * @param pin      The pin to unlock the private key if any, empty or
926         *                 {@code null} if not required.
927         *
928         * @return The public / private RSA or EC JWK, or secret JWK, or
929         *         {@code null} if no key with the specified alias was found.
930         *
931         * @throws KeyStoreException On a key store exception.
932         * @throws JOSEException     If RSA or EC key loading failed.
933         */
934        public static JWK load(final KeyStore keyStore, final String alias, final char[] pin)
935                throws KeyStoreException, JOSEException {
936                
937                java.security.cert.Certificate cert = keyStore.getCertificate(alias);
938                
939                if (cert == null) {
940                        // Try secret key
941                        return OctetSequenceKey.load(keyStore, alias, pin);
942                }
943                
944                if (cert.getPublicKey() instanceof RSAPublicKey) {
945                        return RSAKey.load(keyStore, alias, pin);
946                } else if (cert.getPublicKey() instanceof ECPublicKey) {
947                        return ECKey.load(keyStore, alias, pin);
948                } else {
949                        throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm());
950                }
951        }
952
953        /**
954         * Parses an RSA or EC JWK from the specified string of one or more
955         * PEM-encoded object(s):
956         *
957         * <ul>
958         *     <li>X.509 certificate (PEM header: BEGIN CERTIFICATE)
959         *     <li>PKCS#1 RSAPublicKey (PEM header: BEGIN RSA PUBLIC KEY)
960         *     <li>X.509 SubjectPublicKeyInfo (PEM header: BEGIN PUBLIC KEY)
961         *     <li>PKCS#1 RSAPrivateKey (PEM header: BEGIN RSA PRIVATE KEY)
962         *     <li>PKCS#8 PrivateKeyInfo (PEM header: BEGIN PRIVATE KEY)
963         *     <li>matching pair of the above
964         * </ul>
965         *
966         * <p>Requires BouncyCastle.
967         *
968         * @param pemEncodedObjects The string of PEM-encoded object(s).
969         *
970         * @return The public / (private) RSA or EC JWK.
971         *
972         * @throws JOSEException If RSA or EC key parsing failed.
973         */
974        public static JWK parseFromPEMEncodedObjects(final String pemEncodedObjects)
975                throws JOSEException {
976                
977                final List<KeyPair> keys = PEMEncodedKeyParser.parseKeys(pemEncodedObjects);
978                if (keys.isEmpty()) {
979                        throw new JOSEException("No PEM-encoded keys found");
980                }
981
982                final KeyPair pair = mergeKeyPairs(keys);
983
984                final PublicKey publicKey = pair.getPublic();
985                final PrivateKey privateKey = pair.getPrivate();
986                
987                if (publicKey == null) {
988                        // For EC keys, for RSA the public can be reconstructed
989                        throw new JOSEException("Missing PEM-encoded public key to construct JWK");
990                }
991
992                if (publicKey instanceof ECPublicKey) {
993                        final ECPublicKey ecPubKey = (ECPublicKey) publicKey;
994                        final ECParameterSpec pubParams = ecPubKey.getParams();
995
996                        if (privateKey instanceof ECPrivateKey) {
997                                validateEcCurves(ecPubKey, (ECPrivateKey) privateKey);
998                        }
999                        if (privateKey != null && !(privateKey instanceof ECPrivateKey)) {
1000                                throw new JOSEException("Unsupported " + KeyType.EC.getValue() + " private key type: " + privateKey);
1001                        }
1002
1003                        final Curve curve = Curve.forECParameterSpec(pubParams);
1004                        final ECKey.Builder builder = new ECKey.Builder(curve, (ECPublicKey) publicKey);
1005
1006                        if (privateKey != null) {
1007                                builder.privateKey((ECPrivateKey) privateKey);
1008                        }
1009                        return builder.build();
1010                }
1011
1012                if (publicKey instanceof RSAPublicKey) {
1013                        final RSAKey.Builder builder = new RSAKey.Builder((RSAPublicKey) publicKey);
1014                        if (privateKey instanceof RSAPrivateKey) {
1015                                builder.privateKey((RSAPrivateKey) privateKey);
1016                        } else if (privateKey != null) {
1017                                throw new JOSEException("Unsupported " + KeyType.RSA.getValue() + " private key type: " + privateKey);
1018                        }
1019                        return builder.build();
1020                }
1021
1022                throw new JOSEException("Unsupported algorithm of PEM-encoded key: " + publicKey.getAlgorithm());
1023        }
1024        
1025
1026        private static void validateEcCurves(ECPublicKey publicKey, ECPrivateKey privateKey) throws JOSEException {
1027                final ECParameterSpec pubParams = publicKey.getParams();
1028                final ECParameterSpec privParams = privateKey.getParams();
1029                if (!pubParams.getCurve().equals(privParams.getCurve())) {
1030                        throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key curve mismatch: " + publicKey);
1031                }
1032                if (pubParams.getCofactor() != privParams.getCofactor()) {
1033                        throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key cofactor mismatch: " + publicKey);
1034                }
1035                if (!pubParams.getGenerator().equals(privParams.getGenerator())) {
1036                        throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key generator mismatch: " + publicKey);
1037                }
1038                if (!pubParams.getOrder().equals(privParams.getOrder())) {
1039                        throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key order mismatch: " + publicKey);
1040                }
1041        }
1042
1043        
1044        private static KeyPair mergeKeyPairs(final List<KeyPair> keys) throws JOSEException {
1045                final KeyPair pair;
1046                if (keys.size() == 1) {
1047                        // Assume public key, or private key easy to convert to public,
1048                        // otherwise not representable as a JWK
1049                        pair = keys.get(0);
1050                } else if (keys.size() == 2) {
1051                        // If two keys, assume public + private keys separated
1052                        pair = twoKeysToKeyPair(keys);
1053                } else {
1054                        throw new JOSEException("Expected key or pair of PEM-encoded keys");
1055                }
1056                return pair;
1057        }
1058
1059        
1060        private static KeyPair twoKeysToKeyPair(final List<? extends KeyPair> keys) throws JOSEException {
1061                final KeyPair key1 = keys.get(0);
1062                final KeyPair key2 = keys.get(1);
1063                if (key1.getPublic() != null && key2.getPrivate() != null) {
1064                        return new KeyPair(key1.getPublic(), key2.getPrivate());
1065                } else if (key1.getPrivate() != null && key2.getPublic() != null) {
1066                        return new KeyPair(key2.getPublic(), key1.getPrivate());
1067                } else {
1068                        throw new JOSEException("Not a public/private key pair");
1069                }
1070        }
1071
1072        
1073        @Override
1074        public boolean equals(Object o) {
1075                if (this == o) return true;
1076                if (!(o instanceof JWK)) return false;
1077                JWK jwk = (JWK) o;
1078                return Objects.equals(kty, jwk.kty) &&
1079                                Objects.equals(use, jwk.use) &&
1080                                Objects.equals(ops, jwk.ops) &&
1081                                Objects.equals(alg, jwk.alg) &&
1082                                Objects.equals(kid, jwk.kid) &&
1083                                Objects.equals(x5u, jwk.x5u) &&
1084                                Objects.equals(x5t, jwk.x5t) &&
1085                                Objects.equals(x5t256, jwk.x5t256) &&
1086                                Objects.equals(x5c, jwk.x5c) &&
1087                                Objects.equals(exp, jwk.exp) &&
1088                                Objects.equals(nbf, jwk.nbf) &&
1089                                Objects.equals(iat, jwk.iat) &&
1090                                Objects.equals(revocation, jwk.revocation) &&
1091                                Objects.equals(keyStore, jwk.keyStore);
1092        }
1093
1094        
1095        @Override
1096        public int hashCode() {
1097                return Objects.hash(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, revocation, keyStore);
1098        }
1099}