com.nimbusds.jose.jwk.PEMEncodedKeyParser.java Source code

Java tutorial

Introduction

Here is the source code for com.nimbusds.jose.jwk.PEMEncodedKeyParser.java

Source

/*
 * nimbus-jose-jwt
 *
 * Copyright 2012-2016, Connect2id Ltd and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the
 * License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.nimbusds.jose.jwk;

import java.io.Reader;
import java.io.StringReader;
import java.security.*;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.List;

import com.nimbusds.jose.JOSEException;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.openssl.PEMException;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

/**
 * PEM-encoded public / private key parser. Requires Bouncy Castle.
 *
 * @author Stefan Larsson
 */
class PEMEncodedKeyParser {

    // JcaPEMKeyConverter looks threadsafe
    private static final JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();

    private PEMEncodedKeyParser() {
        // prevent construction of utility class
    }

    /**
     * Parses one or more PEM-encoded certificates, public and / or private
     * keys. The input is assumed to be not password-protected.
     *
     * @param pemEncodedKeys String of one or more PEM-encoded keys.
     *
     * @return The found keys.
     * 
     * @throws JOSEException If parsing failed.
     */
    static List<KeyPair> parseKeys(final String pemEncodedKeys) throws JOSEException {

        // Strips the "---- {BEGIN,END} {CERTIFICATE,PUBLIC/PRIVATE KEY} -----"-like header and footer lines,
        // base64-decodes the body,
        // then uses the proper key specification format to turn it into a JCA Key instance
        final Reader pemReader = new StringReader(pemEncodedKeys);
        final PEMParser parser = new PEMParser(pemReader);
        final List<KeyPair> keys = new ArrayList<>();

        try {
            Object pemObj;
            do {
                pemObj = parser.readObject();

                // if public key, use as-is
                if (pemObj instanceof SubjectPublicKeyInfo) {
                    keys.add(toKeyPair((SubjectPublicKeyInfo) pemObj));
                    continue;
                }

                // if certificate, use the public key which is signed
                if (pemObj instanceof X509CertificateHolder) {
                    keys.add(toKeyPair((X509CertificateHolder) pemObj));
                    continue;
                }

                // if EC private key given, it arrives here as a keypair
                if (pemObj instanceof PEMKeyPair) {
                    keys.add(toKeyPair((PEMKeyPair) pemObj));
                    continue;
                }

                // if (RSA) private key given, return it
                if (pemObj instanceof PrivateKeyInfo) {
                    keys.add(toKeyPair((PrivateKeyInfo) pemObj));
                    // continue implicitly
                }
            } while (pemObj != null);

            return keys;
        } catch (Exception e) {
            throw new JOSEException(e.getMessage(), e);
        }
    }

    private static KeyPair toKeyPair(final SubjectPublicKeyInfo spki) throws PEMException {

        return new KeyPair(pemConverter.getPublicKey(spki), null);
    }

    private static KeyPair toKeyPair(final X509CertificateHolder pemObj) throws PEMException {

        final SubjectPublicKeyInfo spki = pemObj.getSubjectPublicKeyInfo();
        return new KeyPair(pemConverter.getPublicKey(spki), null);
    }

    private static KeyPair toKeyPair(final PEMKeyPair pair) throws PEMException {

        return pemConverter.getKeyPair(pair);
    }

    private static KeyPair toKeyPair(final PrivateKeyInfo pki)
            throws PEMException, NoSuchAlgorithmException, InvalidKeySpecException {

        final PrivateKey privateKey = pemConverter.getPrivateKey(pki);

        // If it's RSA, we can use the modulus and public exponents as BigIntegers to create a public key
        if (privateKey instanceof RSAPrivateCrtKey) {
            final RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(
                    ((RSAPrivateCrtKey) privateKey).getModulus(),
                    ((RSAPrivateCrtKey) privateKey).getPublicExponent());

            final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            final PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
            return new KeyPair(publicKey, privateKey);
        }

        // If was a private EC key, it would already have been received as a PEMKeyPair
        return new KeyPair(null, privateKey);
    }
}