aboutsummaryrefslogtreecommitdiffstats
path: root/libraries/spongycastle/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/spongycastle/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java')
-rw-r--r--libraries/spongycastle/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java509
1 files changed, 509 insertions, 0 deletions
diff --git a/libraries/spongycastle/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java b/libraries/spongycastle/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java
new file mode 100644
index 000000000..91b1cff0b
--- /dev/null
+++ b/libraries/spongycastle/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java
@@ -0,0 +1,509 @@
+package org.spongycastle.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.spongycastle.asn1.ASN1InputStream;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.ASN1Sequence;
+import org.spongycastle.asn1.DERNull;
+import org.spongycastle.asn1.cms.ContentInfo;
+import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.RSAPublicKey;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.DSAParameter;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x9.X9ECParameters;
+import org.spongycastle.asn1.x9.X9ObjectIdentifiers;
+import org.spongycastle.cert.X509AttributeCertificateHolder;
+import org.spongycastle.cert.X509CRLHolder;
+import org.spongycastle.cert.X509CertificateHolder;
+import org.spongycastle.pkcs.PKCS10CertificationRequest;
+import org.spongycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+import org.spongycastle.util.encoders.Hex;
+import org.spongycastle.util.io.pem.PemHeader;
+import org.spongycastle.util.io.pem.PemObject;
+import org.spongycastle.util.io.pem.PemObjectParser;
+import org.spongycastle.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("X509 CERTIFICATE", new X509CertificateParser());
+ parsers.put("X509 CRL", new X509CRLParser());
+ parsers.put("PKCS7", 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());
+ }
+
+ 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.spongycastle.asn1.sec.ECPrivateKey pKey = org.spongycastle.asn1.sec.ECPrivateKey.getInstance(seq);
+ AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters());
+ PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey);
+ SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.getPublicKey().getBytes());
+
+ return new PEMKeyPair(pubInfo, 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.spongycastle.asn1.pkcs.RSAPrivateKey keyStruct = org.spongycastle.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 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);
+ }
+ }
+ }
+}