summary refs log tree commit diff
path: root/crypto/src/openssl
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2013-06-28 15:26:06 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2013-06-28 15:26:06 +0700
commit44288db4414158ac9b98a507b15e81d0d3c66ca6 (patch)
treeaa5ef88948ebb68ed6c8df81eb5da889641a9b50 /crypto/src/openssl
parentSet up text/binary handling for existing file types (diff)
downloadBouncyCastle.NET-ed25519-44288db4414158ac9b98a507b15e81d0d3c66ca6.tar.xz
Initial import of old CVS repository
Diffstat (limited to 'crypto/src/openssl')
-rw-r--r--crypto/src/openssl/EncryptionException.cs25
-rw-r--r--crypto/src/openssl/IPasswordFinder.cs9
-rw-r--r--crypto/src/openssl/MiscPemGenerator.cs276
-rw-r--r--crypto/src/openssl/PEMException.cs25
-rw-r--r--crypto/src/openssl/PEMReader.cs407
-rw-r--r--crypto/src/openssl/PEMUtilities.cs158
-rw-r--r--crypto/src/openssl/PEMWriter.cs61
-rw-r--r--crypto/src/openssl/PasswordException.cs25
-rw-r--r--crypto/src/openssl/Pkcs8Generator.cs111
9 files changed, 1097 insertions, 0 deletions
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);
+			}
+		}
+	}
+}