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