Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'pkix/src/main/java/org/spongycastle/openssl')
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/EncryptionException.java23
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/MiscPEMGenerator.java209
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMDecryptor.java7
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMDecryptorProvider.java9
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMEncryptedKeyPair.java44
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMEncryptor.java11
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMException.java34
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMKeyPair.java26
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMKeyPairParser.java9
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMParser.java510
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMUtilities.java64
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PEMWriter.java69
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PKCS8Generator.java87
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PasswordException.java10
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/PasswordFinder.java9
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaMiscPEMGenerator.java98
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMKeyConverter.java115
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMWriter.java68
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPKCS8Generator.java18
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java141
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java221
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMDecryptorProviderBuilder.java54
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMEncryptorBuilder.java78
-rw-r--r--pkix/src/main/java/org/spongycastle/openssl/jcajce/PEMUtilities.java257
24 files changed, 2171 insertions, 0 deletions
diff --git a/pkix/src/main/java/org/spongycastle/openssl/EncryptionException.java b/pkix/src/main/java/org/spongycastle/openssl/EncryptionException.java
new file mode 100644
index 00000000..64ef73eb
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/EncryptionException.java
@@ -0,0 +1,23 @@
+package org.spongycastle.openssl;
+
+public class EncryptionException
+ extends PEMException
+{
+ private Throwable cause;
+
+ public EncryptionException(String msg)
+ {
+ super(msg);
+ }
+
+ public EncryptionException(String msg, Throwable ex)
+ {
+ super(msg);
+ this.cause = ex;
+ }
+
+ public Throwable getCause()
+ {
+ return cause;
+ }
+} \ No newline at end of file
diff --git a/pkix/src/main/java/org/spongycastle/openssl/MiscPEMGenerator.java b/pkix/src/main/java/org/spongycastle/openssl/MiscPEMGenerator.java
new file mode 100644
index 00000000..995bf7b5
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/MiscPEMGenerator.java
@@ -0,0 +1,209 @@
+package org.spongycastle.openssl;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.cms.ContentInfo;
+import org.spongycastle.asn1.oiw.OIWObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.x509.DSAParameter;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+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.util.Strings;
+import org.spongycastle.util.io.pem.PemGenerationException;
+import org.spongycastle.util.io.pem.PemHeader;
+import org.spongycastle.util.io.pem.PemObject;
+import org.spongycastle.util.io.pem.PemObjectGenerator;
+
+/**
+ * PEM generator for the original set of PEM objects used in Open SSL.
+ */
+public class MiscPEMGenerator
+ implements PemObjectGenerator
+{
+ private static final ASN1ObjectIdentifier[] dsaOids =
+ {
+ X9ObjectIdentifiers.id_dsa,
+ OIWObjectIdentifiers.dsaWithSHA1
+ };
+
+ private static final byte[] hexEncodingTable =
+ {
+ (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7',
+ (byte)'8', (byte)'9', (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F'
+ };
+
+ private final Object obj;
+ private final PEMEncryptor encryptor;
+
+ public MiscPEMGenerator(Object o)
+ {
+ this.obj = o; // use of this confuses some earlier JDKs.
+ this.encryptor = null;
+ }
+
+ public MiscPEMGenerator(Object o, PEMEncryptor encryptor)
+ {
+ this.obj = o;
+ this.encryptor = encryptor;
+ }
+
+ private PemObject createPemObject(Object o)
+ throws IOException
+ {
+ String type;
+ byte[] encoding;
+
+ if (o instanceof PemObject)
+ {
+ return (PemObject)o;
+ }
+ if (o instanceof PemObjectGenerator)
+ {
+ return ((PemObjectGenerator)o).generate();
+ }
+ if (o instanceof X509CertificateHolder)
+ {
+ type = "CERTIFICATE";
+
+ encoding = ((X509CertificateHolder)o).getEncoded();
+ }
+ else if (o instanceof X509CRLHolder)
+ {
+ type = "X509 CRL";
+
+ encoding = ((X509CRLHolder)o).getEncoded();
+ }
+ else if (o instanceof PrivateKeyInfo)
+ {
+ PrivateKeyInfo info = (PrivateKeyInfo)o;
+ ASN1ObjectIdentifier algOID = info.getPrivateKeyAlgorithm().getAlgorithm();
+
+ if (algOID.equals(PKCSObjectIdentifiers.rsaEncryption))
+ {
+ type = "RSA PRIVATE KEY";
+
+ encoding = info.parsePrivateKey().toASN1Primitive().getEncoded();
+ }
+ else if (algOID.equals(dsaOids[0]) || algOID.equals(dsaOids[1]))
+ {
+ type = "DSA PRIVATE KEY";
+
+ DSAParameter p = DSAParameter.getInstance(info.getPrivateKeyAlgorithm().getParameters());
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new ASN1Integer(0));
+ v.add(new ASN1Integer(p.getP()));
+ v.add(new ASN1Integer(p.getQ()));
+ v.add(new ASN1Integer(p.getG()));
+
+ BigInteger x = ASN1Integer.getInstance(info.parsePrivateKey()).getValue();
+ BigInteger y = p.getG().modPow(x, p.getP());
+
+ v.add(new ASN1Integer(y));
+ v.add(new ASN1Integer(x));
+
+ encoding = new DERSequence(v).getEncoded();
+ }
+ else if (algOID.equals(X9ObjectIdentifiers.id_ecPublicKey))
+ {
+ type = "EC PRIVATE KEY";
+
+ encoding = info.parsePrivateKey().toASN1Primitive().getEncoded();
+ }
+ else
+ {
+ throw new IOException("Cannot identify private key");
+ }
+ }
+ else if (o instanceof SubjectPublicKeyInfo)
+ {
+ type = "PUBLIC KEY";
+
+ encoding = ((SubjectPublicKeyInfo)o).getEncoded();
+ }
+ else if (o instanceof X509AttributeCertificateHolder)
+ {
+ type = "ATTRIBUTE CERTIFICATE";
+ encoding = ((X509AttributeCertificateHolder)o).getEncoded();
+ }
+ else if (o instanceof org.spongycastle.pkcs.PKCS10CertificationRequest)
+ {
+ type = "CERTIFICATE REQUEST";
+ encoding = ((PKCS10CertificationRequest)o).getEncoded();
+ }
+ else if (o instanceof ContentInfo)
+ {
+ type = "PKCS7";
+ encoding = ((ContentInfo)o).getEncoded();
+ }
+ else
+ {
+ throw new PemGenerationException("unknown object passed - can't encode.");
+ }
+
+ if (encryptor != null)
+ {
+ String dekAlgName = Strings.toUpperCase(encryptor.getAlgorithm());
+
+ // Note: For backward compatibility
+ if (dekAlgName.equals("DESEDE"))
+ {
+ dekAlgName = "DES-EDE3-CBC";
+ }
+
+
+ byte[] iv = encryptor.getIV();
+
+ byte[] encData = encryptor.encrypt(encoding);
+
+ List headers = new ArrayList(2);
+
+ headers.add(new PemHeader("Proc-Type", "4,ENCRYPTED"));
+ headers.add(new PemHeader("DEK-Info", dekAlgName + "," + getHexEncoded(iv)));
+
+ return new PemObject(type, headers, encData);
+ }
+ return new PemObject(type, encoding);
+ }
+
+ private String getHexEncoded(byte[] bytes)
+ throws IOException
+ {
+ char[] chars = new char[bytes.length * 2];
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ int v = bytes[i] & 0xff;
+
+ chars[2 * i] = (char)(hexEncodingTable[(v >>> 4)]);
+ chars[2 * i + 1] = (char)(hexEncodingTable[v & 0xf]);
+ }
+
+ return new String(chars);
+ }
+
+ public PemObject generate()
+ throws PemGenerationException
+ {
+ try
+ {
+ return createPemObject(obj);
+ }
+ catch (IOException e)
+ {
+ throw new PemGenerationException("encoding exception: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptor.java b/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptor.java
new file mode 100644
index 00000000..3bd54df6
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptor.java
@@ -0,0 +1,7 @@
+package org.spongycastle.openssl;
+
+public interface PEMDecryptor
+{
+ byte[] decrypt(byte[] keyBytes, byte[] iv)
+ throws PEMException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptorProvider.java b/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptorProvider.java
new file mode 100644
index 00000000..0d0b1e5c
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMDecryptorProvider.java
@@ -0,0 +1,9 @@
+package org.spongycastle.openssl;
+
+import org.spongycastle.operator.OperatorCreationException;
+
+public interface PEMDecryptorProvider
+{
+ PEMDecryptor get(String dekAlgName)
+ throws OperatorCreationException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptedKeyPair.java b/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptedKeyPair.java
new file mode 100644
index 00000000..4a04de14
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptedKeyPair.java
@@ -0,0 +1,44 @@
+package org.spongycastle.openssl;
+
+import java.io.IOException;
+
+import org.spongycastle.operator.OperatorCreationException;
+
+public class PEMEncryptedKeyPair
+{
+ private final String dekAlgName;
+ private final byte[] iv;
+ private final byte[] keyBytes;
+ private final PEMKeyPairParser parser;
+
+ PEMEncryptedKeyPair(String dekAlgName, byte[] iv, byte[] keyBytes, PEMKeyPairParser parser)
+ {
+ this.dekAlgName = dekAlgName;
+ this.iv = iv;
+ this.keyBytes = keyBytes;
+ this.parser = parser;
+ }
+
+ public PEMKeyPair decryptKeyPair(PEMDecryptorProvider keyDecryptorProvider)
+ throws IOException
+ {
+ try
+ {
+ PEMDecryptor keyDecryptor = keyDecryptorProvider.get(dekAlgName);
+
+ return parser.parse(keyDecryptor.decrypt(keyBytes, iv));
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (OperatorCreationException e)
+ {
+ throw new PEMException("cannot create extraction operator: " + e.getMessage(), e);
+ }
+ catch (Exception e)
+ {
+ throw new PEMException("exception processing key pair: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptor.java b/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptor.java
new file mode 100644
index 00000000..63b42b2d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMEncryptor.java
@@ -0,0 +1,11 @@
+package org.spongycastle.openssl;
+
+public interface PEMEncryptor
+{
+ String getAlgorithm();
+
+ byte[] getIV();
+
+ byte[] encrypt(byte[] encoding)
+ throws PEMException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMException.java b/pkix/src/main/java/org/spongycastle/openssl/PEMException.java
new file mode 100644
index 00000000..5df7b5f5
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMException.java
@@ -0,0 +1,34 @@
+package org.spongycastle.openssl;
+
+import java.io.IOException;
+
+public class PEMException
+ extends IOException
+{
+ Exception underlying;
+
+ public PEMException(
+ String message)
+ {
+ super(message);
+ }
+
+ public PEMException(
+ String message,
+ Exception underlying)
+ {
+ super(message);
+ this.underlying = underlying;
+ }
+
+ public Exception getUnderlyingException()
+ {
+ return underlying;
+ }
+
+
+ public Throwable getCause()
+ {
+ return underlying;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPair.java b/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPair.java
new file mode 100644
index 00000000..ad010634
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPair.java
@@ -0,0 +1,26 @@
+package org.spongycastle.openssl;
+
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+
+public class PEMKeyPair
+{
+ private final SubjectPublicKeyInfo publicKeyInfo;
+ private final PrivateKeyInfo privateKeyInfo;
+
+ public PEMKeyPair(SubjectPublicKeyInfo publicKeyInfo, PrivateKeyInfo privateKeyInfo)
+ {
+ this.publicKeyInfo = publicKeyInfo;
+ this.privateKeyInfo = privateKeyInfo;
+ }
+
+ public PrivateKeyInfo getPrivateKeyInfo()
+ {
+ return privateKeyInfo;
+ }
+
+ public SubjectPublicKeyInfo getPublicKeyInfo()
+ {
+ return publicKeyInfo;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPairParser.java b/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPairParser.java
new file mode 100644
index 00000000..32583fd9
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMKeyPairParser.java
@@ -0,0 +1,9 @@
+package org.spongycastle.openssl;
+
+import java.io.IOException;
+
+interface PEMKeyPairParser
+{
+ PEMKeyPair parse(byte[] encoding)
+ throws IOException;
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java b/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java
new file mode 100644
index 00000000..bafe2126
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMParser.java
@@ -0,0 +1,510 @@
+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("TRUSTED 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);
+ }
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMUtilities.java b/pkix/src/main/java/org/spongycastle/openssl/PEMUtilities.java
new file mode 100644
index 00000000..e8fec527
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMUtilities.java
@@ -0,0 +1,64 @@
+package org.spongycastle.openssl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.nist.NISTObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.util.Integers;
+
+public final class PEMUtilities
+{
+ private static final Map KEYSIZES = new HashMap();
+ private static final Set PKCS5_SCHEME_1 = new HashSet();
+ private static final Set PKCS5_SCHEME_2 = new HashSet();
+
+ static
+ {
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC);
+
+ PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2);
+ PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC);
+ PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC);
+ PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC);
+ PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC);
+
+ KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192));
+ KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integers.valueOf(128));
+ KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integers.valueOf(192));
+ KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integers.valueOf(256));
+ }
+
+ static int getKeySize(String algorithm)
+ {
+ if (!KEYSIZES.containsKey(algorithm))
+ {
+ throw new IllegalStateException("no key size for algorithm: " + algorithm);
+ }
+
+ return ((Integer)KEYSIZES.get(algorithm)).intValue();
+ }
+
+ static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid)
+ {
+ return PKCS5_SCHEME_1.contains(algOid);
+ }
+
+ public static boolean isPKCS5Scheme2(ASN1ObjectIdentifier algOid)
+ {
+ return PKCS5_SCHEME_2.contains(algOid);
+ }
+
+ public static boolean isPKCS12(ASN1ObjectIdentifier algOid)
+ {
+ return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId());
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PEMWriter.java b/pkix/src/main/java/org/spongycastle/openssl/PEMWriter.java
new file mode 100644
index 00000000..379c014d
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PEMWriter.java
@@ -0,0 +1,69 @@
+package org.spongycastle.openssl;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.spongycastle.openssl.jcajce.JcaMiscPEMGenerator;
+import org.spongycastle.util.io.pem.PemGenerationException;
+import org.spongycastle.util.io.pem.PemObjectGenerator;
+import org.spongycastle.util.io.pem.PemWriter;
+
+/**
+ * General purpose writer for OpenSSL PEM objects.
+ * @deprecated use JcaPEMWriter
+ */
+public class PEMWriter
+ extends PemWriter
+{
+ /**
+ * Base constructor.
+ *
+ * @param out output stream to use.
+ */
+ public PEMWriter(Writer out)
+ {
+ super(out);
+ }
+
+ /**
+ * @throws IOException
+ */
+ public void writeObject(
+ Object obj)
+ throws IOException
+ {
+ writeObject(obj, null);
+ }
+
+ /**
+ * @param obj
+ * @param encryptor
+ * @throws IOException
+ */
+ public void writeObject(
+ Object obj,
+ PEMEncryptor encryptor)
+ throws IOException
+ {
+ try
+ {
+ super.writeObject(new JcaMiscPEMGenerator(obj, encryptor));
+ }
+ catch (PemGenerationException e)
+ {
+ if (e.getCause() instanceof IOException)
+ {
+ throw (IOException)e.getCause();
+ }
+
+ throw e;
+ }
+ }
+
+ public void writeObject(
+ PemObjectGenerator obj)
+ throws IOException
+ {
+ super.writeObject(obj);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PKCS8Generator.java b/pkix/src/main/java/org/spongycastle/openssl/PKCS8Generator.java
new file mode 100644
index 00000000..83130d36
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PKCS8Generator.java
@@ -0,0 +1,87 @@
+package org.spongycastle.openssl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.nist.NISTObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.operator.OutputEncryptor;
+import org.spongycastle.util.io.pem.PemGenerationException;
+import org.spongycastle.util.io.pem.PemObject;
+import org.spongycastle.util.io.pem.PemObjectGenerator;
+
+public class PKCS8Generator
+ implements PemObjectGenerator
+{
+ public static final ASN1ObjectIdentifier AES_128_CBC = NISTObjectIdentifiers.id_aes128_CBC;
+ public static final ASN1ObjectIdentifier AES_192_CBC = NISTObjectIdentifiers.id_aes192_CBC;
+ public static final ASN1ObjectIdentifier AES_256_CBC = NISTObjectIdentifiers.id_aes256_CBC;
+
+ public static final ASN1ObjectIdentifier DES3_CBC = PKCSObjectIdentifiers.des_EDE3_CBC;
+
+ public static final ASN1ObjectIdentifier PBE_SHA1_RC4_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4;
+ public static final ASN1ObjectIdentifier PBE_SHA1_RC4_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4;
+ public static final ASN1ObjectIdentifier PBE_SHA1_3DES = PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC;
+ public static final ASN1ObjectIdentifier PBE_SHA1_2DES = PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC;
+ public static final ASN1ObjectIdentifier PBE_SHA1_RC2_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC;
+ public static final ASN1ObjectIdentifier PBE_SHA1_RC2_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC;
+
+ private PrivateKeyInfo key;
+ private OutputEncryptor outputEncryptor;
+
+ /**
+ * Base constructor.
+ */
+ public PKCS8Generator(PrivateKeyInfo key, OutputEncryptor outputEncryptor)
+ {
+ this.key = key;
+ this.outputEncryptor = outputEncryptor;
+ }
+
+ public PemObject generate()
+ throws PemGenerationException
+ {
+ if (outputEncryptor != null)
+ {
+ return generate(key, outputEncryptor);
+ }
+ else
+ {
+ return generate(key, null);
+ }
+ }
+
+ private PemObject generate(PrivateKeyInfo key, OutputEncryptor encryptor)
+ throws PemGenerationException
+ {
+ try
+ {
+ byte[] keyData = key.getEncoded();
+
+ if (encryptor == null)
+ {
+ return new PemObject("PRIVATE KEY", keyData);
+ }
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ OutputStream cOut = encryptor.getOutputStream(bOut);
+
+ cOut.write(key.getEncoded());
+
+ cOut.close();
+
+ EncryptedPrivateKeyInfo info = new EncryptedPrivateKeyInfo(encryptor.getAlgorithmIdentifier(), bOut.toByteArray());
+
+ return new PemObject("ENCRYPTED PRIVATE KEY", info.getEncoded());
+ }
+ catch (IOException e)
+ {
+ throw new PemGenerationException("unable to process encoded key data: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PasswordException.java b/pkix/src/main/java/org/spongycastle/openssl/PasswordException.java
new file mode 100644
index 00000000..68de3213
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PasswordException.java
@@ -0,0 +1,10 @@
+package org.spongycastle.openssl;
+
+public class PasswordException
+ extends PEMException
+{
+ public PasswordException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/PasswordFinder.java b/pkix/src/main/java/org/spongycastle/openssl/PasswordFinder.java
new file mode 100644
index 00000000..eb981fd5
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/PasswordFinder.java
@@ -0,0 +1,9 @@
+package org.spongycastle.openssl;
+
+/**
+ * call back to allow a password to be fetched when one is requested.
+ */
+public interface PasswordFinder
+{
+ public char[] getPassword();
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaMiscPEMGenerator.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaMiscPEMGenerator.java
new file mode 100644
index 00000000..88f2a32e
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaMiscPEMGenerator.java
@@ -0,0 +1,98 @@
+package org.spongycastle.openssl.jcajce;
+
+import java.io.IOException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.CRLException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.cert.jcajce.JcaX509AttributeCertificateHolder;
+import org.spongycastle.cert.jcajce.JcaX509CRLHolder;
+import org.spongycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.spongycastle.jce.PKCS10CertificationRequest;
+import org.spongycastle.openssl.MiscPEMGenerator;
+import org.spongycastle.openssl.PEMEncryptor;
+import org.spongycastle.x509.X509AttributeCertificate;
+import org.spongycastle.x509.X509V2AttributeCertificate;
+
+/**
+ * PEM generator for the original set of PEM objects used in Open SSL.
+ */
+public class JcaMiscPEMGenerator
+ extends MiscPEMGenerator
+{
+ private Object obj;
+ private String algorithm;
+ private char[] password;
+ private SecureRandom random;
+ private Provider provider;
+
+ public JcaMiscPEMGenerator(Object o)
+ throws IOException
+ {
+ super(convertObject(o));
+ }
+
+ public JcaMiscPEMGenerator(Object o, PEMEncryptor encryptor)
+ throws IOException
+ {
+ super(convertObject(o), encryptor);
+ }
+
+ private static Object convertObject(Object o)
+ throws IOException
+ {
+ if (o instanceof X509Certificate)
+ {
+ try
+ {
+ return new JcaX509CertificateHolder((X509Certificate)o);
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new IllegalArgumentException("Cannot encode object: " + e.toString());
+ }
+ }
+ else if (o instanceof X509CRL)
+ {
+ try
+ {
+ return new JcaX509CRLHolder((X509CRL)o);
+ }
+ catch (CRLException e)
+ {
+ throw new IllegalArgumentException("Cannot encode object: " + e.toString());
+ }
+ }
+ else if (o instanceof KeyPair)
+ {
+ return convertObject(((KeyPair)o).getPrivate());
+ }
+ else if (o instanceof PrivateKey)
+ {
+ return PrivateKeyInfo.getInstance(((Key)o).getEncoded());
+ }
+ else if (o instanceof PublicKey)
+ {
+ return SubjectPublicKeyInfo.getInstance(((PublicKey)o).getEncoded());
+ }
+ else if (o instanceof X509AttributeCertificate)
+ {
+ return new JcaX509AttributeCertificateHolder((X509V2AttributeCertificate)o);
+ }
+ else if (o instanceof PKCS10CertificationRequest)
+ {
+ return new org.spongycastle.pkcs.PKCS10CertificationRequest(((PKCS10CertificationRequest)o).getEncoded());
+ }
+
+ return o;
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMKeyConverter.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMKeyConverter.java
new file mode 100644
index 00000000..1b15639a
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMKeyConverter.java
@@ -0,0 +1,115 @@
+package org.spongycastle.openssl.jcajce;
+
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.spongycastle.asn1.x9.X9ObjectIdentifiers;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.openssl.PEMException;
+import org.spongycastle.openssl.PEMKeyPair;
+
+public class JcaPEMKeyConverter
+{
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+
+ private static final Map algorithms = new HashMap();
+
+ static
+ {
+ algorithms.put(X9ObjectIdentifiers.id_ecPublicKey, "ECDSA");
+ algorithms.put(PKCSObjectIdentifiers.rsaEncryption, "RSA");
+ algorithms.put(X9ObjectIdentifiers.id_dsa, "DSA");
+ }
+
+ public JcaPEMKeyConverter setProvider(Provider provider)
+ {
+ this.helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public JcaPEMKeyConverter setProvider(String providerName)
+ {
+ this.helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ public KeyPair getKeyPair(PEMKeyPair keyPair)
+ throws PEMException
+ {
+ try
+ {
+ KeyFactory keyFactory = getKeyFactory(keyPair.getPrivateKeyInfo().getPrivateKeyAlgorithm());
+
+ return new KeyPair(keyFactory.generatePublic(new X509EncodedKeySpec(keyPair.getPublicKeyInfo().getEncoded())),
+ keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyPair.getPrivateKeyInfo().getEncoded())));
+ }
+ catch (Exception e)
+ {
+ throw new PEMException("unable to convert key pair: " + e.getMessage(), e);
+ }
+ }
+
+ public PublicKey getPublicKey(SubjectPublicKeyInfo publicKeyInfo)
+ throws PEMException
+ {
+ try
+ {
+ KeyFactory keyFactory = getKeyFactory(publicKeyInfo.getAlgorithm());
+
+ return keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded()));
+ }
+ catch (Exception e)
+ {
+ throw new PEMException("unable to convert key pair: " + e.getMessage(), e);
+ }
+ }
+
+ public PrivateKey getPrivateKey(PrivateKeyInfo privateKeyInfo)
+ throws PEMException
+ {
+ try
+ {
+ KeyFactory keyFactory = getKeyFactory(privateKeyInfo.getPrivateKeyAlgorithm());
+
+ return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyInfo.getEncoded()));
+ }
+ catch (Exception e)
+ {
+ throw new PEMException("unable to convert key pair: " + e.getMessage(), e);
+ }
+ }
+
+ private KeyFactory getKeyFactory(AlgorithmIdentifier algId)
+ throws NoSuchAlgorithmException, NoSuchProviderException
+ {
+ ASN1ObjectIdentifier algorithm = algId.getAlgorithm();
+
+ String algName = (String)algorithms.get(algorithm);
+
+ if (algName == null)
+ {
+ algName = algorithm.getId();
+ }
+
+ return helper.createKeyFactory(algName);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMWriter.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMWriter.java
new file mode 100644
index 00000000..0224b89e
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPEMWriter.java
@@ -0,0 +1,68 @@
+package org.spongycastle.openssl.jcajce;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.spongycastle.openssl.PEMEncryptor;
+import org.spongycastle.util.io.pem.PemGenerationException;
+import org.spongycastle.util.io.pem.PemObjectGenerator;
+import org.spongycastle.util.io.pem.PemWriter;
+
+/**
+ * General purpose writer for OpenSSL PEM objects based on JCA/JCE classes.
+ */
+public class JcaPEMWriter
+ extends PemWriter
+{
+ /**
+ * Base constructor.
+ *
+ * @param out output stream to use.
+ */
+ public JcaPEMWriter(Writer out)
+ {
+ super(out);
+ }
+
+ /**
+ * @throws java.io.IOException
+ */
+ public void writeObject(
+ Object obj)
+ throws IOException
+ {
+ writeObject(obj, null);
+ }
+
+ /**
+ * @param obj
+ * @param encryptor
+ * @throws java.io.IOException
+ */
+ public void writeObject(
+ Object obj,
+ PEMEncryptor encryptor)
+ throws IOException
+ {
+ try
+ {
+ super.writeObject(new JcaMiscPEMGenerator(obj, encryptor));
+ }
+ catch (PemGenerationException e)
+ {
+ if (e.getCause() instanceof IOException)
+ {
+ throw (IOException)e.getCause();
+ }
+
+ throw e;
+ }
+ }
+
+ public void writeObject(
+ PemObjectGenerator obj)
+ throws IOException
+ {
+ super.writeObject(obj);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPKCS8Generator.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPKCS8Generator.java
new file mode 100644
index 00000000..9c4e4f46
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcaPKCS8Generator.java
@@ -0,0 +1,18 @@
+package org.spongycastle.openssl.jcajce;
+
+import java.security.PrivateKey;
+
+import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
+import org.spongycastle.openssl.PKCS8Generator;
+import org.spongycastle.operator.OutputEncryptor;
+import org.spongycastle.util.io.pem.PemGenerationException;
+
+public class JcaPKCS8Generator
+ extends PKCS8Generator
+{
+ public JcaPKCS8Generator(PrivateKey key, OutputEncryptor encryptor)
+ throws PemGenerationException
+ {
+ super(PrivateKeyInfo.getInstance(key.getEncoded()), encryptor);
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java
new file mode 100644
index 00000000..5050f4b2
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java
@@ -0,0 +1,141 @@
+package org.spongycastle.openssl.jcajce;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.Provider;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.spongycastle.asn1.pkcs.EncryptionScheme;
+import org.spongycastle.asn1.pkcs.KeyDerivationFunc;
+import org.spongycastle.asn1.pkcs.PBEParameter;
+import org.spongycastle.asn1.pkcs.PBES2Parameters;
+import org.spongycastle.asn1.pkcs.PBKDF2Params;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.openssl.PEMException;
+import org.spongycastle.operator.InputDecryptor;
+import org.spongycastle.operator.InputDecryptorProvider;
+import org.spongycastle.operator.OperatorCreationException;
+
+public class JceOpenSSLPKCS8DecryptorProviderBuilder
+{
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+
+ public JceOpenSSLPKCS8DecryptorProviderBuilder()
+ {
+ helper = new DefaultJcaJceHelper();
+ }
+
+ public JceOpenSSLPKCS8DecryptorProviderBuilder setProvider(String providerName)
+ {
+ helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ public JceOpenSSLPKCS8DecryptorProviderBuilder setProvider(Provider provider)
+ {
+ helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public InputDecryptorProvider build(final char[] password)
+ throws OperatorCreationException
+ {
+ return new InputDecryptorProvider()
+ {
+ public InputDecryptor get(final AlgorithmIdentifier algorithm)
+ throws OperatorCreationException
+ {
+ final Cipher cipher;
+
+ try
+ {
+ if (PEMUtilities.isPKCS5Scheme2(algorithm.getAlgorithm()))
+ {
+ PBES2Parameters params = PBES2Parameters.getInstance(algorithm.getParameters());
+ KeyDerivationFunc func = params.getKeyDerivationFunc();
+ EncryptionScheme scheme = params.getEncryptionScheme();
+ PBKDF2Params defParams = (PBKDF2Params)func.getParameters();
+
+ int iterationCount = defParams.getIterationCount().intValue();
+ byte[] salt = defParams.getSalt();
+
+ String oid = scheme.getAlgorithm().getId();
+
+ SecretKey key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(oid, password, salt, iterationCount);
+
+ cipher = helper.createCipher(oid);
+ AlgorithmParameters algParams = helper.createAlgorithmParameters(oid);
+
+ algParams.init(scheme.getParameters().toASN1Primitive().getEncoded());
+
+ cipher.init(Cipher.DECRYPT_MODE, key, algParams);
+ }
+ else if (PEMUtilities.isPKCS12(algorithm.getAlgorithm()))
+ {
+ PKCS12PBEParams params = PKCS12PBEParams.getInstance(algorithm.getParameters());
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+
+ SecretKeyFactory secKeyFact = helper.createSecretKeyFactory(algorithm.getAlgorithm().getId());
+ PBEParameterSpec defParams = new PBEParameterSpec(params.getIV(), params.getIterations().intValue());
+
+ cipher = helper.createCipher(algorithm.getAlgorithm().getId());
+
+ cipher.init(Cipher.DECRYPT_MODE, secKeyFact.generateSecret(pbeSpec), defParams);
+ }
+ else if (PEMUtilities.isPKCS5Scheme1(algorithm.getAlgorithm()))
+ {
+ PBEParameter params = PBEParameter.getInstance(algorithm.getParameters());
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+
+ SecretKeyFactory secKeyFact = helper.createSecretKeyFactory(algorithm.getAlgorithm().getId());
+ PBEParameterSpec defParams = new PBEParameterSpec(params.getSalt(), params.getIterationCount().intValue());
+
+ cipher = helper.createCipher(algorithm.getAlgorithm().getId());
+
+ cipher.init(Cipher.DECRYPT_MODE, secKeyFact.generateSecret(pbeSpec), defParams);
+ }
+ else
+ {
+ throw new PEMException("Unknown algorithm: " + algorithm.getAlgorithm());
+ }
+
+ return new InputDecryptor()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return algorithm;
+ }
+
+ public InputStream getInputStream(InputStream encIn)
+ {
+ return new CipherInputStream(encIn, cipher);
+ }
+ };
+ }
+ catch (IOException e)
+ {
+ throw new OperatorCreationException(algorithm.getAlgorithm() + " not available: " + e.getMessage(), e);
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new OperatorCreationException(algorithm.getAlgorithm() + " not available: " + e.getMessage(), e);
+ }
+ };
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java
new file mode 100644
index 00000000..8404661f
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java
@@ -0,0 +1,221 @@
+package org.spongycastle.openssl.jcajce;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.AlgorithmParameterGenerator;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.Provider;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.ASN1Integer;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.ASN1Primitive;
+import org.spongycastle.asn1.DEROctetString;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.nist.NISTObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.KeyDerivationFunc;
+import org.spongycastle.asn1.pkcs.PBES2Parameters;
+import org.spongycastle.asn1.pkcs.PBKDF2Params;
+import org.spongycastle.asn1.pkcs.PKCS12PBEParams;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.asn1.x509.AlgorithmIdentifier;
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.operator.GenericKey;
+import org.spongycastle.operator.OperatorCreationException;
+import org.spongycastle.operator.OutputEncryptor;
+import org.spongycastle.operator.jcajce.JceGenericKey;
+
+public class JceOpenSSLPKCS8EncryptorBuilder
+{
+ public static final String AES_128_CBC = NISTObjectIdentifiers.id_aes128_CBC.getId();
+ public static final String AES_192_CBC = NISTObjectIdentifiers.id_aes192_CBC.getId();
+ public static final String AES_256_CBC = NISTObjectIdentifiers.id_aes256_CBC.getId();
+
+ public static final String DES3_CBC = PKCSObjectIdentifiers.des_EDE3_CBC.getId();
+
+ public static final String PBE_SHA1_RC4_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4.getId();
+ public static final String PBE_SHA1_RC4_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4.getId();
+ public static final String PBE_SHA1_3DES = PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC.getId();
+ public static final String PBE_SHA1_2DES = PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC.getId();
+ public static final String PBE_SHA1_RC2_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC.getId();
+ public static final String PBE_SHA1_RC2_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC.getId();
+
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+
+ private AlgorithmParameters params;
+ private ASN1ObjectIdentifier algOID;
+ byte[] salt;
+ int iterationCount;
+ private Cipher cipher;
+ private SecureRandom random;
+ private AlgorithmParameterGenerator paramGen;
+ private SecretKeyFactory secKeyFact;
+ private char[] password;
+
+ private SecretKey key;
+
+ public JceOpenSSLPKCS8EncryptorBuilder(ASN1ObjectIdentifier algorithm)
+ {
+ algOID = algorithm;
+
+ this.iterationCount = 2048;
+ }
+
+ public JceOpenSSLPKCS8EncryptorBuilder setRandom(SecureRandom random)
+ {
+ this.random = random;
+
+ return this;
+ }
+
+ public JceOpenSSLPKCS8EncryptorBuilder setPasssword(char[] password)
+ {
+ this.password = password;
+
+ return this;
+ }
+
+ public JceOpenSSLPKCS8EncryptorBuilder setIterationCount(int iterationCount)
+ {
+ this.iterationCount = iterationCount;
+
+ return this;
+ }
+
+ public JceOpenSSLPKCS8EncryptorBuilder setProvider(String providerName)
+ {
+ helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ public JceOpenSSLPKCS8EncryptorBuilder setProvider(Provider provider)
+ {
+ helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public OutputEncryptor build()
+ throws OperatorCreationException
+ {
+ final AlgorithmIdentifier algID;
+
+ salt = new byte[20];
+
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+
+ random.nextBytes(salt);
+
+ try
+ {
+ this.cipher = helper.createCipher(algOID.getId());
+
+ if (PEMUtilities.isPKCS5Scheme2(algOID))
+ {
+ this.paramGen = helper.createAlgorithmParameterGenerator(algOID.getId());
+ }
+ else
+ {
+ this.secKeyFact = helper.createSecretKeyFactory(algOID.getId());
+ }
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new OperatorCreationException(algOID + " not available: " + e.getMessage(), e);
+ }
+
+ if (PEMUtilities.isPKCS5Scheme2(algOID))
+ {
+ params = paramGen.generateParameters();
+
+ try
+ {
+ KeyDerivationFunc scheme = new KeyDerivationFunc(algOID, ASN1Primitive.fromByteArray(params.getEncoded()));
+ KeyDerivationFunc func = new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount));
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(func);
+ v.add(scheme);
+
+ algID = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBES2, PBES2Parameters.getInstance(new DERSequence(v)));
+ }
+ catch (IOException e)
+ {
+ throw new OperatorCreationException(e.getMessage(), e);
+ }
+
+ key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(algOID.getId(), password, salt, iterationCount);
+
+ try
+ {
+ cipher.init(Cipher.ENCRYPT_MODE, key, params);
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new OperatorCreationException(e.getMessage(), e);
+ }
+ }
+ else if (PEMUtilities.isPKCS12(algOID))
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ v.add(new DEROctetString(salt));
+ v.add(new ASN1Integer(iterationCount));
+
+ algID = new AlgorithmIdentifier(algOID, PKCS12PBEParams.getInstance(new DERSequence(v)));
+
+ try
+ {
+ PBEKeySpec pbeSpec = new PBEKeySpec(password);
+ PBEParameterSpec defParams = new PBEParameterSpec(salt, iterationCount);
+
+ key = secKeyFact.generateSecret(pbeSpec);
+
+ cipher.init(Cipher.ENCRYPT_MODE, key, defParams);
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new OperatorCreationException(e.getMessage(), e);
+ }
+ }
+ else
+ {
+ throw new OperatorCreationException("unknown algorithm: " + algOID, null);
+ }
+
+ return new OutputEncryptor()
+ {
+ public AlgorithmIdentifier getAlgorithmIdentifier()
+ {
+ return algID;
+ }
+
+ public OutputStream getOutputStream(OutputStream encOut)
+ {
+ return new CipherOutputStream(encOut, cipher);
+ }
+
+ public GenericKey getKey()
+ {
+ return new JceGenericKey(algID, key);
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMDecryptorProviderBuilder.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMDecryptorProviderBuilder.java
new file mode 100644
index 00000000..b54d7fd3
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMDecryptorProviderBuilder.java
@@ -0,0 +1,54 @@
+package org.spongycastle.openssl.jcajce;
+
+import java.security.Provider;
+
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.openssl.PEMDecryptor;
+import org.spongycastle.openssl.PEMDecryptorProvider;
+import org.spongycastle.openssl.PEMException;
+import org.spongycastle.openssl.PasswordException;
+
+public class JcePEMDecryptorProviderBuilder
+{
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+
+ public JcePEMDecryptorProviderBuilder setProvider(Provider provider)
+ {
+ this.helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public JcePEMDecryptorProviderBuilder setProvider(String providerName)
+ {
+ this.helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ public PEMDecryptorProvider build(final char[] password)
+ {
+ return new PEMDecryptorProvider()
+ {
+ public PEMDecryptor get(final String dekAlgName)
+ {
+ return new PEMDecryptor()
+ {
+ public byte[] decrypt(byte[] keyBytes, byte[] iv)
+ throws PEMException
+ {
+ if (password == null)
+ {
+ throw new PasswordException("Password is null, but a password is required");
+ }
+
+ return PEMUtilities.crypt(false, helper, keyBytes, password, dekAlgName, iv);
+ }
+ };
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMEncryptorBuilder.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMEncryptorBuilder.java
new file mode 100644
index 00000000..3360f8a8
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/JcePEMEncryptorBuilder.java
@@ -0,0 +1,78 @@
+package org.spongycastle.openssl.jcajce;
+
+import java.security.Provider;
+import java.security.SecureRandom;
+
+import org.spongycastle.jcajce.util.DefaultJcaJceHelper;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.jcajce.util.NamedJcaJceHelper;
+import org.spongycastle.jcajce.util.ProviderJcaJceHelper;
+import org.spongycastle.openssl.PEMEncryptor;
+import org.spongycastle.openssl.PEMException;
+
+public class JcePEMEncryptorBuilder
+{
+ private final String algorithm;
+
+ private JcaJceHelper helper = new DefaultJcaJceHelper();
+ private SecureRandom random;
+
+ public JcePEMEncryptorBuilder(String algorithm)
+ {
+ this.algorithm = algorithm;
+ }
+
+ public JcePEMEncryptorBuilder setProvider(Provider provider)
+ {
+ this.helper = new ProviderJcaJceHelper(provider);
+
+ return this;
+ }
+
+ public JcePEMEncryptorBuilder setProvider(String providerName)
+ {
+ this.helper = new NamedJcaJceHelper(providerName);
+
+ return this;
+ }
+
+ public JcePEMEncryptorBuilder setSecureRandom(SecureRandom random)
+ {
+ this.random = random;
+
+ return this;
+ }
+
+ public PEMEncryptor build(final char[] password)
+ {
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+
+ int ivLength = algorithm.startsWith("AES-") ? 16 : 8;
+
+ final byte[] iv = new byte[ivLength];
+
+ random.nextBytes(iv);
+
+ return new PEMEncryptor()
+ {
+ public String getAlgorithm()
+ {
+ return algorithm;
+ }
+
+ public byte[] getIV()
+ {
+ return iv;
+ }
+
+ public byte[] encrypt(byte[] encoding)
+ throws PEMException
+ {
+ return PEMUtilities.crypt(true, helper, encoding, password, algorithm, iv);
+ }
+ };
+ }
+}
diff --git a/pkix/src/main/java/org/spongycastle/openssl/jcajce/PEMUtilities.java b/pkix/src/main/java/org/spongycastle/openssl/jcajce/PEMUtilities.java
new file mode 100644
index 00000000..6bfa2a57
--- /dev/null
+++ b/pkix/src/main/java/org/spongycastle/openssl/jcajce/PEMUtilities.java
@@ -0,0 +1,257 @@
+package org.spongycastle.openssl.jcajce;
+
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.RC2ParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.nist.NISTObjectIdentifiers;
+import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.spongycastle.crypto.PBEParametersGenerator;
+import org.spongycastle.crypto.generators.OpenSSLPBEParametersGenerator;
+import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.jcajce.util.JcaJceHelper;
+import org.spongycastle.openssl.EncryptionException;
+import org.spongycastle.openssl.PEMException;
+import org.spongycastle.util.Integers;
+
+class PEMUtilities
+{
+ private static final Map KEYSIZES = new HashMap();
+ private static final Set PKCS5_SCHEME_1 = new HashSet();
+ private static final Set PKCS5_SCHEME_2 = new HashSet();
+
+ static
+ {
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC);
+ PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC);
+
+ PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2);
+ PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC);
+ PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC);
+ PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC);
+ PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC);
+
+ KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192));
+ KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integers.valueOf(128));
+ KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integers.valueOf(192));
+ KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integers.valueOf(256));
+ }
+
+ static int getKeySize(String algorithm)
+ {
+ if (!KEYSIZES.containsKey(algorithm))
+ {
+ throw new IllegalStateException("no key size for algorithm: " + algorithm);
+ }
+
+ return ((Integer)KEYSIZES.get(algorithm)).intValue();
+ }
+
+ static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid)
+ {
+ return PKCS5_SCHEME_1.contains(algOid);
+ }
+
+ static boolean isPKCS5Scheme2(ASN1ObjectIdentifier algOid)
+ {
+ return PKCS5_SCHEME_2.contains(algOid);
+ }
+
+ public static boolean isPKCS12(ASN1ObjectIdentifier algOid)
+ {
+ return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId());
+ }
+
+ public static SecretKey generateSecretKeyForPKCS5Scheme2(String algorithm, char[] password, byte[] salt, int iterationCount)
+ {
+ PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
+
+ generator.init(
+ PBEParametersGenerator.PKCS5PasswordToBytes(password),
+ salt,
+ iterationCount);
+
+ return new SecretKeySpec(((KeyParameter)generator.generateDerivedParameters(PEMUtilities.getKeySize(algorithm))).getKey(), algorithm);
+ }
+
+ static byte[] crypt(
+ boolean encrypt,
+ JcaJceHelper helper,
+ byte[] bytes,
+ char[] password,
+ String dekAlgName,
+ byte[] iv)
+ throws PEMException
+ {
+ AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
+ String alg;
+ String blockMode = "CBC";
+ String padding = "PKCS5Padding";
+ Key sKey;
+
+ // Figure out block mode and padding.
+ if (dekAlgName.endsWith("-CFB"))
+ {
+ blockMode = "CFB";
+ padding = "NoPadding";
+ }
+ if (dekAlgName.endsWith("-ECB") ||
+ "DES-EDE".equals(dekAlgName) ||
+ "DES-EDE3".equals(dekAlgName))
+ {
+ // ECB is actually the default (though seldom used) when OpenSSL
+ // uses DES-EDE (des2) or DES-EDE3 (des3).
+ blockMode = "ECB";
+ paramSpec = null;
+ }
+ if (dekAlgName.endsWith("-OFB"))
+ {
+ blockMode = "OFB";
+ padding = "NoPadding";
+ }
+
+
+ // Figure out algorithm and key size.
+ if (dekAlgName.startsWith("DES-EDE"))
+ {
+ alg = "DESede";
+ // "DES-EDE" is actually des2 in OpenSSL-speak!
+ // "DES-EDE3" is des3.
+ boolean des2 = !dekAlgName.startsWith("DES-EDE3");
+ sKey = getKey(password, alg, 24, iv, des2);
+ }
+ else if (dekAlgName.startsWith("DES-"))
+ {
+ alg = "DES";
+ sKey = getKey(password, alg, 8, iv);
+ }
+ else if (dekAlgName.startsWith("BF-"))
+ {
+ alg = "Blowfish";
+ sKey = getKey(password, alg, 16, iv);
+ }
+ else if (dekAlgName.startsWith("RC2-"))
+ {
+ alg = "RC2";
+ int keyBits = 128;
+ if (dekAlgName.startsWith("RC2-40-"))
+ {
+ keyBits = 40;
+ }
+ else if (dekAlgName.startsWith("RC2-64-"))
+ {
+ keyBits = 64;
+ }
+ sKey = getKey(password, alg, keyBits / 8, iv);
+ if (paramSpec == null) // ECB block mode
+ {
+ paramSpec = new RC2ParameterSpec(keyBits);
+ }
+ else
+ {
+ paramSpec = new RC2ParameterSpec(keyBits, iv);
+ }
+ }
+ else if (dekAlgName.startsWith("AES-"))
+ {
+ alg = "AES";
+ byte[] salt = iv;
+ if (salt.length > 8)
+ {
+ salt = new byte[8];
+ System.arraycopy(iv, 0, salt, 0, 8);
+ }
+
+ int keyBits;
+ if (dekAlgName.startsWith("AES-128-"))
+ {
+ keyBits = 128;
+ }
+ else if (dekAlgName.startsWith("AES-192-"))
+ {
+ keyBits = 192;
+ }
+ else if (dekAlgName.startsWith("AES-256-"))
+ {
+ keyBits = 256;
+ }
+ else
+ {
+ throw new EncryptionException("unknown AES encryption with private key");
+ }
+ sKey = getKey(password, "AES", keyBits / 8, salt);
+ }
+ else
+ {
+ throw new EncryptionException("unknown encryption with private key");
+ }
+
+ String transformation = alg + "/" + blockMode + "/" + padding;
+
+ try
+ {
+ Cipher c = helper.createCipher(transformation);
+ int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
+
+ if (paramSpec == null) // ECB block mode
+ {
+ c.init(mode, sKey);
+ }
+ else
+ {
+ c.init(mode, sKey, paramSpec);
+ }
+ return c.doFinal(bytes);
+ }
+ catch (Exception e)
+ {
+ throw new EncryptionException("exception using cipher - please check password and data.", e);
+ }
+ }
+
+ private static SecretKey getKey(
+ char[] password,
+ String algorithm,
+ int keyLength,
+ byte[] salt)
+ {
+ return getKey(password, algorithm, keyLength, salt, false);
+ }
+
+ private static SecretKey getKey(
+ char[] password,
+ String algorithm,
+ int keyLength,
+ byte[] salt,
+ boolean des2)
+ {
+ OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator();
+
+ pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt);
+
+ KeyParameter keyParam;
+ keyParam = (KeyParameter) pGen.generateDerivedParameters(keyLength * 8);
+ byte[] key = keyParam.getKey();
+ if (des2 && key.length >= 24)
+ {
+ // For DES2, we must copy first 8 bytes into the last 8 bytes.
+ System.arraycopy(key, 0, key, 16, 8);
+ }
+ return new SecretKeySpec(key, algorithm);
+ }
+}