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.*;
024import java.security.cert.X509Certificate;
025import java.security.interfaces.ECPrivateKey;
026import java.security.interfaces.ECPublicKey;
027import java.security.interfaces.RSAPrivateKey;
028import java.security.interfaces.RSAPublicKey;
029import java.security.spec.ECParameterSpec;
030import java.text.ParseException;
031import java.util.*;
032
033import net.minidev.json.JSONAware;
034import net.minidev.json.JSONObject;
035
036import com.nimbusds.jose.Algorithm;
037import com.nimbusds.jose.JOSEException;
038import com.nimbusds.jose.util.Base64;
039import com.nimbusds.jose.util.*;
040
041
042/**
043 * The base abstract class for JSON Web Keys (JWKs). It serialises to a JSON
044 * object.
045 *
046 * <p>The following JSON object members are common to all JWK types:
047 *
048 * <ul>
049 *     <li>{@link #getKeyType kty} (required)
050 *     <li>{@link #getKeyUse use} (optional)
051 *     <li>{@link #getKeyOperations key_ops} (optional)
052 *     <li>{@link #getKeyID kid} (optional)
053 *     <li>{@link #getX509CertURL()  x5u} (optional)
054 *     <li>{@link #getX509CertThumbprint()  x5t} (optional)
055 *     <li>{@link #getX509CertSHA256Thumbprint()  x5t#S256} (optional)
056 *     <li>{@link #getX509CertChain() x5c} (optional)
057 *     <li>{@link #getKeyStore()}
058 * </ul>
059 *
060 * <p>Example JWK (of the Elliptic Curve type):
061 *
062 * <pre>
063 * {
064 *   "kty" : "EC",
065 *   "crv" : "P-256",
066 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
067 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
068 *   "use" : "enc",
069 *   "kid" : "1"
070 * }
071 * </pre>
072 *
073 * @author Vladimir Dzhuvinov
074 * @author Justin Richer
075 * @author Stefan Larsson
076 * @version 2019-04-15
077 */
078public abstract class JWK implements JSONAware, Serializable {
079
080
081        private static final long serialVersionUID = 1L;
082
083
084        /**
085         * The MIME type of JWK objects: 
086         * {@code application/jwk+json; charset=UTF-8}
087         */
088        public static final String MIME_TYPE = "application/jwk+json; charset=UTF-8";
089
090
091        /**
092         * The key type, required.
093         */
094        private final KeyType kty;
095
096
097        /**
098         * The key use, optional.
099         */
100        private final KeyUse use;
101
102
103        /**
104         * The key operations, optional.
105         */
106        private final Set<KeyOperation> ops;
107
108
109        /**
110         * The intended JOSE algorithm for the key, optional.
111         */
112        private final Algorithm alg;
113
114
115        /**
116         * The key ID, optional.
117         */
118        private final String kid;
119
120
121        /**
122         * X.509 certificate URL, optional.
123         */
124        private final URI x5u;
125
126
127        /**
128         * X.509 certificate SHA-1 thumbprint, optional.
129         */
130        @Deprecated
131        private final Base64URL x5t;
132        
133        
134        /**
135         * X.509 certificate SHA-256 thumbprint, optional.
136         */
137        private Base64URL x5t256;
138
139
140        /**
141         * The X.509 certificate chain, optional.
142         */
143        private final List<Base64> x5c;
144        
145        
146        /**
147         * The parsed X.509 certificate chain, optional.
148         */
149        private final List<X509Certificate> parsedX5c;
150        
151        
152        /**
153         * Reference to the underlying key store, {@code null} if none.
154         */
155        private final KeyStore keyStore;
156
157
158        /**
159         * Creates a new JSON Web Key (JWK).
160         *
161         * @param kty    The key type. Must not be {@code null}.
162         * @param use    The key use, {@code null} if not specified or if the
163         *               key is intended for signing as well as encryption.
164         * @param ops    The key operations, {@code null} if not specified.
165         * @param alg    The intended JOSE algorithm for the key, {@code null}
166         *               if not specified.
167         * @param kid    The key ID, {@code null} if not specified.
168         * @param x5u    The X.509 certificate URL, {@code null} if not
169         *               specified.
170         * @param x5t    The X.509 certificate thumbprint, {@code null} if not
171         *               specified.
172         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
173         *               if not specified.
174         * @param x5c    The X.509 certificate chain, {@code null} if not
175         *               specified.
176         * @param ks     Reference to the underlying key store, {@code null} if
177         *               none.
178         */
179        protected JWK(final KeyType kty,
180                      final KeyUse use,
181                      final Set<KeyOperation> ops,
182                      final Algorithm alg,
183                      final String kid,
184                      final URI x5u,
185                      final Base64URL x5t,
186                      final Base64URL x5t256,
187                      final List<Base64> x5c,
188                      final KeyStore ks) {
189
190                if (kty == null) {
191                        throw new IllegalArgumentException("The key type \"kty\" parameter must not be null");
192                }
193
194                this.kty = kty;
195
196                if (! KeyUseAndOpsConsistency.areConsistent(use, ops)) {
197                        throw new IllegalArgumentException("The key use \"use\" and key options \"key_opts\" parameters are not consistent, " +
198                                "see RFC 7517, section 4.3");
199                }
200
201                this.use = use;
202                this.ops = ops;
203
204                this.alg = alg;
205                this.kid = kid;
206
207                this.x5u = x5u;
208                this.x5t = x5t;
209                this.x5t256 = x5t256;
210                
211                if (x5c != null && x5c.isEmpty()) {
212                        throw new IllegalArgumentException("The X.509 certificate chain \"x5c\" must not be empty");
213                }
214                this.x5c = x5c;
215                
216                try {
217                        parsedX5c = X509CertChainUtils.parse(x5c);
218                } catch (ParseException e) {
219                        throw new IllegalArgumentException("Invalid X.509 certificate chain \"x5c\": " + e.getMessage(), e);
220                }
221                
222                this.keyStore = ks;
223        }
224
225
226        /**
227         * Gets the type ({@code kty}) of this JWK.
228         *
229         * @return The key type.
230         */
231        public KeyType getKeyType() {
232
233                return kty;
234        }
235
236
237        /**
238         * Gets the use ({@code use}) of this JWK.
239         *
240         * @return The key use, {@code null} if not specified or if the key is
241         *         intended for signing as well as encryption.
242         */
243        public KeyUse getKeyUse() {
244
245                return use;
246        }
247
248
249        /**
250         * Gets the operations ({@code key_ops}) for this JWK.
251         *
252         * @return The key operations, {@code null} if not specified.
253         */
254        public Set<KeyOperation> getKeyOperations() {
255
256                return ops;
257        }
258
259
260        /**
261         * Gets the intended JOSE algorithm ({@code alg}) for this JWK.
262         *
263         * @return The intended JOSE algorithm, {@code null} if not specified.
264         */
265        public Algorithm getAlgorithm() {
266
267                return alg;
268        }
269
270
271        /**
272         * Gets the ID ({@code kid}) of this JWK. The key ID can be used to 
273         * match a specific key. This can be used, for instance, to choose a 
274         * key within a {@link JWKSet} during key rollover. The key ID may also 
275         * correspond to a JWS/JWE {@code kid} header parameter value.
276         *
277         * @return The key ID, {@code null} if not specified.
278         */
279        public String getKeyID() {
280
281                return kid;
282        }
283
284
285        /**
286         * Gets the X.509 certificate URL ({@code x5u}) of this JWK.
287         *
288         * @return The X.509 certificate URL, {@code null} if not specified.
289         */
290        public URI getX509CertURL() {
291
292                return x5u;
293        }
294
295
296        /**
297         * Gets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of this
298         * JWK.
299         *
300         * @return The X.509 certificate SHA-1 thumbprint, {@code null} if not
301         *         specified.
302         */
303        @Deprecated
304        public Base64URL getX509CertThumbprint() {
305
306                return x5t;
307        }
308        
309        
310        /**
311         * Gets the X.509 certificate SHA-256 thumbprint ({@code x5t#S256}) of
312         * this JWK.
313         *
314         * @return The X.509 certificate SHA-256 thumbprint, {@code null} if
315         *         not specified.
316         */
317        public Base64URL getX509CertSHA256Thumbprint() {
318                
319                return x5t256;
320        }
321
322
323        /**
324         * Gets the X.509 certificate chain ({@code x5c}) of this JWK.
325         *
326         * @return The X.509 certificate chain as a unmodifiable list,
327         *         {@code null} if not specified.
328         */
329        public List<Base64> getX509CertChain() {
330
331                if (x5c == null) {
332                        return null;
333                }
334
335                return Collections.unmodifiableList(x5c);
336        }
337        
338        
339        /**
340         * Gets the parsed X.509 certificate chain ({@code x5c}) of this JWK.
341         *
342         * @return The X.509 certificate chain as a unmodifiable list,
343         *         {@code null} if not specified.
344         */
345        public List<X509Certificate> getParsedX509CertChain() {
346                
347                if (parsedX5c == null) {
348                        return null;
349                }
350                
351                return Collections.unmodifiableList(parsedX5c);
352        }
353        
354        
355        /**
356         * Returns a reference to the underlying key store.
357         *
358         * @return The underlying key store, {@code null} if none.
359         */
360        public KeyStore getKeyStore() {
361                
362                return keyStore;
363        }
364
365
366        /**
367         * Returns the required JWK parameters. Intended as input for JWK
368         * thumbprint computation. See RFC 7638 for more information.
369         *
370         * @return The required JWK parameters, sorted alphanumerically by key
371         *         name and ready for JSON serialisation.
372         */
373        public abstract LinkedHashMap<String,?> getRequiredParams();
374
375
376        /**
377         * Computes the SHA-256 thumbprint of this JWK. See RFC 7638 for more
378         * information.
379         *
380         * @return The SHA-256 thumbprint.
381         *
382         * @throws JOSEException If the SHA-256 hash algorithm is not
383         *                       supported.
384         */
385        public Base64URL computeThumbprint()
386                throws JOSEException {
387
388                return computeThumbprint("SHA-256");
389        }
390
391
392        /**
393         * Computes the thumbprint of this JWK using the specified hash
394         * algorithm. See RFC 7638 for more information.
395         *
396         * @param hashAlg The hash algorithm. Must not be {@code null}.
397         *
398         * @return The SHA-256 thumbprint.
399         *
400         * @throws JOSEException If the hash algorithm is not supported.
401         */
402        public Base64URL computeThumbprint(final String hashAlg)
403                throws JOSEException {
404
405                return ThumbprintUtils.compute(hashAlg, this);
406        }
407
408
409        /**
410         * Returns {@code true} if this JWK contains private or sensitive
411         * (non-public) parameters.
412         *
413         * @return {@code true} if this JWK contains private parameters, else
414         *         {@code false}.
415         */
416        public abstract boolean isPrivate();
417
418
419        /**
420         * Creates a copy of this JWK with all private or sensitive parameters 
421         * removed.
422         * 
423         * @return The newly created public JWK, or {@code null} if none can be
424         *         created.
425         */
426        public abstract JWK toPublicJWK();
427
428
429        /**
430         * Returns the size of this JWK.
431         *
432         * @return The JWK size, in bits.
433         */
434        public abstract int size();
435
436
437        /**
438         * Returns a JSON object representation of this JWK. This method is 
439         * intended to be called from extending classes.
440         *
441         * <p>Example:
442         *
443         * <pre>
444         * {
445         *   "kty" : "RSA",
446         *   "use" : "sig",
447         *   "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b"
448         * }
449         * </pre>
450         *
451         * @return The JSON object representation.
452         */
453        public JSONObject toJSONObject() {
454
455                JSONObject o = new JSONObject();
456
457                o.put("kty", kty.getValue());
458
459                if (use != null) {
460                        o.put("use", use.identifier());
461                }
462
463                if (ops != null) {
464
465                        List<String> sl = new ArrayList<>(ops.size());
466
467                        for (KeyOperation op: ops) {
468                                sl.add(op.identifier());
469                        }
470
471                        o.put("key_ops", sl);
472                }
473
474                if (alg != null) {
475                        o.put("alg", alg.getName());
476                }
477
478                if (kid != null) {
479                        o.put("kid", kid);
480                }
481
482                if (x5u != null) {
483                        o.put("x5u", x5u.toString());
484                }
485
486                if (x5t != null) {
487                        o.put("x5t", x5t.toString());
488                }
489                
490                if (x5t256 != null) {
491                        o.put("x5t#S256", x5t256.toString());
492                }
493
494                if (x5c != null) {
495                        o.put("x5c", x5c);
496                }
497
498                return o;
499        }
500
501
502        /**
503         * Returns the JSON object string representation of this JWK.
504         *
505         * @return The JSON object string representation.
506         */
507        @Override
508        public String toJSONString() {
509
510                return toJSONObject().toString();
511        }
512
513
514        /**
515         * @see #toJSONString
516         */
517        @Override
518        public String toString() {
519
520                return toJSONObject().toString();
521        }
522
523
524        /**
525         * Parses a JWK from the specified JSON object string representation. 
526         * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a 
527         * {@link OctetSequenceKey}.
528         *
529         * @param s The JSON object string to parse. Must not be {@code null}.
530         *
531         * @return The JWK.
532         *
533         * @throws ParseException If the string couldn't be parsed to a
534         *                        supported JWK.
535         */
536        public static JWK parse(final String s)
537                throws ParseException {
538
539                return parse(JSONObjectUtils.parse(s));
540        }
541
542
543        /**
544         * Parses a JWK from the specified JSON object representation. The JWK 
545         * must be an {@link ECKey}, an {@link RSAKey}, or a 
546         * {@link OctetSequenceKey}.
547         *
548         * @param jsonObject The JSON object to parse. Must not be 
549         *                   {@code null}.
550         *
551         * @return The JWK.
552         *
553         * @throws ParseException If the JSON object couldn't be parsed to a 
554         *                        supported JWK.
555         */
556        public static JWK parse(final JSONObject jsonObject)
557                throws ParseException {
558
559                KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
560
561                if (kty == KeyType.EC) {
562                        
563                        return ECKey.parse(jsonObject);
564
565                } else if (kty == KeyType.RSA) {
566                        
567                        return RSAKey.parse(jsonObject);
568
569                } else if (kty == KeyType.OCT) {
570                        
571                        return OctetSequenceKey.parse(jsonObject);
572                        
573                } else if (kty == KeyType.OKP) {
574                        
575                        return OctetKeyPair.parse(jsonObject);
576
577                } else {
578
579                        throw new ParseException("Unsupported key type \"kty\" parameter: " + kty, 0);
580                }
581        }
582        
583        
584        /**
585         * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the
586         * specified X.509 certificate. Requires BouncyCastle.
587         *
588         * <p><strong>Important:</strong> The X.509 certificate is not
589         * validated!
590         *
591         * <p>Sets the following JWK parameters:
592         *
593         * <ul>
594         *     <li>For an EC key the curve is obtained from the subject public
595         *         key info algorithm parameters.
596         *     <li>The JWK use inferred by {@link KeyUse#from}.
597         *     <li>The JWK ID from the X.509 serial number (in base 10).
598         *     <li>The JWK X.509 certificate chain (this certificate only).
599         *     <li>The JWK X.509 certificate SHA-256 thumbprint.
600         * </ul>
601         *
602         * @param cert The X.509 certificate. Must not be {@code null}.
603         *
604         * @return The public RSA or EC JWK.
605         *
606         * @throws JOSEException If parsing failed.
607         */
608        public static JWK parse(final X509Certificate cert)
609                throws JOSEException {
610                
611                if (cert.getPublicKey() instanceof RSAPublicKey) {
612                        return RSAKey.parse(cert);
613                } else if (cert.getPublicKey() instanceof ECPublicKey) {
614                        return ECKey.parse(cert);
615                } else {
616                        throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm());
617                }
618        }
619        
620        
621        /**
622         * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the
623         * specified PEM-encoded X.509 certificate. Requires BouncyCastle.
624         *
625         * <p><strong>Important:</strong> The X.509 certificate is not
626         * validated!
627         *
628         * <p>Sets the following JWK parameters:
629         *
630         * <ul>
631         *     <li>For an EC key the curve is obtained from the subject public
632         *         key info algorithm parameters.
633         *     <li>The JWK use inferred by {@link KeyUse#from}.
634         *     <li>The JWK ID from the X.509 serial number (in base 10).
635         *     <li>The JWK X.509 certificate chain (this certificate only).
636         *     <li>The JWK X.509 certificate SHA-256 thumbprint.
637         * </ul>
638         *
639         * @param pemEncodedCert The PEM-encoded X.509 certificate. Must not be
640         *                       {@code null}.
641         *
642         * @return The public RSA or EC JWK.
643         *
644         * @throws JOSEException If parsing failed.
645         */
646        public static JWK parseFromPEMEncodedX509Cert(final String pemEncodedCert)
647                throws JOSEException {
648                
649                X509Certificate cert = X509CertUtils.parse(pemEncodedCert);
650                
651                if (cert == null) {
652                        throw new JOSEException("Couldn't parse PEM-encoded X.509 certificate");
653                }
654                
655                return parse(cert);
656        }
657        
658        
659        /**
660         * Loads a JWK from the specified JCE key store. The JWK can be a
661         * public / private {@link RSAKey RSA key}, a public / private
662         * {@link ECKey EC key}, or a {@link OctetSequenceKey secret key}.
663         * Requires BouncyCastle.
664         *
665         * <p><strong>Important:</strong> The X.509 certificate is not
666         * validated!
667         *
668         * @param keyStore The key store. Must not be {@code null}.
669         * @param alias    The alias. Must not be {@code null}.
670         * @param pin      The pin to unlock the private key if any, empty or
671         *                 {@code null} if not required.
672         *
673         * @return The public / private RSA or EC JWK, or secret JWK, or
674         *         {@code null} if no key with the specified alias was found.
675         *
676         * @throws KeyStoreException On a key store exception.
677         * @throws JOSEException     If RSA or EC key loading failed.
678         */
679        public static JWK load(final KeyStore keyStore, final String alias, final char[] pin)
680                throws KeyStoreException, JOSEException {
681                
682                java.security.cert.Certificate cert = keyStore.getCertificate(alias);
683                
684                if (cert == null) {
685                        // Try secret key
686                        return OctetSequenceKey.load(keyStore, alias, pin);
687                }
688                
689                if (cert.getPublicKey() instanceof RSAPublicKey) {
690                        return RSAKey.load(keyStore, alias, pin);
691                } else if (cert.getPublicKey() instanceof ECPublicKey) {
692                        return ECKey.load(keyStore, alias, pin);
693                } else {
694                        throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm());
695                }
696        }
697
698        /**
699         * Parses an RSA or EC JWK from the specified string of one or more
700         * PEM-encoded object(s):
701         *
702         * <ul>
703         *     <li>X.509 certificate (PEM header: BEGIN CERTIFICATE)
704         *     <li>PKCS#1 RSAPublicKey (PEM header: BEGIN RSA PUBLIC KEY)
705         *     <li>X.509 SubjectPublicKeyInfo (PEM header: BEGIN PUBLIC KEY)
706         *     <li>PKCS#1 RSAPrivateKey (PEM header: BEGIN RSA PRIVATE KEY)
707         *     <li>PKCS#8 PrivateKeyInfo (PEM header: BEGIN PRIVATE KEY)
708         *     <li>matching pair of the above
709         * </ul>
710         *
711         * <p>Requires BouncyCastle.
712         *
713         * @param pemEncodedObjects The string of PEM-encoded object(s).
714         *
715         * @return The public / (private) RSA or EC JWK.
716         *
717         * @throws JOSEException If RSA or EC key parsing failed.
718         */
719        public static JWK parseFromPEMEncodedObjects(final String pemEncodedObjects)
720                throws JOSEException {
721                
722                final List<KeyPair> keys = PEMEncodedKeyParser.parseKeys(pemEncodedObjects);
723                if (keys.isEmpty()) {
724                        throw new JOSEException("No PEM-encoded keys found");
725                }
726
727                final KeyPair pair = mergeKeyPairs(toKeyPairList(pemEncodedObjects));
728
729                final PublicKey publicKey = pair.getPublic();
730                final PrivateKey privateKey = pair.getPrivate();
731
732                if (publicKey instanceof ECPublicKey) {
733                        final ECPublicKey ecPubKey = (ECPublicKey) publicKey;
734                        final ECParameterSpec pubParams = ecPubKey.getParams();
735
736                        if (privateKey instanceof ECPrivateKey) {
737                                validateEcCurves(ecPubKey, (ECPrivateKey) privateKey);
738                        }
739                        if (privateKey != null && !(privateKey instanceof ECPrivateKey)) {
740                                throw new JOSEException("Unsupported EC private key type: " + privateKey);
741                        }
742
743                        final Curve curve = Curve.forECParameterSpec(pubParams);
744                        final ECKey.Builder builder = new ECKey.Builder(curve, (ECPublicKey) publicKey);
745
746                        if (privateKey != null) {
747                                builder.privateKey((ECPrivateKey) privateKey);
748                        }
749                        return builder.build();
750                }
751
752                if (publicKey instanceof RSAPublicKey) {
753                        final RSAKey.Builder builder = new RSAKey.Builder((RSAPublicKey) publicKey);
754                        if (privateKey instanceof RSAPrivateKey) {
755                                builder.privateKey((RSAPrivateKey) privateKey);
756                        } else if (privateKey != null) {
757                                throw new JOSEException("Unsupported RSA private key type: " + privateKey);
758                        }
759                        return builder.build();
760                }
761
762                throw new JOSEException("Unsupported algorithm of PEM-encoded key: " + publicKey.getAlgorithm());
763        }
764        
765
766        private static void validateEcCurves(ECPublicKey publicKey, ECPrivateKey privateKey) throws JOSEException {
767                final ECParameterSpec pubParams = publicKey.getParams();
768                final ECParameterSpec privParams = privateKey.getParams();
769                if (!pubParams.getCurve().equals(privParams.getCurve())) {
770                        throw new JOSEException("Public/private EC key curve mismatch: " + publicKey);
771                }
772                if (pubParams.getCofactor() != privParams.getCofactor()) {
773                        throw new JOSEException("Public/private EC key cofactor mismatch: " + publicKey);
774                }
775                if (!pubParams.getGenerator().equals(privParams.getGenerator())) {
776                        throw new JOSEException("Public/private EC key generator mismatch: " + publicKey);
777                }
778                if (!pubParams.getOrder().equals(privParams.getOrder())) {
779                        throw new JOSEException("Public/private EC key order mismatch: " + publicKey);
780                }
781        }
782
783        
784        private static KeyPair mergeKeyPairs(final List<KeyPair> keys) throws JOSEException {
785                final KeyPair pair;
786                if (keys.size() == 1) {
787                        // Assume public key, or private key easy to convert to public,
788                        // otherwise not representable as a JWK
789                        pair = keys.get(0);
790                } else if (keys.size() == 2) {
791                        // If two keys, assume public + private keys separated
792                        pair = twoKeysToKeyPair(keys);
793                } else {
794                        throw new JOSEException("Expected key or pair of PEM-encoded keys");
795                }
796                return pair;
797        }
798
799        
800        private static List<KeyPair> toKeyPairList(final String pem) throws JOSEException {
801                final List<KeyPair> keys = PEMEncodedKeyParser.parseKeys(pem);
802                if (keys.isEmpty()) {
803                        throw new JOSEException("No PEM-encoded keys found");
804                }
805                return keys;
806        }
807
808        
809        private static KeyPair twoKeysToKeyPair(final List<? extends KeyPair> keys) throws JOSEException {
810                final KeyPair key1 = keys.get(0);
811                final KeyPair key2 = keys.get(1);
812                if (key1.getPublic() != null && key2.getPrivate() != null) {
813                        return new KeyPair(key1.getPublic(), key2.getPrivate());
814                } else if (key1.getPrivate() != null && key2.getPublic() != null) {
815                        return new KeyPair(key2.getPublic(), key1.getPrivate());
816                } else {
817                        throw new JOSEException("Not a public/private key pair");
818                }
819        }
820
821        
822        @Override
823        public boolean equals(Object o) {
824                if (this == o) return true;
825                if (!(o instanceof JWK)) return false;
826                JWK jwk = (JWK) o;
827                return Objects.equals(kty, jwk.kty) &&
828                                Objects.equals(use, jwk.use) &&
829                                Objects.equals(ops, jwk.ops) &&
830                                Objects.equals(alg, jwk.alg) &&
831                                Objects.equals(kid, jwk.kid) &&
832                                Objects.equals(x5u, jwk.x5u) &&
833                                Objects.equals(x5t, jwk.x5t) &&
834                                Objects.equals(x5t256, jwk.x5t256) &&
835                                Objects.equals(x5c, jwk.x5c) &&
836                                Objects.equals(parsedX5c, jwk.parsedX5c) &&
837                                Objects.equals(keyStore, jwk.keyStore);
838        }
839
840        
841        @Override
842        public int hashCode() {
843                return Objects.hash(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, parsedX5c, keyStore);
844        }
845}