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.

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.

/** * 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
Params:
  • reader – the Reader
/** * 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.
Throws:
Returns:the next object in the stream, null if no objects left.
/** * 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
/** * 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.
Throws:
Returns:the X509Certificate
/** * 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.
Throws:
Returns:the X509Certificate
/** * 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.
Throws:
Returns:the X509Certificate
/** * 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.
Throws:
Returns:the certificate request.
/** * 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.
Throws:
Returns:the X509Certificate
/** * 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
Throws:
Returns:the X509Certificate
/** * 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); } } } }