org.bouncycastle.openssl.PEMParser.java Source code

Java tutorial

Introduction

Here is the source code for org.bouncycastle.openssl.PEMParser.java

Source

package org.bouncycastle.openssl;

import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.pkcs.RSAPublicKey;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DSAParameter;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.io.pem.PemHeader;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemObjectParser;
import org.bouncycastle.util.io.pem.PemReader;

/**
 * Class for parsing OpenSSL PEM encoded streams containing
 * X509 certificates, PKCS8 encoded keys and PKCS7 objects.
 * <p>
 * In the case of PKCS7 objects the reader will return a CMS ContentInfo object. Public keys will be returned as
 * well formed SubjectPublicKeyInfo objects, private keys will be returned as well formed PrivateKeyInfo objects. In the
 * case of a private key a PEMKeyPair will normally be returned if the encoding contains both the private and public
 * key definition. CRLs, Certificates, PKCS#10 requests, and Attribute Certificates will generate the appropriate BC holder class.
 * </p>
 */
public class PEMParser extends PemReader {
    private final Map parsers = new HashMap();

    /**
     * Create a new PEMReader
     *
     * @param reader the Reader
     */
    public PEMParser(Reader reader) {
        super(reader);

        parsers.put("CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
        parsers.put("NEW CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
        parsers.put("CERTIFICATE", new X509CertificateParser());
        parsers.put("TRUSTED CERTIFICATE", new X509TrustedCertificateParser());
        parsers.put("X509 CERTIFICATE", new X509CertificateParser());
        parsers.put("X509 CRL", new X509CRLParser());
        parsers.put("PKCS7", new PKCS7Parser());
        parsers.put("CMS", new PKCS7Parser());
        parsers.put("ATTRIBUTE CERTIFICATE", new X509AttributeCertificateParser());
        parsers.put("EC PARAMETERS", new ECCurveParamsParser());
        parsers.put("PUBLIC KEY", new PublicKeyParser());
        parsers.put("RSA PUBLIC KEY", new RSAPublicKeyParser());
        parsers.put("RSA PRIVATE KEY", new KeyPairParser(new RSAKeyPairParser()));
        parsers.put("DSA PRIVATE KEY", new KeyPairParser(new DSAKeyPairParser()));
        parsers.put("EC PRIVATE KEY", new KeyPairParser(new ECDSAKeyPairParser()));
        parsers.put("ENCRYPTED PRIVATE KEY", new EncryptedPrivateKeyParser());
        parsers.put("PRIVATE KEY", new PrivateKeyParser());
    }

    /**
     * Read the next PEM object attempting to interpret the header and
     * create a higher level object from the content.
     *
     * @return the next object in the stream, null if no objects left.
     * @throws IOException in case of a parse error.
     */
    public Object readObject() throws IOException {
        PemObject obj = readPemObject();

        if (obj != null) {
            String type = obj.getType();
            if (parsers.containsKey(type)) {
                return ((PemObjectParser) parsers.get(type)).parseObject(obj);
            } else {
                throw new IOException("unrecognised object: " + type);
            }
        }

        return null;
    }

    private class KeyPairParser implements PemObjectParser {
        private final PEMKeyPairParser pemKeyPairParser;

        public KeyPairParser(PEMKeyPairParser pemKeyPairParser) {
            this.pemKeyPairParser = pemKeyPairParser;
        }

        /**
         * Read a Key Pair
         */
        public Object parseObject(PemObject obj) throws IOException {
            boolean isEncrypted = false;
            String dekInfo = null;
            List headers = obj.getHeaders();

            for (Iterator it = headers.iterator(); it.hasNext();) {
                PemHeader hdr = (PemHeader) it.next();

                if (hdr.getName().equals("Proc-Type") && hdr.getValue().equals("4,ENCRYPTED")) {
                    isEncrypted = true;
                } else if (hdr.getName().equals("DEK-Info")) {
                    dekInfo = hdr.getValue();
                }
            }

            //
            // extract the key
            //
            byte[] keyBytes = obj.getContent();

            try {
                if (isEncrypted) {
                    StringTokenizer tknz = new StringTokenizer(dekInfo, ",");
                    String dekAlgName = tknz.nextToken();
                    byte[] iv = Hex.decode(tknz.nextToken());

                    return new PEMEncryptedKeyPair(dekAlgName, iv, keyBytes, pemKeyPairParser);
                }

                return pemKeyPairParser.parse(keyBytes);
            } catch (IOException e) {
                if (isEncrypted) {
                    throw new PEMException("exception decoding - please check password and data.", e);
                } else {
                    throw new PEMException(e.getMessage(), e);
                }
            } catch (IllegalArgumentException e) {
                if (isEncrypted) {
                    throw new PEMException("exception decoding - please check password and data.", e);
                } else {
                    throw new PEMException(e.getMessage(), e);
                }
            }
        }
    }

    private class DSAKeyPairParser implements PEMKeyPairParser {
        public PEMKeyPair parse(byte[] encoding) throws IOException {
            try {
                ASN1Sequence seq = ASN1Sequence.getInstance(encoding);

                if (seq.size() != 6) {
                    throw new PEMException("malformed sequence in DSA private key");
                }

                //            ASN1Integer              v = (ASN1Integer)seq.getObjectAt(0);
                ASN1Integer p = ASN1Integer.getInstance(seq.getObjectAt(1));
                ASN1Integer q = ASN1Integer.getInstance(seq.getObjectAt(2));
                ASN1Integer g = ASN1Integer.getInstance(seq.getObjectAt(3));
                ASN1Integer y = ASN1Integer.getInstance(seq.getObjectAt(4));
                ASN1Integer x = ASN1Integer.getInstance(seq.getObjectAt(5));

                return new PEMKeyPair(
                        new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
                                new DSAParameter(p.getValue(), q.getValue(), g.getValue())), y),
                        new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
                                new DSAParameter(p.getValue(), q.getValue(), g.getValue())), x));
            } catch (IOException e) {
                throw e;
            } catch (Exception e) {
                throw new PEMException("problem creating DSA private key: " + e.toString(), e);
            }
        }
    }

    private class ECDSAKeyPairParser implements PEMKeyPairParser {
        public PEMKeyPair parse(byte[] encoding) throws IOException {
            try {
                ASN1Sequence seq = ASN1Sequence.getInstance(encoding);

                org.bouncycastle.asn1.sec.ECPrivateKey pKey = org.bouncycastle.asn1.sec.ECPrivateKey
                        .getInstance(seq);
                AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey,
                        pKey.getParameters());
                PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey);

                if (pKey.getPublicKey() != null) {
                    SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.getPublicKey().getBytes());

                    return new PEMKeyPair(pubInfo, privInfo);
                } else {
                    return new PEMKeyPair(null, privInfo);
                }
            } catch (IOException e) {
                throw e;
            } catch (Exception e) {
                throw new PEMException("problem creating EC private key: " + e.toString(), e);
            }
        }
    }

    private class RSAKeyPairParser implements PEMKeyPairParser {
        public PEMKeyPair parse(byte[] encoding) throws IOException {
            try {
                ASN1Sequence seq = ASN1Sequence.getInstance(encoding);

                if (seq.size() != 9) {
                    throw new PEMException("malformed sequence in RSA private key");
                }

                org.bouncycastle.asn1.pkcs.RSAPrivateKey keyStruct = org.bouncycastle.asn1.pkcs.RSAPrivateKey
                        .getInstance(seq);

                RSAPublicKey pubSpec = new RSAPublicKey(keyStruct.getModulus(), keyStruct.getPublicExponent());

                AlgorithmIdentifier algId = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption,
                        DERNull.INSTANCE);

                return new PEMKeyPair(new SubjectPublicKeyInfo(algId, pubSpec),
                        new PrivateKeyInfo(algId, keyStruct));
            } catch (IOException e) {
                throw e;
            } catch (Exception e) {
                throw new PEMException("problem creating RSA private key: " + e.toString(), e);
            }
        }
    }

    private class PublicKeyParser implements PemObjectParser {
        public PublicKeyParser() {
        }

        public Object parseObject(PemObject obj) throws IOException {
            return SubjectPublicKeyInfo.getInstance(obj.getContent());
        }
    }

    private class RSAPublicKeyParser implements PemObjectParser {
        public RSAPublicKeyParser() {
        }

        public Object parseObject(PemObject obj) throws IOException {
            try {
                RSAPublicKey rsaPubStructure = RSAPublicKey.getInstance(obj.getContent());

                return new SubjectPublicKeyInfo(
                        new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE),
                        rsaPubStructure);
            } catch (IOException e) {
                throw e;
            } catch (Exception e) {
                throw new PEMException("problem extracting key: " + e.toString(), e);
            }
        }
    }

    private class X509CertificateParser implements PemObjectParser {
        /**
         * Reads in a X509Certificate.
         *
         * @return the X509Certificate
         * @throws java.io.IOException if an I/O error occured
         */
        public Object parseObject(PemObject obj) throws IOException {
            try {
                return new X509CertificateHolder(obj.getContent());
            } catch (Exception e) {
                throw new PEMException("problem parsing cert: " + e.toString(), e);
            }
        }
    }

    private class X509TrustedCertificateParser implements PemObjectParser {
        /**
         * Reads in a X509Certificate.
         *
         * @return the X509Certificate
         * @throws java.io.IOException if an I/O error occured
         */
        public Object parseObject(PemObject obj) throws IOException {
            try {
                return new X509TrustedCertificateBlock(obj.getContent());
            } catch (Exception e) {
                throw new PEMException("problem parsing cert: " + e.toString(), e);
            }
        }
    }

    private class X509CRLParser implements PemObjectParser {
        /**
         * Reads in a X509CRL.
         *
         * @return the X509Certificate
         * @throws java.io.IOException if an I/O error occured
         */
        public Object parseObject(PemObject obj) throws IOException {
            try {
                return new X509CRLHolder(obj.getContent());
            } catch (Exception e) {
                throw new PEMException("problem parsing cert: " + e.toString(), e);
            }
        }
    }

    private class PKCS10CertificationRequestParser implements PemObjectParser {
        /**
         * Reads in a PKCS10 certification request.
         *
         * @return the certificate request.
         * @throws java.io.IOException if an I/O error occured
         */
        public Object parseObject(PemObject obj) throws IOException {
            try {
                return new PKCS10CertificationRequest(obj.getContent());
            } catch (Exception e) {
                throw new PEMException("problem parsing certrequest: " + e.toString(), e);
            }
        }
    }

    private class PKCS7Parser implements PemObjectParser {
        /**
         * Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS
         * API.
         *
         * @return the X509Certificate
         * @throws java.io.IOException if an I/O error occured
         */
        public Object parseObject(PemObject obj) throws IOException {
            try {
                ASN1InputStream aIn = new ASN1InputStream(obj.getContent());

                return ContentInfo.getInstance(aIn.readObject());
            } catch (Exception e) {
                throw new PEMException("problem parsing PKCS7 object: " + e.toString(), e);
            }
        }
    }

    private class X509AttributeCertificateParser implements PemObjectParser {
        public Object parseObject(PemObject obj) throws IOException {
            return new X509AttributeCertificateHolder(obj.getContent());
        }
    }

    private class ECCurveParamsParser implements PemObjectParser {
        public Object parseObject(PemObject obj) throws IOException {
            try {
                Object param = ASN1Primitive.fromByteArray(obj.getContent());

                if (param instanceof ASN1ObjectIdentifier) {
                    return ASN1Primitive.fromByteArray(obj.getContent());
                } else if (param instanceof ASN1Sequence) {
                    return X9ECParameters.getInstance(param);
                } else {
                    return null; // implicitly CA
                }
            } catch (IOException e) {
                throw e;
            } catch (Exception e) {
                throw new PEMException("exception extracting EC named curve: " + e.toString());
            }
        }
    }

    private class EncryptedPrivateKeyParser implements PemObjectParser {
        public EncryptedPrivateKeyParser() {
        }

        /**
         * Reads in an EncryptedPrivateKeyInfo
         *
         * @return the X509Certificate
         * @throws java.io.IOException if an I/O error occured
         */
        public Object parseObject(PemObject obj) throws IOException {
            try {
                return new PKCS8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo.getInstance(obj.getContent()));
            } catch (Exception e) {
                throw new PEMException("problem parsing ENCRYPTED PRIVATE KEY: " + e.toString(), e);
            }
        }
    }

    private class PrivateKeyParser implements PemObjectParser {
        public PrivateKeyParser() {
        }

        public Object parseObject(PemObject obj) throws IOException {
            try {
                return PrivateKeyInfo.getInstance(obj.getContent());
            } catch (Exception e) {
                throw new PEMException("problem parsing PRIVATE KEY: " + e.toString(), e);
            }
        }
    }
}