diff options
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.java | 509 |
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); + } + } + } +} |