diff --git a/crypto/src/openssl/EncryptionException.cs b/crypto/src/openssl/EncryptionException.cs
new file mode 100644
index 000000000..c4a6ec02f
--- /dev/null
+++ b/crypto/src/openssl/EncryptionException.cs
@@ -0,0 +1,25 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Security
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+ [Serializable]
+#endif
+ public class EncryptionException
+ : IOException
+ {
+ public EncryptionException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public EncryptionException(
+ string message,
+ Exception exception)
+ : base(message, exception)
+ {
+ }
+ }
+}
diff --git a/crypto/src/openssl/IPasswordFinder.cs b/crypto/src/openssl/IPasswordFinder.cs
new file mode 100644
index 000000000..4fcef1bd7
--- /dev/null
+++ b/crypto/src/openssl/IPasswordFinder.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Org.BouncyCastle.OpenSsl
+{
+ public interface IPasswordFinder
+ {
+ char[] GetPassword();
+ }
+}
diff --git a/crypto/src/openssl/MiscPemGenerator.cs b/crypto/src/openssl/MiscPemGenerator.cs
new file mode 100644
index 000000000..c4c537904
--- /dev/null
+++ b/crypto/src/openssl/MiscPemGenerator.cs
@@ -0,0 +1,276 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO.Pem;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.OpenSsl
+{
+ /**
+ * PEM generator for the original set of PEM objects used in Open SSL.
+ */
+ public class MiscPemGenerator
+ : PemObjectGenerator
+ {
+ private object obj;
+ private string algorithm;
+ private char[] password;
+ private SecureRandom random;
+
+ public MiscPemGenerator(object obj)
+ {
+ this.obj = obj;
+ }
+
+ public MiscPemGenerator(
+ object obj,
+ string algorithm,
+ char[] password,
+ SecureRandom random)
+ {
+ this.obj = obj;
+ this.algorithm = algorithm;
+ this.password = password;
+ this.random = random;
+ }
+
+ private static PemObject CreatePemObject(object obj)
+ {
+ if (obj == null)
+ throw new ArgumentNullException("obj");
+
+ if (obj is AsymmetricCipherKeyPair)
+ {
+ return CreatePemObject(((AsymmetricCipherKeyPair)obj).Private);
+ }
+
+ string type;
+ byte[] encoding;
+
+ if (obj is PemObject)
+ return (PemObject)obj;
+
+ if (obj is PemObjectGenerator)
+ return ((PemObjectGenerator)obj).Generate();
+
+ if (obj is X509Certificate)
+ {
+ // TODO Should we prefer "X509 CERTIFICATE" here?
+ type = "CERTIFICATE";
+ try
+ {
+ encoding = ((X509Certificate)obj).GetEncoded();
+ }
+ catch (CertificateEncodingException e)
+ {
+ throw new IOException("Cannot Encode object: " + e.ToString());
+ }
+ }
+ else if (obj is X509Crl)
+ {
+ type = "X509 CRL";
+ try
+ {
+ encoding = ((X509Crl)obj).GetEncoded();
+ }
+ catch (CrlException e)
+ {
+ throw new IOException("Cannot Encode object: " + e.ToString());
+ }
+ }
+ else if (obj is AsymmetricKeyParameter)
+ {
+ AsymmetricKeyParameter akp = (AsymmetricKeyParameter) obj;
+ if (akp.IsPrivate)
+ {
+ string keyType;
+ encoding = EncodePrivateKey(akp, out keyType);
+
+ type = keyType + " PRIVATE KEY";
+ }
+ else
+ {
+ type = "PUBLIC KEY";
+
+ encoding = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(akp).GetDerEncoded();
+ }
+ }
+ else if (obj is IX509AttributeCertificate)
+ {
+ type = "ATTRIBUTE CERTIFICATE";
+ encoding = ((X509V2AttributeCertificate)obj).GetEncoded();
+ }
+ else if (obj is Pkcs10CertificationRequest)
+ {
+ type = "CERTIFICATE REQUEST";
+ encoding = ((Pkcs10CertificationRequest)obj).GetEncoded();
+ }
+ else if (obj is Asn1.Cms.ContentInfo)
+ {
+ type = "PKCS7";
+ encoding = ((Asn1.Cms.ContentInfo)obj).GetEncoded();
+ }
+ else
+ {
+ throw new PemGenerationException("Object type not supported: " + obj.GetType().FullName);
+ }
+
+ return new PemObject(type, encoding);
+ }
+
+// private string GetHexEncoded(byte[] bytes)
+// {
+// bytes = Hex.Encode(bytes);
+//
+// char[] chars = new char[bytes.Length];
+//
+// for (int i = 0; i != bytes.Length; i++)
+// {
+// chars[i] = (char)bytes[i];
+// }
+//
+// return new string(chars);
+// }
+
+ private static PemObject CreatePemObject(
+ object obj,
+ string algorithm,
+ char[] password,
+ SecureRandom random)
+ {
+ if (obj == null)
+ throw new ArgumentNullException("obj");
+ if (algorithm == null)
+ throw new ArgumentNullException("algorithm");
+ if (password == null)
+ throw new ArgumentNullException("password");
+ if (random == null)
+ throw new ArgumentNullException("random");
+
+ if (obj is AsymmetricCipherKeyPair)
+ {
+ return CreatePemObject(((AsymmetricCipherKeyPair)obj).Private, algorithm, password, random);
+ }
+
+ string type = null;
+ byte[] keyData = null;
+
+ if (obj is AsymmetricKeyParameter)
+ {
+ AsymmetricKeyParameter akp = (AsymmetricKeyParameter) obj;
+ if (akp.IsPrivate)
+ {
+ string keyType;
+ keyData = EncodePrivateKey(akp, out keyType);
+
+ type = keyType + " PRIVATE KEY";
+ }
+ }
+
+ if (type == null || keyData == null)
+ {
+ // TODO Support other types?
+ throw new PemGenerationException("Object type not supported: " + obj.GetType().FullName);
+ }
+
+
+ string dekAlgName = Platform.ToUpperInvariant(algorithm);
+
+ // Note: For backward compatibility
+ if (dekAlgName == "DESEDE")
+ {
+ dekAlgName = "DES-EDE3-CBC";
+ }
+
+ int ivLength = dekAlgName.StartsWith("AES-") ? 16 : 8;
+
+ byte[] iv = new byte[ivLength];
+ random.NextBytes(iv);
+
+ byte[] encData = PemUtilities.Crypt(true, keyData, password, dekAlgName, iv);
+
+ IList headers = Platform.CreateArrayList(2);
+
+ headers.Add(new PemHeader("Proc-Type", "4,ENCRYPTED"));
+ headers.Add(new PemHeader("DEK-Info", dekAlgName + "," + Hex.ToHexString(iv)));
+
+ return new PemObject(type, headers, encData);
+ }
+
+ private static byte[] EncodePrivateKey(
+ AsymmetricKeyParameter akp,
+ out string keyType)
+ {
+ PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp);
+
+ DerObjectIdentifier oid = info.AlgorithmID.ObjectID;
+
+ if (oid.Equals(X9ObjectIdentifiers.IdDsa))
+ {
+ keyType = "DSA";
+
+ DsaParameter p = DsaParameter.GetInstance(info.AlgorithmID.Parameters);
+
+ BigInteger x = ((DsaPrivateKeyParameters) akp).X;
+ BigInteger y = p.G.ModPow(x, p.P);
+
+ // TODO Create an ASN1 object somewhere for this?
+ return new DerSequence(
+ new DerInteger(0),
+ new DerInteger(p.P),
+ new DerInteger(p.Q),
+ new DerInteger(p.G),
+ new DerInteger(y),
+ new DerInteger(x)).GetEncoded();
+ }
+
+ if (oid.Equals(PkcsObjectIdentifiers.RsaEncryption))
+ {
+ keyType = "RSA";
+ }
+ else if (oid.Equals(CryptoProObjectIdentifiers.GostR3410x2001)
+ || oid.Equals(X9ObjectIdentifiers.IdECPublicKey))
+ {
+ keyType = "EC";
+ }
+ else
+ {
+ throw new ArgumentException("Cannot handle private key of type: " + akp.GetType().FullName, "akp");
+ }
+
+ return info.PrivateKey.GetEncoded();
+ }
+
+ public PemObject Generate()
+ {
+ try
+ {
+ if (algorithm != null)
+ {
+ return CreatePemObject(obj, algorithm, password, random);
+ }
+
+ return CreatePemObject(obj);
+ }
+ catch (IOException e)
+ {
+ throw new PemGenerationException("encoding exception", e);
+ }
+ }
+ }
+}
diff --git a/crypto/src/openssl/PEMException.cs b/crypto/src/openssl/PEMException.cs
new file mode 100644
index 000000000..4d33a2a1f
--- /dev/null
+++ b/crypto/src/openssl/PEMException.cs
@@ -0,0 +1,25 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.OpenSsl
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+ [Serializable]
+#endif
+ public class PemException
+ : IOException
+ {
+ public PemException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public PemException(
+ string message,
+ Exception exception)
+ : base(message, exception)
+ {
+ }
+ }
+}
diff --git a/crypto/src/openssl/PEMReader.cs b/crypto/src/openssl/PEMReader.cs
new file mode 100644
index 000000000..a2fedab96
--- /dev/null
+++ b/crypto/src/openssl/PEMReader.cs
@@ -0,0 +1,407 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO.Pem;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.OpenSsl
+{
+ /**
+ * Class for reading 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. Keys and
+ * Certificates will be returned using the appropriate java.security type.</p>
+ */
+ public class PemReader
+ : Org.BouncyCastle.Utilities.IO.Pem.PemReader
+ {
+// private static readonly IDictionary parsers = new Hashtable();
+
+ static PemReader()
+ {
+// parsers.Add("CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
+// parsers.Add("NEW CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
+// parsers.Add("CERTIFICATE", new X509CertificateParser(provider));
+// parsers.Add("X509 CERTIFICATE", new X509CertificateParser(provider));
+// parsers.Add("X509 CRL", new X509CRLParser(provider));
+// parsers.Add("PKCS7", new PKCS7Parser());
+// parsers.Add("ATTRIBUTE CERTIFICATE", new X509AttributeCertificateParser());
+// parsers.Add("EC PARAMETERS", new ECNamedCurveSpecParser());
+// parsers.Add("PUBLIC KEY", new PublicKeyParser(provider));
+// parsers.Add("RSA PUBLIC KEY", new RSAPublicKeyParser(provider));
+// parsers.Add("RSA PRIVATE KEY", new RSAKeyPairParser(provider));
+// parsers.Add("DSA PRIVATE KEY", new DSAKeyPairParser(provider));
+// parsers.Add("EC PRIVATE KEY", new ECDSAKeyPairParser(provider));
+// parsers.Add("ENCRYPTED PRIVATE KEY", new EncryptedPrivateKeyParser(provider));
+// parsers.Add("PRIVATE KEY", new PrivateKeyParser(provider));
+ }
+
+ private readonly IPasswordFinder pFinder;
+
+ /**
+ * Create a new PemReader
+ *
+ * @param reader the Reader
+ */
+ public PemReader(
+ TextReader reader)
+ : this(reader, null)
+ {
+ }
+
+ /**
+ * Create a new PemReader with a password finder
+ *
+ * @param reader the Reader
+ * @param pFinder the password finder
+ */
+ public PemReader(
+ TextReader reader,
+ IPasswordFinder pFinder)
+ : base(reader)
+ {
+ this.pFinder = pFinder;
+ }
+
+ public object ReadObject()
+ {
+ PemObject obj = ReadPemObject();
+
+ if (obj == null)
+ return null;
+
+ // TODO Follow Java build and map to parser objects?
+// if (parsers.Contains(obj.Type))
+// return ((PemObjectParser)parsers[obj.Type]).ParseObject(obj);
+
+ if (obj.Type.EndsWith("PRIVATE KEY"))
+ return ReadPrivateKey(obj);
+
+ switch (obj.Type)
+ {
+ case "PUBLIC KEY":
+ return ReadPublicKey(obj);
+ case "RSA PUBLIC KEY":
+ return ReadRsaPublicKey(obj);
+ case "CERTIFICATE REQUEST":
+ case "NEW CERTIFICATE REQUEST":
+ return ReadCertificateRequest(obj);
+ case "CERTIFICATE":
+ case "X509 CERTIFICATE":
+ return ReadCertificate(obj);
+ case "PKCS7":
+ return ReadPkcs7(obj);
+ case "X509 CRL":
+ return ReadCrl(obj);
+ case "ATTRIBUTE CERTIFICATE":
+ return ReadAttributeCertificate(obj);
+ // TODO Add back in when tests done, and return type issue resolved
+ //case "EC PARAMETERS":
+ // return ReadECParameters(obj);
+ default:
+ throw new IOException("unrecognised object: " + obj.Type);
+ }
+ }
+
+ private AsymmetricKeyParameter ReadRsaPublicKey(PemObject pemObject)
+ {
+ RsaPublicKeyStructure rsaPubStructure = RsaPublicKeyStructure.GetInstance(
+ Asn1Object.FromByteArray(pemObject.Content));
+
+ return new RsaKeyParameters(
+ false, // not private
+ rsaPubStructure.Modulus,
+ rsaPubStructure.PublicExponent);
+ }
+
+ private AsymmetricKeyParameter ReadPublicKey(PemObject pemObject)
+ {
+ return PublicKeyFactory.CreateKey(pemObject.Content);
+ }
+
+ /**
+ * Reads in a X509Certificate.
+ *
+ * @return the X509Certificate
+ * @throws IOException if an I/O error occured
+ */
+ private X509Certificate ReadCertificate(PemObject pemObject)
+ {
+ try
+ {
+ return new X509CertificateParser().ReadCertificate(pemObject.Content);
+ }
+ catch (Exception e)
+ {
+ throw new PemException("problem parsing cert: " + e.ToString());
+ }
+ }
+
+ /**
+ * Reads in a X509CRL.
+ *
+ * @return the X509Certificate
+ * @throws IOException if an I/O error occured
+ */
+ private X509Crl ReadCrl(PemObject pemObject)
+ {
+ try
+ {
+ return new X509CrlParser().ReadCrl(pemObject.Content);
+ }
+ catch (Exception e)
+ {
+ throw new PemException("problem parsing cert: " + e.ToString());
+ }
+ }
+
+ /**
+ * Reads in a PKCS10 certification request.
+ *
+ * @return the certificate request.
+ * @throws IOException if an I/O error occured
+ */
+ private Pkcs10CertificationRequest ReadCertificateRequest(PemObject pemObject)
+ {
+ try
+ {
+ return new Pkcs10CertificationRequest(pemObject.Content);
+ }
+ catch (Exception e)
+ {
+ throw new PemException("problem parsing cert: " + e.ToString());
+ }
+ }
+
+ /**
+ * Reads in a X509 Attribute Certificate.
+ *
+ * @return the X509 Attribute Certificate
+ * @throws IOException if an I/O error occured
+ */
+ private IX509AttributeCertificate ReadAttributeCertificate(PemObject pemObject)
+ {
+ return new X509V2AttributeCertificate(pemObject.Content);
+ }
+
+ /**
+ * Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS
+ * API.
+ *
+ * @return the X509Certificate
+ * @throws IOException if an I/O error occured
+ */
+ // TODO Consider returning Asn1.Pkcs.ContentInfo
+ private Asn1.Cms.ContentInfo ReadPkcs7(PemObject pemObject)
+ {
+ try
+ {
+ return Asn1.Cms.ContentInfo.GetInstance(
+ Asn1Object.FromByteArray(pemObject.Content));
+ }
+ catch (Exception e)
+ {
+ throw new PemException("problem parsing PKCS7 object: " + e.ToString());
+ }
+ }
+
+ /**
+ * Read a Key Pair
+ */
+ private object ReadPrivateKey(PemObject pemObject)
+ {
+ //
+ // extract the key
+ //
+ Debug.Assert(pemObject.Type.EndsWith("PRIVATE KEY"));
+
+ string type = pemObject.Type.Substring(0, pemObject.Type.Length - "PRIVATE KEY".Length).Trim();
+ byte[] keyBytes = pemObject.Content;
+
+ IDictionary fields = Platform.CreateHashtable();
+ foreach (PemHeader header in pemObject.Headers)
+ {
+ fields[header.Name] = header.Value;
+ }
+
+ string procType = (string) fields["Proc-Type"];
+
+ if (procType == "4,ENCRYPTED")
+ {
+ if (pFinder == null)
+ throw new PasswordException("No password finder specified, but a password is required");
+
+ char[] password = pFinder.GetPassword();
+
+ if (password == null)
+ throw new PasswordException("Password is null, but a password is required");
+
+ string dekInfo = (string) fields["DEK-Info"];
+ string[] tknz = dekInfo.Split(',');
+
+ string dekAlgName = tknz[0].Trim();
+ byte[] iv = Hex.Decode(tknz[1].Trim());
+
+ keyBytes = PemUtilities.Crypt(false, keyBytes, password, dekAlgName, iv);
+ }
+
+ try
+ {
+ AsymmetricKeyParameter pubSpec, privSpec;
+ Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(keyBytes);
+
+ switch (type)
+ {
+ case "RSA":
+ {
+ if (seq.Count != 9)
+ throw new PemException("malformed sequence in RSA private key");
+
+ RsaPrivateKeyStructure rsa = new RsaPrivateKeyStructure(seq);
+
+ pubSpec = new RsaKeyParameters(false, rsa.Modulus, rsa.PublicExponent);
+ privSpec = new RsaPrivateCrtKeyParameters(
+ rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent,
+ rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2,
+ rsa.Coefficient);
+
+ break;
+ }
+
+ case "DSA":
+ {
+ if (seq.Count != 6)
+ throw new PemException("malformed sequence in DSA private key");
+
+ // TODO Create an ASN1 object somewhere for this?
+ //DerInteger v = (DerInteger)seq[0];
+ DerInteger p = (DerInteger)seq[1];
+ DerInteger q = (DerInteger)seq[2];
+ DerInteger g = (DerInteger)seq[3];
+ DerInteger y = (DerInteger)seq[4];
+ DerInteger x = (DerInteger)seq[5];
+
+ DsaParameters parameters = new DsaParameters(p.Value, q.Value, g.Value);
+
+ privSpec = new DsaPrivateKeyParameters(x.Value, parameters);
+ pubSpec = new DsaPublicKeyParameters(y.Value, parameters);
+
+ break;
+ }
+
+ case "EC":
+ {
+ ECPrivateKeyStructure pKey = new ECPrivateKeyStructure(seq);
+ AlgorithmIdentifier algId = new AlgorithmIdentifier(
+ X9ObjectIdentifiers.IdECPublicKey, pKey.GetParameters());
+
+ PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey.ToAsn1Object());
+
+ // TODO Are the keys returned here ECDSA, as Java version forces?
+ privSpec = PrivateKeyFactory.CreateKey(privInfo);
+
+ DerBitString pubKey = pKey.GetPublicKey();
+ if (pubKey != null)
+ {
+ SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pubKey.GetBytes());
+
+ // TODO Are the keys returned here ECDSA, as Java version forces?
+ pubSpec = PublicKeyFactory.CreateKey(pubInfo);
+ }
+ else
+ {
+ pubSpec = ECKeyPairGenerator.GetCorrespondingPublicKey(
+ (ECPrivateKeyParameters)privSpec);
+ }
+
+ break;
+ }
+
+ case "ENCRYPTED":
+ {
+ char[] password = pFinder.GetPassword();
+
+ if (password == null)
+ throw new PasswordException("Password is null, but a password is required");
+
+ return PrivateKeyFactory.DecryptKey(password, EncryptedPrivateKeyInfo.GetInstance(seq));
+ }
+
+ case "":
+ {
+ return PrivateKeyFactory.CreateKey(PrivateKeyInfo.GetInstance(seq));
+ }
+
+ default:
+ throw new ArgumentException("Unknown key type: " + type, "type");
+ }
+
+ return new AsymmetricCipherKeyPair(pubSpec, privSpec);
+ }
+ catch (IOException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new PemException(
+ "problem creating " + type + " private key: " + e.ToString());
+ }
+ }
+
+ // TODO Add an equivalent class for ECNamedCurveParameterSpec?
+ //private ECNamedCurveParameterSpec ReadECParameters(
+// private X9ECParameters ReadECParameters(PemObject pemObject)
+// {
+// DerObjectIdentifier oid = (DerObjectIdentifier)Asn1Object.FromByteArray(pemObject.Content);
+//
+// //return ECNamedCurveTable.getParameterSpec(oid.Id);
+// return GetCurveParameters(oid.Id);
+// }
+
+ //private static ECDomainParameters GetCurveParameters(
+ private static X9ECParameters GetCurveParameters(
+ string name)
+ {
+ // TODO ECGost3410NamedCurves support (returns ECDomainParameters though)
+ X9ECParameters ecP = X962NamedCurves.GetByName(name);
+
+ if (ecP == null)
+ {
+ ecP = SecNamedCurves.GetByName(name);
+ if (ecP == null)
+ {
+ ecP = NistNamedCurves.GetByName(name);
+ if (ecP == null)
+ {
+ ecP = TeleTrusTNamedCurves.GetByName(name);
+
+ if (ecP == null)
+ throw new Exception("unknown curve name: " + name);
+ }
+ }
+ }
+
+ //return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
+ return ecP;
+ }
+ }
+}
diff --git a/crypto/src/openssl/PEMUtilities.cs b/crypto/src/openssl/PEMUtilities.cs
new file mode 100644
index 000000000..b58e5e765
--- /dev/null
+++ b/crypto/src/openssl/PEMUtilities.cs
@@ -0,0 +1,158 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.OpenSsl
+{
+ internal sealed class PemUtilities
+ {
+ private enum PemBaseAlg { AES_128, AES_192, AES_256, BF, DES, DES_EDE, DES_EDE3, RC2, RC2_40, RC2_64 };
+ private enum PemMode { CBC, CFB, ECB, OFB };
+
+ static PemUtilities()
+ {
+ // Signal to obfuscation tools not to change enum constants
+ ((PemBaseAlg)Enums.GetArbitraryValue(typeof(PemBaseAlg))).ToString();
+ ((PemMode)Enums.GetArbitraryValue(typeof(PemMode))).ToString();
+ }
+
+ private static void ParseDekAlgName(
+ string dekAlgName,
+ out PemBaseAlg baseAlg,
+ out PemMode mode)
+ {
+ try
+ {
+ mode = PemMode.ECB;
+
+ if (dekAlgName == "DES-EDE" || dekAlgName == "DES-EDE3")
+ {
+ baseAlg = (PemBaseAlg)Enums.GetEnumValue(typeof(PemBaseAlg), dekAlgName);
+ return;
+ }
+
+ int pos = dekAlgName.LastIndexOf('-');
+ if (pos >= 0)
+ {
+ baseAlg = (PemBaseAlg)Enums.GetEnumValue(typeof(PemBaseAlg), dekAlgName.Substring(0, pos));
+ mode = (PemMode)Enums.GetEnumValue(typeof(PemMode), dekAlgName.Substring(pos + 1));
+ return;
+ }
+ }
+ catch (ArgumentException)
+ {
+ }
+
+ throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName);
+ }
+
+ internal static byte[] Crypt(
+ bool encrypt,
+ byte[] bytes,
+ char[] password,
+ string dekAlgName,
+ byte[] iv)
+ {
+ PemBaseAlg baseAlg;
+ PemMode mode;
+ ParseDekAlgName(dekAlgName, out baseAlg, out mode);
+
+ string padding;
+ switch (mode)
+ {
+ case PemMode.CBC:
+ case PemMode.ECB:
+ padding = "PKCS5Padding";
+ break;
+ case PemMode.CFB:
+ case PemMode.OFB:
+ padding = "NoPadding";
+ break;
+ default:
+ throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName);
+ }
+
+ string algorithm;
+
+ byte[] salt = iv;
+ switch (baseAlg)
+ {
+ case PemBaseAlg.AES_128:
+ case PemBaseAlg.AES_192:
+ case PemBaseAlg.AES_256:
+ algorithm = "AES";
+ if (salt.Length > 8)
+ {
+ salt = new byte[8];
+ Array.Copy(iv, 0, salt, 0, salt.Length);
+ }
+ break;
+ case PemBaseAlg.BF:
+ algorithm = "BLOWFISH";
+ break;
+ case PemBaseAlg.DES:
+ algorithm = "DES";
+ break;
+ case PemBaseAlg.DES_EDE:
+ case PemBaseAlg.DES_EDE3:
+ algorithm = "DESede";
+ break;
+ case PemBaseAlg.RC2:
+ case PemBaseAlg.RC2_40:
+ case PemBaseAlg.RC2_64:
+ algorithm = "RC2";
+ break;
+ default:
+ throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName);
+ }
+
+ string cipherName = algorithm + "/" + mode + "/" + padding;
+ IBufferedCipher cipher = CipherUtilities.GetCipher(cipherName);
+
+ ICipherParameters cParams = GetCipherParameters(password, baseAlg, salt);
+
+ if (mode != PemMode.ECB)
+ {
+ cParams = new ParametersWithIV(cParams, iv);
+ }
+
+ cipher.Init(encrypt, cParams);
+
+ return cipher.DoFinal(bytes);
+ }
+
+ private static ICipherParameters GetCipherParameters(
+ char[] password,
+ PemBaseAlg baseAlg,
+ byte[] salt)
+ {
+ string algorithm;
+ int keyBits;
+ switch (baseAlg)
+ {
+ case PemBaseAlg.AES_128: keyBits = 128; algorithm = "AES128"; break;
+ case PemBaseAlg.AES_192: keyBits = 192; algorithm = "AES192"; break;
+ case PemBaseAlg.AES_256: keyBits = 256; algorithm = "AES256"; break;
+ case PemBaseAlg.BF: keyBits = 128; algorithm = "BLOWFISH"; break;
+ case PemBaseAlg.DES: keyBits = 64; algorithm = "DES"; break;
+ case PemBaseAlg.DES_EDE: keyBits = 128; algorithm = "DESEDE"; break;
+ case PemBaseAlg.DES_EDE3: keyBits = 192; algorithm = "DESEDE3"; break;
+ case PemBaseAlg.RC2: keyBits = 128; algorithm = "RC2"; break;
+ case PemBaseAlg.RC2_40: keyBits = 40; algorithm = "RC2"; break;
+ case PemBaseAlg.RC2_64: keyBits = 64; algorithm = "RC2"; break;
+ default:
+ return null;
+ }
+
+ OpenSslPbeParametersGenerator pGen = new OpenSslPbeParametersGenerator();
+
+ pGen.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password), salt);
+
+ return pGen.GenerateDerivedParameters(algorithm, keyBits);
+ }
+ }
+}
diff --git a/crypto/src/openssl/PEMWriter.cs b/crypto/src/openssl/PEMWriter.cs
new file mode 100644
index 000000000..aefb018f3
--- /dev/null
+++ b/crypto/src/openssl/PEMWriter.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO.Pem;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.OpenSsl
+{
+ /// <remarks>General purpose writer for OpenSSL PEM objects.</remarks>
+ public class PemWriter
+ : Org.BouncyCastle.Utilities.IO.Pem.PemWriter
+ {
+ /// <param name="writer">The TextWriter object to write the output to.</param>
+ public PemWriter(
+ TextWriter writer)
+ : base(writer)
+ {
+ }
+
+ public void WriteObject(
+ object obj)
+ {
+ try
+ {
+ base.WriteObject(new MiscPemGenerator(obj));
+ }
+ catch (PemGenerationException e)
+ {
+ if (e.InnerException is IOException)
+ throw (IOException)e.InnerException;
+
+ throw e;
+ }
+ }
+
+ public void WriteObject(
+ object obj,
+ string algorithm,
+ char[] password,
+ SecureRandom random)
+ {
+ base.WriteObject(new MiscPemGenerator(obj, algorithm, password, random));
+ }
+ }
+}
diff --git a/crypto/src/openssl/PasswordException.cs b/crypto/src/openssl/PasswordException.cs
new file mode 100644
index 000000000..fba958aa0
--- /dev/null
+++ b/crypto/src/openssl/PasswordException.cs
@@ -0,0 +1,25 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Security
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+ [Serializable]
+#endif
+ public class PasswordException
+ : IOException
+ {
+ public PasswordException(
+ string message)
+ : base(message)
+ {
+ }
+
+ public PasswordException(
+ string message,
+ Exception exception)
+ : base(message, exception)
+ {
+ }
+ }
+}
diff --git a/crypto/src/openssl/Pkcs8Generator.cs b/crypto/src/openssl/Pkcs8Generator.cs
new file mode 100644
index 000000000..d03ea08d2
--- /dev/null
+++ b/crypto/src/openssl/Pkcs8Generator.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO.Pem;
+
+namespace Org.BouncyCastle.OpenSsl
+{
+ public class Pkcs8Generator
+ : PemObjectGenerator
+ {
+ // FIXME See PbeUtilities static constructor
+// public static readonly string Aes128Cbc = NistObjectIdentifiers.IdAes128Cbc.Id;
+// public static readonly string Aes192Cbc = NistObjectIdentifiers.IdAes192Cbc.Id;
+// public static readonly string Aes256Cbc = NistObjectIdentifiers.IdAes256Cbc.Id;
+//
+// public static readonly string Des3Cbc = PkcsObjectIdentifiers.DesEde3Cbc.Id;
+
+ public static readonly string PbeSha1_RC4_128 = PkcsObjectIdentifiers.PbeWithShaAnd128BitRC4.Id;
+ public static readonly string PbeSha1_RC4_40 = PkcsObjectIdentifiers.PbeWithShaAnd40BitRC4.Id;
+ public static readonly string PbeSha1_3DES = PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc.Id;
+ public static readonly string PbeSha1_2DES = PkcsObjectIdentifiers.PbeWithShaAnd2KeyTripleDesCbc.Id;
+ public static readonly string PbeSha1_RC2_128 = PkcsObjectIdentifiers.PbeWithShaAnd128BitRC2Cbc.Id;
+ public static readonly string PbeSha1_RC2_40 = PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc.Id;
+
+ private char[] password;
+ private string algorithm;
+ private int iterationCount;
+ private AsymmetricKeyParameter privKey;
+ private SecureRandom random;
+
+ /**
+ * Constructor for an unencrypted private key PEM object.
+ *
+ * @param key private key to be encoded.
+ */
+ public Pkcs8Generator(AsymmetricKeyParameter privKey)
+ {
+ this.privKey = privKey;
+ }
+
+ /**
+ * Constructor for an encrypted private key PEM object.
+ *
+ * @param key private key to be encoded
+ * @param algorithm encryption algorithm to use
+ * @param provider provider to use
+ * @throws NoSuchAlgorithmException if algorithm/mode cannot be found
+ */
+ public Pkcs8Generator(AsymmetricKeyParameter privKey, string algorithm)
+ {
+ // TODO Check privKey.IsPrivate
+ this.privKey = privKey;
+ this.algorithm = algorithm;
+ this.iterationCount = 2048;
+ }
+
+ public SecureRandom SecureRandom
+ {
+ set { this.random = value; }
+ }
+
+ public char[] Password
+ {
+ set { this.password = value; }
+ }
+
+ public int IterationCount
+ {
+ set { this.iterationCount = value; }
+ }
+
+ public PemObject Generate()
+ {
+ if (algorithm == null)
+ {
+ PrivateKeyInfo pki = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privKey);
+
+ return new PemObject("PRIVATE KEY", pki.GetEncoded());
+ }
+
+ // TODO Theoretically, the amount of salt needed depends on the algorithm
+ byte[] salt = new byte[20];
+ if (random == null)
+ {
+ random = new SecureRandom();
+ }
+ random.NextBytes(salt);
+
+ try
+ {
+ EncryptedPrivateKeyInfo epki = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(
+ algorithm, password, salt, iterationCount, privKey);
+
+ return new PemObject("ENCRYPTED PRIVATE KEY", epki.GetEncoded());
+ }
+ catch (Exception e)
+ {
+ throw new PemGenerationException("Couldn't encrypt private key", e);
+ }
+ }
+ }
+}
|