summary refs log tree commit diff
path: root/crypto/src/pkcs
diff options
context:
space:
mode:
authorOren Novotny <oren@novotny.org>2014-02-26 10:08:50 -0500
committerOren Novotny <oren@novotny.org>2014-02-26 10:08:50 -0500
commit4816fdea71230c76b1b5b43d61e5f29824851fdf (patch)
treef29c97c3341c7ac862ebd98452d1bad9e00738fb /crypto/src/pkcs
parentAdd git files (diff)
downloadBouncyCastle.NET-ed25519-4816fdea71230c76b1b5b43d61e5f29824851fdf.tar.xz
Add BouncyCastle PCL files
Diffstat (limited to 'crypto/src/pkcs')
-rw-r--r--crypto/src/pkcs/AsymmetricKeyEntry.cs60
-rw-r--r--crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs75
-rw-r--r--crypto/src/pkcs/PKCS12StoreBuilder.cs41
-rw-r--r--crypto/src/pkcs/Pkcs10CertificationRequest.cs466
-rw-r--r--crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs150
-rw-r--r--crypto/src/pkcs/Pkcs12Entry.cs64
-rw-r--r--crypto/src/pkcs/Pkcs12Store.cs1231
-rw-r--r--crypto/src/pkcs/Pkcs12Utilities.cs77
-rw-r--r--crypto/src/pkcs/PrivateKeyInfoFactory.cs214
-rw-r--r--crypto/src/pkcs/X509CertificateEntry.cs60
10 files changed, 2438 insertions, 0 deletions
diff --git a/crypto/src/pkcs/AsymmetricKeyEntry.cs b/crypto/src/pkcs/AsymmetricKeyEntry.cs
new file mode 100644
index 000000000..6da3ade3e
--- /dev/null
+++ b/crypto/src/pkcs/AsymmetricKeyEntry.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pkcs
+{
+    public class AsymmetricKeyEntry
+        : Pkcs12Entry
+    {
+        private readonly AsymmetricKeyParameter key;
+
+		public AsymmetricKeyEntry(
+            AsymmetricKeyParameter key)
+			: base(Platform.CreateHashtable())
+        {
+            this.key = key;
+        }
+
+#if !(SILVERLIGHT || PORTABLE)
+        [Obsolete]
+        public AsymmetricKeyEntry(
+            AsymmetricKeyParameter key,
+            Hashtable attributes)
+			: base(attributes)
+        {
+            this.key = key;
+        }
+#endif
+
+        public AsymmetricKeyEntry(
+            AsymmetricKeyParameter  key,
+            IDictionary             attributes)
+			: base(attributes)
+        {
+            this.key = key;
+        }
+
+		public AsymmetricKeyParameter Key
+        {
+            get { return this.key; }
+        }
+
+		public override bool Equals(object obj)
+		{
+			AsymmetricKeyEntry other = obj as AsymmetricKeyEntry;
+
+			if (other == null)
+				return false;
+
+			return key.Equals(other.key);
+		}
+
+		public override int GetHashCode()
+		{
+			return ~key.GetHashCode();
+		}
+	}
+}
diff --git a/crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs b/crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs
new file mode 100644
index 000000000..b69693490
--- /dev/null
+++ b/crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs
@@ -0,0 +1,75 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Pkcs
+{
+    public sealed class EncryptedPrivateKeyInfoFactory
+    {
+        private EncryptedPrivateKeyInfoFactory()
+        {
+        }
+
+        public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo(
+            DerObjectIdentifier		algorithm,
+            char[]					passPhrase,
+            byte[]					salt,
+            int						iterationCount,
+            AsymmetricKeyParameter	key)
+        {
+            return CreateEncryptedPrivateKeyInfo(
+				algorithm.Id, passPhrase, salt, iterationCount,
+				PrivateKeyInfoFactory.CreatePrivateKeyInfo(key));
+        }
+
+		public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo(
+			string					algorithm,
+			char[]					passPhrase,
+			byte[]					salt,
+			int						iterationCount,
+			AsymmetricKeyParameter	key)
+		{
+			return CreateEncryptedPrivateKeyInfo(
+				algorithm, passPhrase, salt, iterationCount,
+				PrivateKeyInfoFactory.CreatePrivateKeyInfo(key));
+		}
+
+		public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo(
+            string			algorithm,
+            char[]			passPhrase,
+            byte[]			salt,
+            int				iterationCount,
+            PrivateKeyInfo	keyInfo)
+        {
+            if (!PbeUtilities.IsPbeAlgorithm(algorithm))
+                throw new ArgumentException("attempt to use non-PBE algorithm with PBE EncryptedPrivateKeyInfo generation");
+
+			IBufferedCipher cipher = PbeUtilities.CreateEngine(algorithm) as IBufferedCipher;
+
+			if (cipher == null)
+			{
+				// TODO Throw exception?
+			}
+
+			Asn1Encodable parameters = PbeUtilities.GenerateAlgorithmParameters(
+				algorithm, salt, iterationCount);
+
+			ICipherParameters keyParameters = PbeUtilities.GenerateCipherParameters(
+				algorithm, passPhrase, parameters);
+
+			cipher.Init(true, keyParameters);
+
+			byte[] keyBytes = keyInfo.GetEncoded();
+			byte[] encoding = cipher.DoFinal(keyBytes);
+
+			DerObjectIdentifier oid = PbeUtilities.GetObjectIdentifier(algorithm);
+			AlgorithmIdentifier algID = new AlgorithmIdentifier(oid, parameters);
+
+			return new EncryptedPrivateKeyInfo(algID, encoding);
+        }
+    }
+}
diff --git a/crypto/src/pkcs/PKCS12StoreBuilder.cs b/crypto/src/pkcs/PKCS12StoreBuilder.cs
new file mode 100644
index 000000000..c8fa0f603
--- /dev/null
+++ b/crypto/src/pkcs/PKCS12StoreBuilder.cs
@@ -0,0 +1,41 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+
+namespace Org.BouncyCastle.Pkcs
+{
+	public class Pkcs12StoreBuilder
+	{
+		private DerObjectIdentifier	keyAlgorithm = PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc;
+		private DerObjectIdentifier	certAlgorithm = PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc;
+		private bool useDerEncoding = false;
+
+		public Pkcs12StoreBuilder()
+		{
+		}
+
+		public Pkcs12Store Build()
+		{
+			return new Pkcs12Store(keyAlgorithm, certAlgorithm, useDerEncoding);
+		}
+
+		public Pkcs12StoreBuilder SetCertAlgorithm(DerObjectIdentifier certAlgorithm)
+		{
+			this.certAlgorithm = certAlgorithm;
+			return this;
+		}
+
+		public Pkcs12StoreBuilder SetKeyAlgorithm(DerObjectIdentifier keyAlgorithm)
+		{
+			this.keyAlgorithm = keyAlgorithm;
+			return this;
+		}
+
+		public Pkcs12StoreBuilder SetUseDerEncoding(bool useDerEncoding)
+		{
+			this.useDerEncoding = useDerEncoding;
+			return this;
+		}
+	}
+}
diff --git a/crypto/src/pkcs/Pkcs10CertificationRequest.cs b/crypto/src/pkcs/Pkcs10CertificationRequest.cs
new file mode 100644
index 000000000..2c343c0b4
--- /dev/null
+++ b/crypto/src/pkcs/Pkcs10CertificationRequest.cs
@@ -0,0 +1,466 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Pkcs
+{
+	/// <remarks>
+	/// A class for verifying and creating Pkcs10 Certification requests.
+	/// </remarks>
+	/// <code>
+	/// CertificationRequest ::= Sequence {
+	///   certificationRequestInfo  CertificationRequestInfo,
+	///   signatureAlgorithm        AlgorithmIdentifier{{ SignatureAlgorithms }},
+	///   signature                 BIT STRING
+	/// }
+	///
+	/// CertificationRequestInfo ::= Sequence {
+	///   version             Integer { v1(0) } (v1,...),
+	///   subject             Name,
+	///   subjectPKInfo   SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
+	///   attributes          [0] Attributes{{ CRIAttributes }}
+	///  }
+	///
+	///  Attributes { ATTRIBUTE:IOSet } ::= Set OF Attr{{ IOSet }}
+	///
+	///  Attr { ATTRIBUTE:IOSet } ::= Sequence {
+	///    type    ATTRIBUTE.&amp;id({IOSet}),
+	///    values  Set SIZE(1..MAX) OF ATTRIBUTE.&amp;Type({IOSet}{\@type})
+	///  }
+	/// </code>
+	/// see <a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2132"/>
+	public class Pkcs10CertificationRequest
+		: CertificationRequest
+	{
+		protected static readonly IDictionary algorithms = Platform.CreateHashtable();
+        protected static readonly IDictionary exParams = Platform.CreateHashtable();
+        protected static readonly IDictionary keyAlgorithms = Platform.CreateHashtable();
+        protected static readonly IDictionary oids = Platform.CreateHashtable();
+		protected static readonly ISet noParams = new HashSet();
+
+		static Pkcs10CertificationRequest()
+		{
+			algorithms.Add("MD2WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.2"));
+			algorithms.Add("MD2WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.2"));
+			algorithms.Add("MD5WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.4"));
+			algorithms.Add("MD5WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.4"));
+			algorithms.Add("RSAWITHMD5", new DerObjectIdentifier("1.2.840.113549.1.1.4"));
+			algorithms.Add("SHA1WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.5"));
+			algorithms.Add("SHA1WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.5"));
+			algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption);
+			algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption);
+			algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption);
+			algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption);
+			algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption);
+			algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption);
+			algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption);
+			algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption);
+			algorithms.Add("SHA1WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("SHA224WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("SHA256WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("SHA384WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("SHA512WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss);
+			algorithms.Add("RSAWITHSHA1", new DerObjectIdentifier("1.2.840.113549.1.1.5"));
+			algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+			algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+			algorithms.Add("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+			algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+			algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+			algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+			algorithms.Add("SHA1WITHDSA", new DerObjectIdentifier("1.2.840.10040.4.3"));
+			algorithms.Add("DSAWITHSHA1", new DerObjectIdentifier("1.2.840.10040.4.3"));
+			algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224);
+			algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256);
+			algorithms.Add("SHA384WITHDSA", NistObjectIdentifiers.DsaWithSha384);
+			algorithms.Add("SHA512WITHDSA", NistObjectIdentifiers.DsaWithSha512);
+			algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1);
+			algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224);
+			algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256);
+			algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384);
+			algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512);
+			algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1);
+			algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+			algorithms.Add("GOST3410WITHGOST3411", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+			algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+			algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+			algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+
+			//
+			// reverse mappings
+			//
+			oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
+			oids.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224WITHRSA");
+			oids.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256WITHRSA");
+			oids.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384WITHRSA");
+			oids.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512WITHRSA");
+			oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, "GOST3411WITHGOST3410");
+			oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001, "GOST3411WITHECGOST3410");
+
+			oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
+			oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
+			oids.Add(new DerObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
+			oids.Add(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1WITHECDSA");
+			oids.Add(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224WITHECDSA");
+			oids.Add(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256WITHECDSA");
+			oids.Add(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384WITHECDSA");
+			oids.Add(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512WITHECDSA");
+			oids.Add(OiwObjectIdentifiers.Sha1WithRsa, "SHA1WITHRSA");
+			oids.Add(OiwObjectIdentifiers.DsaWithSha1, "SHA1WITHDSA");
+			oids.Add(NistObjectIdentifiers.DsaWithSha224, "SHA224WITHDSA");
+			oids.Add(NistObjectIdentifiers.DsaWithSha256, "SHA256WITHDSA");
+
+			//
+			// key types
+			//
+			keyAlgorithms.Add(PkcsObjectIdentifiers.RsaEncryption, "RSA");
+			keyAlgorithms.Add(X9ObjectIdentifiers.IdDsa, "DSA");
+
+			//
+			// According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
+			// The parameters field SHALL be NULL for RSA based signature algorithms.
+			//
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1);
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224);
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256);
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384);
+			noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512);
+			noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1);
+			noParams.Add(NistObjectIdentifiers.DsaWithSha224);
+			noParams.Add(NistObjectIdentifiers.DsaWithSha256);
+
+			//
+			// RFC 4491
+			//
+			noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+			noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+
+			//
+			// explicit params
+			//
+			AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance);
+			exParams.Add("SHA1WITHRSAANDMGF1", CreatePssParams(sha1AlgId, 20));
+
+			AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha224, DerNull.Instance);
+			exParams.Add("SHA224WITHRSAANDMGF1", CreatePssParams(sha224AlgId, 28));
+
+			AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance);
+			exParams.Add("SHA256WITHRSAANDMGF1", CreatePssParams(sha256AlgId, 32));
+
+			AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance);
+			exParams.Add("SHA384WITHRSAANDMGF1", CreatePssParams(sha384AlgId, 48));
+
+			AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance);
+			exParams.Add("SHA512WITHRSAANDMGF1", CreatePssParams(sha512AlgId, 64));
+		}
+
+		private static RsassaPssParameters CreatePssParams(
+			AlgorithmIdentifier	hashAlgId,
+			int					saltSize)
+		{
+			return new RsassaPssParameters(
+				hashAlgId,
+				new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, hashAlgId),
+				new DerInteger(saltSize),
+				new DerInteger(1));
+		}
+
+		protected Pkcs10CertificationRequest()
+		{
+		}
+
+		public Pkcs10CertificationRequest(
+			byte[] encoded)
+			: base((Asn1Sequence) Asn1Object.FromByteArray(encoded))
+		{
+		}
+
+		public Pkcs10CertificationRequest(
+			Asn1Sequence seq)
+			: base(seq)
+		{
+		}
+
+		public Pkcs10CertificationRequest(
+			Stream input)
+			: base((Asn1Sequence) Asn1Object.FromStream(input))
+		{
+		}
+
+		/// <summary>
+		/// Instantiate a Pkcs10CertificationRequest object with the necessary credentials.
+		/// </summary>
+		///<param name="signatureAlgorithm">Name of Sig Alg.</param>
+		/// <param name="subject">X509Name of subject eg OU="My unit." O="My Organisatioin" C="au" </param>
+		/// <param name="publicKey">Public Key to be included in cert reqest.</param>
+		/// <param name="attributes">ASN1Set of Attributes.</param>
+		/// <param name="signingKey">Matching Private key for nominated (above) public key to be used to sign the request.</param>
+		public Pkcs10CertificationRequest(
+			string					signatureAlgorithm,
+			X509Name				subject,
+			AsymmetricKeyParameter	publicKey,
+			Asn1Set					attributes,
+			AsymmetricKeyParameter	signingKey)
+		{
+			if (signatureAlgorithm == null)
+				throw new ArgumentNullException("signatureAlgorithm");
+			if (subject == null)
+				throw new ArgumentNullException("subject");
+			if (publicKey == null)
+				throw new ArgumentNullException("publicKey");
+			if (publicKey.IsPrivate)
+				throw new ArgumentException("expected public key", "publicKey");
+			if (!signingKey.IsPrivate)
+				throw new ArgumentException("key for signing must be private", "signingKey");
+
+//			DerObjectIdentifier sigOid = SignerUtilities.GetObjectIdentifier(signatureAlgorithm);
+			string algorithmName = signatureAlgorithm.ToUpperInvariant();
+			DerObjectIdentifier sigOid = (DerObjectIdentifier) algorithms[algorithmName];
+
+			if (sigOid == null)
+			{
+				try
+				{
+					sigOid = new DerObjectIdentifier(algorithmName);
+				}
+				catch (Exception e)
+				{
+					throw new ArgumentException("Unknown signature type requested", e);
+				}
+			}
+
+			if (noParams.Contains(sigOid))
+			{
+				this.sigAlgId = new AlgorithmIdentifier(sigOid);
+			}
+			else if (exParams.Contains(algorithmName))
+			{
+				this.sigAlgId = new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]);
+			}
+			else
+			{
+				this.sigAlgId = new AlgorithmIdentifier(sigOid, DerNull.Instance);
+			}
+
+			SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey);
+
+			this.reqInfo = new CertificationRequestInfo(subject, pubInfo, attributes);
+
+			ISigner sig = SignerUtilities.GetSigner(signatureAlgorithm);
+
+			sig.Init(true, signingKey);
+
+			try
+			{
+				// Encode.
+				byte[] b = reqInfo.GetDerEncoded();
+				sig.BlockUpdate(b, 0, b.Length);
+			}
+			catch (Exception e)
+			{
+				throw new ArgumentException("exception encoding TBS cert request", e);
+			}
+
+			// Generate Signature.
+			sigBits = new DerBitString(sig.GenerateSignature());
+		}
+
+//        internal Pkcs10CertificationRequest(
+//        	Asn1InputStream seqStream)
+//        {
+//			Asn1Sequence seq = (Asn1Sequence) seqStream.ReadObject();
+//            try
+//            {
+//                this.reqInfo = CertificationRequestInfo.GetInstance(seq[0]);
+//                this.sigAlgId = AlgorithmIdentifier.GetInstance(seq[1]);
+//                this.sigBits = (DerBitString) seq[2];
+//            }
+//            catch (Exception ex)
+//            {
+//                throw new ArgumentException("Create From Asn1Sequence: " + ex.Message);
+//            }
+//        }
+
+		/// <summary>
+		/// Get the public key.
+		/// </summary>
+		/// <returns>The public key.</returns>
+		public AsymmetricKeyParameter GetPublicKey()
+		{
+			return PublicKeyFactory.CreateKey(reqInfo.SubjectPublicKeyInfo);
+		}
+
+		/// <summary>
+		/// Verify Pkcs10 Cert Request is valid.
+		/// </summary>
+		/// <returns>true = valid.</returns>
+		public bool Verify()
+		{
+			return Verify(this.GetPublicKey());
+		}
+
+		public bool Verify(
+			AsymmetricKeyParameter publicKey)
+		{
+			ISigner sig;
+
+			try
+			{
+				sig = SignerUtilities.GetSigner(GetSignatureName(sigAlgId));
+			}
+			catch (Exception e)
+			{
+				// try an alternate
+				string alt = (string) oids[sigAlgId.ObjectID];
+
+				if (alt != null)
+				{
+					sig = SignerUtilities.GetSigner(alt);
+				}
+				else
+				{
+					throw e;
+				}
+			}
+
+			SetSignatureParameters(sig, sigAlgId.Parameters);
+
+			sig.Init(false, publicKey);
+
+			try
+			{
+				byte[] b = reqInfo.GetDerEncoded();
+				sig.BlockUpdate(b, 0, b.Length);
+			}
+			catch (Exception e)
+			{
+				throw new SignatureException("exception encoding TBS cert request", e);
+			}
+
+			return sig.VerifySignature(sigBits.GetBytes());
+		}
+
+//        /// <summary>
+//        /// Get the Der Encoded Pkcs10 Certification Request.
+//        /// </summary>
+//        /// <returns>A byte array.</returns>
+//        public byte[] GetEncoded()
+//        {
+//        	return new CertificationRequest(reqInfo, sigAlgId, sigBits).GetDerEncoded();
+//        }
+
+		// TODO Figure out how to set parameters on an ISigner
+		private void SetSignatureParameters(
+			ISigner			signature,
+			Asn1Encodable	asn1Params)
+		{
+			if (asn1Params != null && !(asn1Params is Asn1Null))
+			{
+//				AlgorithmParameters sigParams = AlgorithmParameters.GetInstance(signature.getAlgorithm());
+//
+//				try
+//				{
+//					sigParams.init(asn1Params.ToAsn1Object().GetDerEncoded());
+//				}
+//				catch (IOException e)
+//				{
+//					throw new SignatureException("IOException decoding parameters: " + e.Message);
+//				}
+
+				if (signature.AlgorithmName.EndsWith("MGF1"))
+				{
+					throw Platform.CreateNotImplementedException("signature algorithm with MGF1");
+
+//					try
+//					{
+//						signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class));
+//					}
+//					catch (GeneralSecurityException e)
+//					{
+//						throw new SignatureException("Exception extracting parameters: " + e.getMessage());
+//					}
+				}
+			}
+		}
+
+		internal static string GetSignatureName(
+			AlgorithmIdentifier sigAlgId)
+		{
+			Asn1Encodable asn1Params = sigAlgId.Parameters;
+
+			if (asn1Params != null && !(asn1Params is Asn1Null))
+			{
+				if (sigAlgId.ObjectID.Equals(PkcsObjectIdentifiers.IdRsassaPss))
+				{
+					RsassaPssParameters rsaParams = RsassaPssParameters.GetInstance(asn1Params);
+					return GetDigestAlgName(rsaParams.HashAlgorithm.ObjectID) + "withRSAandMGF1";
+				}
+			}
+
+			return sigAlgId.ObjectID.Id;
+		}
+
+		private static string GetDigestAlgName(
+			DerObjectIdentifier digestAlgOID)
+		{
+			if (PkcsObjectIdentifiers.MD5.Equals(digestAlgOID))
+			{
+				return "MD5";
+			}
+			else if (OiwObjectIdentifiers.IdSha1.Equals(digestAlgOID))
+			{
+				return "SHA1";
+			}
+			else if (NistObjectIdentifiers.IdSha224.Equals(digestAlgOID))
+			{
+				return "SHA224";
+			}
+			else if (NistObjectIdentifiers.IdSha256.Equals(digestAlgOID))
+			{
+				return "SHA256";
+			}
+			else if (NistObjectIdentifiers.IdSha384.Equals(digestAlgOID))
+			{
+				return "SHA384";
+			}
+			else if (NistObjectIdentifiers.IdSha512.Equals(digestAlgOID))
+			{
+				return "SHA512";
+			}
+			else if (TeleTrusTObjectIdentifiers.RipeMD128.Equals(digestAlgOID))
+			{
+				return "RIPEMD128";
+			}
+			else if (TeleTrusTObjectIdentifiers.RipeMD160.Equals(digestAlgOID))
+			{
+				return "RIPEMD160";
+			}
+			else if (TeleTrusTObjectIdentifiers.RipeMD256.Equals(digestAlgOID))
+			{
+				return "RIPEMD256";
+			}
+			else if (CryptoProObjectIdentifiers.GostR3411.Equals(digestAlgOID))
+			{
+				return "GOST3411";
+			}
+			else
+			{
+				return digestAlgOID.Id;
+			}
+		}
+	}
+}
diff --git a/crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs b/crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs
new file mode 100644
index 000000000..f649b47a2
--- /dev/null
+++ b/crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.TeleTrust;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Pkcs
+{
+	/// <remarks>
+	/// A class for creating and verifying Pkcs10 Certification requests (this is an extension on <see cref="Pkcs10CertificationRequest"/>).
+	/// The requests are made using delay signing. This is useful for situations where
+	/// the private key is in another environment and not directly accessible (e.g. HSM)
+	/// So the first step creates the request, then the signing is done outside this
+	/// object and the signature is then used to complete the request.
+	/// </remarks>
+	/// <code>
+	/// CertificationRequest ::= Sequence {
+	///   certificationRequestInfo  CertificationRequestInfo,
+	///   signatureAlgorithm        AlgorithmIdentifier{{ SignatureAlgorithms }},
+	///   signature                 BIT STRING
+	/// }
+	///
+	/// CertificationRequestInfo ::= Sequence {
+	///   version             Integer { v1(0) } (v1,...),
+	///   subject             Name,
+	///   subjectPKInfo   SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
+	///   attributes          [0] Attributes{{ CRIAttributes }}
+	///  }
+	///
+	///  Attributes { ATTRIBUTE:IOSet } ::= Set OF Attr{{ IOSet }}
+	///
+	///  Attr { ATTRIBUTE:IOSet } ::= Sequence {
+	///    type    ATTRIBUTE.&amp;id({IOSet}),
+	///    values  Set SIZE(1..MAX) OF ATTRIBUTE.&amp;Type({IOSet}{\@type})
+	///  }
+	/// </code>
+	/// see <a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2132"/>
+	public class Pkcs10CertificationRequestDelaySigned : Pkcs10CertificationRequest
+	{
+		protected Pkcs10CertificationRequestDelaySigned()
+			: base()
+		{
+		}
+		public Pkcs10CertificationRequestDelaySigned(
+			byte[] encoded)
+			: base(encoded)
+		{
+		}
+		public Pkcs10CertificationRequestDelaySigned(
+			Asn1Sequence seq)
+			: base(seq)
+		{
+		}
+		public Pkcs10CertificationRequestDelaySigned(
+			Stream input)
+			: base(input)
+		{
+		}
+		public Pkcs10CertificationRequestDelaySigned(
+			string					signatureAlgorithm,
+			X509Name				subject,
+			AsymmetricKeyParameter	publicKey,
+			Asn1Set					attributes,
+			AsymmetricKeyParameter	signingKey)
+			: base(signatureAlgorithm, subject, publicKey, attributes, signingKey)
+		{
+		}
+		/// <summary>
+		/// Instantiate a Pkcs10CertificationRequest object with the necessary credentials.
+		/// </summary>
+		/// <param name="signatureAlgorithm">Name of Sig Alg.</param>
+		/// <param name="subject">X509Name of subject eg OU="My unit." O="My Organisatioin" C="au" </param>
+		/// <param name="publicKey">Public Key to be included in cert reqest.</param>
+		/// <param name="attributes">ASN1Set of Attributes.</param>
+		/// <remarks>
+        /// After the object is constructed use the <see cref="GetDataToSign"/> and finally the
+        /// SignRequest methods to finalize the request.
+		/// </remarks>
+		public Pkcs10CertificationRequestDelaySigned(
+			string					signatureAlgorithm,
+			X509Name				subject,
+			AsymmetricKeyParameter	publicKey,
+			Asn1Set					attributes)
+		{
+			if (signatureAlgorithm == null)
+				throw new ArgumentNullException("signatureAlgorithm");
+			if (subject == null)
+				throw new ArgumentNullException("subject");
+			if (publicKey == null)
+				throw new ArgumentNullException("publicKey");
+			if (publicKey.IsPrivate)
+				throw new ArgumentException("expected public key", "publicKey");
+//			DerObjectIdentifier sigOid = SignerUtilities.GetObjectIdentifier(signatureAlgorithm);
+			string algorithmName = signatureAlgorithm.ToUpperInvariant();
+			DerObjectIdentifier sigOid = (DerObjectIdentifier) algorithms[algorithmName];
+			if (sigOid == null)
+			{
+				try
+				{
+					sigOid = new DerObjectIdentifier(algorithmName);
+				}
+				catch (Exception e)
+				{
+					throw new ArgumentException("Unknown signature type requested", e);
+				}
+			}
+			if (noParams.Contains(sigOid))
+			{
+				this.sigAlgId = new AlgorithmIdentifier(sigOid);
+			}
+			else if (exParams.Contains(algorithmName))
+			{
+				this.sigAlgId = new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]);
+			}
+			else
+			{
+				this.sigAlgId = new AlgorithmIdentifier(sigOid, DerNull.Instance);
+			}
+			SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey);
+			this.reqInfo = new CertificationRequestInfo(subject, pubInfo, attributes);
+		}
+		public byte[] GetDataToSign()
+		{
+			return reqInfo.GetDerEncoded();
+		}
+		public void SignRequest(byte[] signedData)
+		{
+			//build the signature from the signed data
+			sigBits = new DerBitString(signedData);
+		}
+		public void SignRequest(DerBitString signedData)
+		{
+			//build the signature from the signed data
+			sigBits = signedData;
+		}
+	}
+}
diff --git a/crypto/src/pkcs/Pkcs12Entry.cs b/crypto/src/pkcs/Pkcs12Entry.cs
new file mode 100644
index 000000000..5dcc94e88
--- /dev/null
+++ b/crypto/src/pkcs/Pkcs12Entry.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Pkcs
+{
+    public abstract class Pkcs12Entry
+    {
+        private readonly IDictionary attributes;
+
+		protected internal Pkcs12Entry(
+            IDictionary attributes)
+        {
+            this.attributes = attributes;
+
+			foreach (DictionaryEntry entry in attributes)
+			{
+				if (!(entry.Key is string))
+					throw new ArgumentException("Attribute keys must be of type: " + typeof(string).FullName, "attributes");
+				if (!(entry.Value is Asn1Encodable))
+					throw new ArgumentException("Attribute values must be of type: " + typeof(Asn1Encodable).FullName, "attributes");
+			}
+        }
+
+		[Obsolete("Use 'object[index]' syntax instead")]
+		public Asn1Encodable GetBagAttribute(
+            DerObjectIdentifier oid)
+        {
+            return (Asn1Encodable)this.attributes[oid.Id];
+        }
+
+		[Obsolete("Use 'object[index]' syntax instead")]
+		public Asn1Encodable GetBagAttribute(
+            string oid)
+        {
+            return (Asn1Encodable)this.attributes[oid];
+        }
+
+		[Obsolete("Use 'BagAttributeKeys' property")]
+        public IEnumerator GetBagAttributeKeys()
+        {
+            return this.attributes.Keys.GetEnumerator();
+        }
+
+		public Asn1Encodable this[
+			DerObjectIdentifier oid]
+		{
+			get { return (Asn1Encodable) this.attributes[oid.Id]; }
+		}
+
+		public Asn1Encodable this[
+			string oid]
+		{
+			get { return (Asn1Encodable) this.attributes[oid]; }
+		}
+
+		public IEnumerable BagAttributeKeys
+		{
+			get { return new EnumerableProxy(this.attributes.Keys); }
+		}
+    }
+}
diff --git a/crypto/src/pkcs/Pkcs12Store.cs b/crypto/src/pkcs/Pkcs12Store.cs
new file mode 100644
index 000000000..2e7f2295e
--- /dev/null
+++ b/crypto/src/pkcs/Pkcs12Store.cs
@@ -0,0 +1,1231 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Pkcs
+{
+	public class Pkcs12Store
+	{
+		private readonly IgnoresCaseHashtable	keys = new IgnoresCaseHashtable();
+		private readonly IDictionary            localIds = Platform.CreateHashtable();
+		private readonly IgnoresCaseHashtable	certs = new IgnoresCaseHashtable();
+        private readonly IDictionary            chainCerts = Platform.CreateHashtable();
+        private readonly IDictionary            keyCerts = Platform.CreateHashtable();
+		private readonly DerObjectIdentifier	keyAlgorithm;
+		private readonly DerObjectIdentifier	certAlgorithm;
+		private readonly bool					useDerEncoding;
+
+		private const int MinIterations = 1024;
+		private const int SaltSize = 20;
+
+		private static SubjectKeyIdentifier CreateSubjectKeyID(
+			AsymmetricKeyParameter pubKey)
+		{
+			return new SubjectKeyIdentifier(
+				SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey));
+		}
+
+		internal class CertId
+		{
+			private readonly byte[] id;
+
+			internal CertId(
+				AsymmetricKeyParameter pubKey)
+			{
+				this.id = CreateSubjectKeyID(pubKey).GetKeyIdentifier();
+			}
+
+			internal CertId(
+				byte[] id)
+			{
+				this.id = id;
+			}
+
+			internal byte[] Id
+			{
+				get { return id; }
+			}
+
+			public override int GetHashCode()
+			{
+				return Arrays.GetHashCode(id);
+			}
+
+			public override bool Equals(
+				object obj)
+			{
+				if (obj == this)
+					return true;
+
+				CertId other = obj as CertId;
+
+				if (other == null)
+					return false;
+
+				return Arrays.AreEqual(id, other.id);
+			}
+		}
+
+		internal Pkcs12Store(
+			DerObjectIdentifier	keyAlgorithm,
+			DerObjectIdentifier	certAlgorithm,
+			bool				useDerEncoding)
+		{
+			this.keyAlgorithm = keyAlgorithm;
+			this.certAlgorithm = certAlgorithm;
+			this.useDerEncoding = useDerEncoding;
+		}
+
+		// TODO Consider making obsolete
+//		[Obsolete("Use 'Pkcs12StoreBuilder' instead")]
+		public Pkcs12Store()
+			: this(PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc,
+				PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc, false)
+		{
+		}
+
+		// TODO Consider making obsolete
+//		[Obsolete("Use 'Pkcs12StoreBuilder' and 'Load' method instead")]
+		public Pkcs12Store(
+			Stream	input,
+			char[]	password)
+			: this()
+		{
+			Load(input, password);
+		}
+
+		public void Load(
+			Stream	input,
+			char[]	password)
+		{
+			if (input == null)
+				throw new ArgumentNullException("input");
+			if (password == null)
+				throw new ArgumentNullException("password");
+
+			Asn1Sequence obj = (Asn1Sequence) Asn1Object.FromStream(input);
+			Pfx bag = new Pfx(obj);
+			ContentInfo info = bag.AuthSafe;
+			bool unmarkedKey = false;
+			bool wrongPkcs12Zero = false;
+
+			if (bag.MacData != null) // check the mac code
+			{
+				MacData mData = bag.MacData;
+				DigestInfo dInfo = mData.Mac;
+				AlgorithmIdentifier algId = dInfo.AlgorithmID;
+				byte[] salt = mData.GetSalt();
+				int itCount = mData.IterationCount.IntValue;
+
+				byte[] data = ((Asn1OctetString) info.Content).GetOctets();
+
+				byte[] mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, false, data);
+				byte[] dig = dInfo.GetDigest();
+
+				if (!Arrays.ConstantTimeAreEqual(mac, dig))
+				{
+					if (password.Length > 0)
+						throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file.");
+
+					// Try with incorrect zero length password
+					mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, true, data);
+
+					if (!Arrays.ConstantTimeAreEqual(mac, dig))
+						throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file.");
+
+					wrongPkcs12Zero = true;
+				}
+			}
+
+			keys.Clear();
+			localIds.Clear();
+
+            IList chain = Platform.CreateArrayList();
+
+			if (info.ContentType.Equals(PkcsObjectIdentifiers.Data))
+			{
+				byte[] octs = ((Asn1OctetString)info.Content).GetOctets();
+				AuthenticatedSafe authSafe = new AuthenticatedSafe(
+					(Asn1Sequence) Asn1OctetString.FromByteArray(octs));
+				ContentInfo[] cis = authSafe.GetContentInfo();
+
+				foreach (ContentInfo ci in cis)
+				{
+					DerObjectIdentifier oid = ci.ContentType;
+
+					if (oid.Equals(PkcsObjectIdentifiers.Data))
+					{
+						byte[] octets = ((Asn1OctetString)ci.Content).GetOctets();
+						Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets);
+
+						foreach (Asn1Sequence subSeq in seq)
+						{
+							SafeBag b = new SafeBag(subSeq);
+
+							if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag))
+							{
+								EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue);
+								PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(
+									password, wrongPkcs12Zero, eIn);
+								AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo);
+
+								//
+								// set the attributes on the key
+								//
+								IDictionary attributes = Platform.CreateHashtable();
+								AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes);
+								string alias = null;
+								Asn1OctetString localId = null;
+
+								if (b.BagAttributes != null)
+								{
+									foreach (Asn1Sequence sq in b.BagAttributes)
+									{
+										DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0];
+										Asn1Set attrSet = (Asn1Set) sq[1];
+										Asn1Encodable attr = null;
+
+										if (attrSet.Count > 0)
+										{
+											// TODO We should be adding all attributes in the set
+											attr = attrSet[0];
+
+											// TODO We might want to "merge" attribute sets with
+											// the same OID - currently, differing values give an error
+											if (attributes.Contains(aOid.Id))
+											{
+												// OK, but the value has to be the same
+												if (!attributes[aOid.Id].Equals(attr))
+												{
+													throw new IOException("attempt to add existing attribute with different value");
+												}
+											}
+											else
+											{
+											    attributes.Add(aOid.Id, attr);
+											}
+
+											if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
+											{
+												alias = ((DerBmpString)attr).GetString();
+												// TODO Do these in a separate loop, just collect aliases here
+												keys[alias] = pkcs12Key;
+											}
+											else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
+											{
+												localId = (Asn1OctetString)attr;
+											}
+										}
+									}
+								}
+
+								if (localId != null)
+								{
+									string name = Hex.ToHexString(localId.GetOctets());
+
+									if (alias == null)
+									{
+										keys[name] = pkcs12Key;
+									}
+									else
+									{
+										// TODO There may have been more than one alias
+										localIds[alias] = name;
+									}
+								}
+								else
+								{
+									unmarkedKey = true;
+									keys["unmarked"] = pkcs12Key;
+								}
+							}
+							else if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag))
+							{
+								chain.Add(b);
+							}
+							else
+							{
+                                System.Diagnostics.Debug.WriteLine("extra " + b.BagID);
+                                System.Diagnostics.Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b));
+							}
+						}
+					}
+					else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData))
+					{
+						EncryptedData d = EncryptedData.GetInstance(ci.Content);
+						byte[] octets = CryptPbeData(false, d.EncryptionAlgorithm,
+							password, wrongPkcs12Zero, d.Content.GetOctets());
+						Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets);
+
+						foreach (Asn1Sequence subSeq in seq)
+						{
+							SafeBag b = new SafeBag(subSeq);
+
+							if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag))
+							{
+								chain.Add(b);
+							}
+							else if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag))
+							{
+								EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue);
+								PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(
+									password, wrongPkcs12Zero, eIn);
+								AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo);
+
+								//
+								// set the attributes on the key
+								//
+								IDictionary attributes = Platform.CreateHashtable();
+								AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes);
+								string alias = null;
+								Asn1OctetString localId = null;
+
+								foreach (Asn1Sequence sq in b.BagAttributes)
+								{
+									DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0];
+									Asn1Set attrSet = (Asn1Set) sq[1];
+									Asn1Encodable attr = null;
+
+									if (attrSet.Count > 0)
+									{
+										// TODO We should be adding all attributes in the set
+										attr = attrSet[0];
+
+										// TODO We might want to "merge" attribute sets with
+										// the same OID - currently, differing values give an error
+										if (attributes.Contains(aOid.Id))
+										{
+											// OK, but the value has to be the same
+											if (!attributes[aOid.Id].Equals(attr))
+											{
+												throw new IOException("attempt to add existing attribute with different value");
+											}
+										}
+										else
+										{
+										    attributes.Add(aOid.Id, attr);
+										}
+
+										if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
+										{
+											alias = ((DerBmpString)attr).GetString();
+											// TODO Do these in a separate loop, just collect aliases here
+											keys[alias] = pkcs12Key;
+										}
+										else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
+										{
+											localId = (Asn1OctetString)attr;
+										}
+									}
+								}
+
+								// TODO Should we be checking localIds != null here
+								// as for PkcsObjectIdentifiers.Data version above?
+
+								string name = Hex.ToHexString(localId.GetOctets());
+
+								if (alias == null)
+								{
+									keys[name] = pkcs12Key;
+								}
+								else
+								{
+									// TODO There may have been more than one alias
+									localIds[alias] = name;
+								}
+							}
+							else if (b.BagID.Equals(PkcsObjectIdentifiers.KeyBag))
+							{
+								PrivateKeyInfo privKeyInfo = PrivateKeyInfo.GetInstance(b.BagValue);
+								AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo);
+
+								//
+								// set the attributes on the key
+								//
+								string alias = null;
+								Asn1OctetString localId = null;
+								IDictionary attributes = Platform.CreateHashtable();
+								AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes);
+
+								foreach (Asn1Sequence sq in b.BagAttributes)
+								{
+									DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0];
+									Asn1Set attrSet = (Asn1Set) sq[1];
+									Asn1Encodable attr = null;
+
+									if (attrSet.Count > 0)
+									{
+										// TODO We should be adding all attributes in the set
+										attr = attrSet[0];
+
+										// TODO We might want to "merge" attribute sets with
+										// the same OID - currently, differing values give an error
+										if (attributes.Contains(aOid.Id))
+										{
+											// OK, but the value has to be the same
+											if (!attributes[aOid.Id].Equals(attr))
+											{
+												throw new IOException("attempt to add existing attribute with different value");
+											}
+										}
+										else
+										{
+										    attributes.Add(aOid.Id, attr);
+										}
+
+										if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
+										{
+											alias = ((DerBmpString)attr).GetString();
+											// TODO Do these in a separate loop, just collect aliases here
+											keys[alias] = pkcs12Key;
+										}
+										else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
+										{
+											localId = (Asn1OctetString)attr;
+										}
+									}
+								}
+
+								// TODO Should we be checking localIds != null here
+								// as for PkcsObjectIdentifiers.Data version above?
+
+								string name = Hex.ToHexString(localId.GetOctets());
+
+								if (alias == null)
+								{
+									keys[name] = pkcs12Key;
+								}
+								else
+								{
+									// TODO There may have been more than one alias
+									localIds[alias] = name;
+								}
+							}
+							else
+							{
+                                System.Diagnostics.Debug.WriteLine("extra " + b.BagID);
+                                System.Diagnostics.Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b));
+							}
+						}
+					}
+					else
+					{
+                        System.Diagnostics.Debug.WriteLine("extra " + oid);
+                        System.Diagnostics.Debug.WriteLine("extra " + Asn1Dump.DumpAsString(ci.Content));
+					}
+				}
+			}
+
+			certs.Clear();
+			chainCerts.Clear();
+			keyCerts.Clear();
+
+			foreach (SafeBag b in chain)
+			{
+				CertBag cb = new CertBag((Asn1Sequence)b.BagValue);
+				byte[] octets = ((Asn1OctetString) cb.CertValue).GetOctets();
+				X509Certificate cert = new X509CertificateParser().ReadCertificate(octets);
+
+				//
+				// set the attributes
+				//
+                IDictionary attributes = Platform.CreateHashtable();
+				Asn1OctetString localId = null;
+				string alias = null;
+
+				if (b.BagAttributes != null)
+				{
+					foreach (Asn1Sequence sq in b.BagAttributes)
+					{
+						DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0];
+						Asn1Set attrSet = (Asn1Set) sq[1];
+
+						if (attrSet.Count > 0)
+						{
+							// TODO We should be adding all attributes in the set
+							Asn1Encodable attr = attrSet[0];
+
+							// TODO We might want to "merge" attribute sets with
+							// the same OID - currently, differing values give an error
+							if (attributes.Contains(aOid.Id))
+							{
+								// OK, but the value has to be the same
+								if (!attributes[aOid.Id].Equals(attr))
+								{
+									throw new IOException("attempt to add existing attribute with different value");
+								}
+							}
+							else
+							{
+							    attributes.Add(aOid.Id, attr);
+							}
+
+							if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName))
+							{
+								alias = ((DerBmpString)attr).GetString();
+							}
+							else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID))
+							{
+								localId = (Asn1OctetString)attr;
+							}
+						}
+					}
+				}
+
+				CertId certId = new CertId(cert.GetPublicKey());
+				X509CertificateEntry pkcs12Cert = new X509CertificateEntry(cert, attributes);
+
+				chainCerts[certId] = pkcs12Cert;
+
+				if (unmarkedKey)
+				{
+					if (keyCerts.Count == 0)
+					{
+						string name = Hex.ToHexString(certId.Id);
+
+						keyCerts[name] = pkcs12Cert;
+
+						object temp = keys["unmarked"];
+						keys.Remove("unmarked");
+						keys[name] = temp;
+					}
+				}
+				else
+				{
+					if (localId != null)
+					{
+						string name = Hex.ToHexString(localId.GetOctets());
+
+						keyCerts[name] = pkcs12Cert;
+					}
+
+					if (alias != null)
+					{
+						// TODO There may have been more than one alias
+						certs[alias] = pkcs12Cert;
+					}
+				}
+			}
+		}
+
+		public AsymmetricKeyEntry GetKey(
+			string alias)
+		{
+			if (alias == null)
+				throw new ArgumentNullException("alias");
+
+			return (AsymmetricKeyEntry)keys[alias];
+		}
+
+		public bool IsCertificateEntry(
+			string alias)
+		{
+			if (alias == null)
+				throw new ArgumentNullException("alias");
+
+			return (certs[alias] != null && keys[alias] == null);
+		}
+
+		public bool IsKeyEntry(
+			string alias)
+		{
+			if (alias == null)
+				throw new ArgumentNullException("alias");
+
+			return (keys[alias] != null);
+		}
+
+		private IDictionary GetAliasesTable()
+		{
+            IDictionary tab = Platform.CreateHashtable();
+
+			foreach (string key in certs.Keys)
+			{
+				tab[key] = "cert";
+			}
+
+			foreach (string a in keys.Keys)
+			{
+				if (tab[a] == null)
+				{
+					tab[a] = "key";
+				}
+			}
+
+			return tab;
+		}
+
+		public IEnumerable Aliases
+		{
+			get { return new EnumerableProxy(GetAliasesTable().Keys); }
+		}
+
+		public bool ContainsAlias(
+			string alias)
+		{
+			return certs[alias] != null || keys[alias] != null;
+		}
+
+		/**
+		 * simply return the cert entry for the private key
+		 */
+		public X509CertificateEntry GetCertificate(
+			string alias)
+		{
+			if (alias == null)
+				throw new ArgumentNullException("alias");
+
+			X509CertificateEntry c = (X509CertificateEntry) certs[alias];
+
+			//
+			// look up the key table - and try the local key id
+			//
+			if (c == null)
+			{
+				string id = (string)localIds[alias];
+				if (id != null)
+				{
+					c = (X509CertificateEntry)keyCerts[id];
+				}
+				else
+				{
+					c = (X509CertificateEntry)keyCerts[alias];
+				}
+			}
+
+			return c;
+		}
+
+		public string GetCertificateAlias(
+			X509Certificate cert)
+		{
+			if (cert == null)
+				throw new ArgumentNullException("cert");
+
+			foreach (DictionaryEntry entry in certs)
+			{
+				X509CertificateEntry entryValue = (X509CertificateEntry) entry.Value;
+				if (entryValue.Certificate.Equals(cert))
+				{
+					return (string) entry.Key;
+				}
+			}
+
+			foreach (DictionaryEntry entry in keyCerts)
+			{
+				X509CertificateEntry entryValue = (X509CertificateEntry) entry.Value;
+				if (entryValue.Certificate.Equals(cert))
+				{
+					return (string) entry.Key;
+				}
+			}
+
+			return null;
+		}
+
+		public X509CertificateEntry[] GetCertificateChain(
+			string alias)
+		{
+			if (alias == null)
+				throw new ArgumentNullException("alias");
+
+			if (!IsKeyEntry(alias))
+			{
+				return null;
+			}
+
+			X509CertificateEntry c = GetCertificate(alias);
+
+			if (c != null)
+			{
+				IList cs = Platform.CreateArrayList();
+
+				while (c != null)
+				{
+					X509Certificate x509c = c.Certificate;
+					X509CertificateEntry nextC = null;
+
+					Asn1OctetString ext = x509c.GetExtensionValue(X509Extensions.AuthorityKeyIdentifier);
+					if (ext != null)
+					{
+						AuthorityKeyIdentifier id = AuthorityKeyIdentifier.GetInstance(
+							Asn1Object.FromByteArray(ext.GetOctets()));
+
+						if (id.GetKeyIdentifier() != null)
+						{
+							nextC = (X509CertificateEntry) chainCerts[new CertId(id.GetKeyIdentifier())];
+						}
+					}
+
+					if (nextC == null)
+					{
+						//
+						// no authority key id, try the Issuer DN
+						//
+						X509Name i = x509c.IssuerDN;
+						X509Name s = x509c.SubjectDN;
+
+						if (!i.Equivalent(s))
+						{
+							foreach (CertId certId in chainCerts.Keys)
+							{
+								X509CertificateEntry x509CertEntry = (X509CertificateEntry) chainCerts[certId];
+
+								X509Certificate crt = x509CertEntry.Certificate;
+
+								X509Name sub = crt.SubjectDN;
+								if (sub.Equivalent(i))
+								{
+									try
+									{
+										x509c.Verify(crt.GetPublicKey());
+
+										nextC = x509CertEntry;
+										break;
+									}
+									catch (InvalidKeyException)
+									{
+										// TODO What if it doesn't verify?
+									}
+								}
+							}
+						}
+					}
+
+					cs.Add(c);
+					if (nextC != c) // self signed - end of the chain
+					{
+						c = nextC;
+					}
+					else
+					{
+						c = null;
+					}
+				}
+
+                X509CertificateEntry[] result = new X509CertificateEntry[cs.Count];
+                for (int i = 0; i < cs.Count; ++i)
+                {
+                    result[i] = (X509CertificateEntry)cs[i];
+                }
+                return result;
+			}
+
+			return null;
+		}
+
+		public void SetCertificateEntry(
+			string					alias,
+			X509CertificateEntry	certEntry)
+		{
+			if (alias == null)
+				throw new ArgumentNullException("alias");
+			if (certEntry == null)
+				throw new ArgumentNullException("certEntry");
+			if (keys[alias] != null)
+				throw new ArgumentException("There is a key entry with the name " + alias + ".");
+
+			certs[alias] = certEntry;
+			chainCerts[new CertId(certEntry.Certificate.GetPublicKey())] = certEntry;
+		}
+
+		public void SetKeyEntry(
+			string					alias,
+			AsymmetricKeyEntry		keyEntry,
+			X509CertificateEntry[]	chain)
+		{
+			if (alias == null)
+				throw new ArgumentNullException("alias");
+			if (keyEntry == null)
+				throw new ArgumentNullException("keyEntry");
+			if (keyEntry.Key.IsPrivate && (chain == null))
+				throw new ArgumentException("No certificate chain for private key");
+
+			if (keys[alias] != null)
+			{
+				DeleteEntry(alias);
+			}
+
+			keys[alias] = keyEntry;
+			certs[alias] = chain[0];
+
+			for (int i = 0; i != chain.Length; i++)
+			{
+				chainCerts[new CertId(chain[i].Certificate.GetPublicKey())] = chain[i];
+			}
+		}
+
+		public void DeleteEntry(
+			string alias)
+		{
+			if (alias == null)
+				throw new ArgumentNullException("alias");
+
+			AsymmetricKeyEntry k = (AsymmetricKeyEntry)keys[alias];
+			if (k != null)
+			{
+				keys.Remove(alias);
+			}
+
+			X509CertificateEntry c = (X509CertificateEntry)certs[alias];
+
+			if (c != null)
+			{
+				certs.Remove(alias);
+				chainCerts.Remove(new CertId(c.Certificate.GetPublicKey()));
+			}
+
+			if (k != null)
+			{
+				string id = (string)localIds[alias];
+				if (id != null)
+				{
+					localIds.Remove(alias);
+					c = (X509CertificateEntry)keyCerts[id];
+				}
+				if (c != null)
+				{
+					keyCerts.Remove(id);
+					chainCerts.Remove(new CertId(c.Certificate.GetPublicKey()));
+				}
+			}
+
+			if (c == null && k == null)
+			{
+				throw new ArgumentException("no such entry as " + alias);
+			}
+		}
+
+		public bool IsEntryOfType(
+			string	alias,
+			Type	entryType)
+		{
+			if (entryType == typeof(X509CertificateEntry))
+				return IsCertificateEntry(alias);
+
+			if (entryType == typeof(AsymmetricKeyEntry))
+				return IsKeyEntry(alias) && GetCertificate(alias) != null;
+
+			return false;
+		}
+
+		[Obsolete("Use 'Count' property instead")]
+		public int Size()
+		{
+			return Count;
+		}
+
+		public int Count
+		{
+			// TODO Seems a little inefficient
+			get { return GetAliasesTable().Count; }
+		}
+
+		public void Save(
+			Stream			stream,
+			char[]			password,
+			SecureRandom	random)
+		{
+			if (stream == null)
+				throw new ArgumentNullException("stream");
+			if (password == null)
+				throw new ArgumentNullException("password");
+			if (random == null)
+				throw new ArgumentNullException("random");
+
+			//
+			// handle the key
+			//
+			Asn1EncodableVector keyS = new Asn1EncodableVector();
+			foreach (string name in keys.Keys)
+			{
+				byte[] kSalt = new byte[SaltSize];
+				random.NextBytes(kSalt);
+
+				AsymmetricKeyEntry privKey = (AsymmetricKeyEntry) keys[name];
+				EncryptedPrivateKeyInfo kInfo =
+					EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo(
+					keyAlgorithm, password, kSalt, MinIterations, privKey.Key);
+
+				Asn1EncodableVector kName = new Asn1EncodableVector();
+
+				foreach (string oid in privKey.BagAttributeKeys)
+				{
+					Asn1Encodable entry = privKey[oid];
+
+					// NB: Ignore any existing FriendlyName
+					if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id))
+						continue;
+
+					kName.Add(
+						new DerSequence(
+							new DerObjectIdentifier(oid),
+							new DerSet(entry)));
+				}
+
+				//
+				// make sure we are using the local alias on store
+				//
+				// NB: We always set the FriendlyName based on 'name'
+				//if (privKey[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null)
+				{
+					kName.Add(
+						new DerSequence(
+							PkcsObjectIdentifiers.Pkcs9AtFriendlyName,
+							new DerSet(new DerBmpString(name))));
+				}
+
+				//
+				// make sure we have a local key-id
+				//
+				if (privKey[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null)
+				{
+					X509CertificateEntry ct = GetCertificate(name);
+					AsymmetricKeyParameter pubKey = ct.Certificate.GetPublicKey();
+					SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey);
+
+					kName.Add(
+						new DerSequence(
+							PkcsObjectIdentifiers.Pkcs9AtLocalKeyID,
+							new DerSet(subjectKeyID)));
+				}
+
+				SafeBag kBag = new SafeBag(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag, kInfo.ToAsn1Object(), new DerSet(kName));
+				keyS.Add(kBag);
+			}
+
+			byte[] derEncodedBytes = new DerSequence(keyS).GetDerEncoded();
+
+			BerOctetString keyString = new BerOctetString(derEncodedBytes);
+
+			//
+			// certificate processing
+			//
+			byte[] cSalt = new byte[SaltSize];
+
+			random.NextBytes(cSalt);
+
+			Asn1EncodableVector	certSeq = new Asn1EncodableVector();
+			Pkcs12PbeParams		cParams = new Pkcs12PbeParams(cSalt, MinIterations);
+			AlgorithmIdentifier	cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.ToAsn1Object());
+			ISet				doneCerts = new HashSet();
+
+			foreach (string name in keys.Keys)
+			{
+				X509CertificateEntry certEntry = GetCertificate(name);
+				CertBag cBag = new CertBag(
+					PkcsObjectIdentifiers.X509Certificate,
+					new DerOctetString(certEntry.Certificate.GetEncoded()));
+
+				Asn1EncodableVector fName = new Asn1EncodableVector();
+
+				foreach (string oid in certEntry.BagAttributeKeys)
+				{
+					Asn1Encodable entry = certEntry[oid];
+
+					// NB: Ignore any existing FriendlyName
+					if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id))
+						continue;
+
+					fName.Add(
+						new DerSequence(
+							new DerObjectIdentifier(oid),
+							new DerSet(entry)));
+				}
+
+				//
+				// make sure we are using the local alias on store
+				//
+				// NB: We always set the FriendlyName based on 'name'
+				//if (certEntry[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null)
+				{
+					fName.Add(
+						new DerSequence(
+							PkcsObjectIdentifiers.Pkcs9AtFriendlyName,
+							new DerSet(new DerBmpString(name))));
+				}
+
+				//
+				// make sure we have a local key-id
+				//
+				if (certEntry[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null)
+				{
+					AsymmetricKeyParameter pubKey = certEntry.Certificate.GetPublicKey();
+					SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey);
+
+					fName.Add(
+						new DerSequence(
+							PkcsObjectIdentifiers.Pkcs9AtLocalKeyID,
+							new DerSet(subjectKeyID)));
+				}
+
+				SafeBag sBag = new SafeBag(
+					PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName));
+
+				certSeq.Add(sBag);
+
+				doneCerts.Add(certEntry.Certificate);
+			}
+
+			foreach (string certId in certs.Keys)
+			{
+				X509CertificateEntry cert = (X509CertificateEntry)certs[certId];
+
+				if (keys[certId] != null)
+					continue;
+
+				CertBag cBag = new CertBag(
+					PkcsObjectIdentifiers.X509Certificate,
+					new DerOctetString(cert.Certificate.GetEncoded()));
+
+				Asn1EncodableVector fName = new Asn1EncodableVector();
+
+				foreach (string oid in cert.BagAttributeKeys)
+				{
+					// a certificate not immediately linked to a key doesn't require
+					// a localKeyID and will confuse some PKCS12 implementations.
+					//
+					// If we find one, we'll prune it out.
+					if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Id))
+						continue;
+
+					Asn1Encodable entry = cert[oid];
+
+					// NB: Ignore any existing FriendlyName
+					if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id))
+						continue;
+
+					fName.Add(
+						new DerSequence(
+							new DerObjectIdentifier(oid),
+							new DerSet(entry)));
+				}
+
+				//
+				// make sure we are using the local alias on store
+				//
+				// NB: We always set the FriendlyName based on 'certId'
+				//if (cert[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null)
+				{
+					fName.Add(
+						new DerSequence(
+							PkcsObjectIdentifiers.Pkcs9AtFriendlyName,
+							new DerSet(new DerBmpString(certId))));
+				}
+
+				SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag,
+					cBag.ToAsn1Object(), new DerSet(fName));
+
+				certSeq.Add(sBag);
+
+				doneCerts.Add(cert.Certificate);
+			}
+
+			foreach (CertId certId in chainCerts.Keys)
+			{
+				X509CertificateEntry cert = (X509CertificateEntry)chainCerts[certId];
+
+				if (doneCerts.Contains(cert.Certificate))
+					continue;
+
+				CertBag cBag = new CertBag(
+					PkcsObjectIdentifiers.X509Certificate,
+					new DerOctetString(cert.Certificate.GetEncoded()));
+
+				Asn1EncodableVector fName = new Asn1EncodableVector();
+
+				foreach (string oid in cert.BagAttributeKeys)
+				{
+					// a certificate not immediately linked to a key doesn't require
+					// a localKeyID and will confuse some PKCS12 implementations.
+					//
+					// If we find one, we'll prune it out.
+					if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Id))
+						continue;
+
+					fName.Add(
+						new DerSequence(
+							new DerObjectIdentifier(oid),
+							new DerSet(cert[oid])));
+				}
+
+				SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName));
+
+				certSeq.Add(sBag);
+			}
+
+			derEncodedBytes = new DerSequence(certSeq).GetDerEncoded();
+
+			byte[] certBytes = CryptPbeData(true, cAlgId, password, false, derEncodedBytes);
+
+			EncryptedData cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes));
+
+			ContentInfo[] info = new ContentInfo[]
+			{
+				new ContentInfo(PkcsObjectIdentifiers.Data, keyString),
+				new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object())
+			};
+
+			byte[] data = new AuthenticatedSafe(info).GetEncoded(
+				useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber);
+
+			ContentInfo mainInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(data));
+
+			//
+			// create the mac
+			//
+			byte[] mSalt = new byte[20];
+			random.NextBytes(mSalt);
+
+			byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1,
+				mSalt, MinIterations, password, false, data);
+
+			AlgorithmIdentifier algId = new AlgorithmIdentifier(
+				OiwObjectIdentifiers.IdSha1, DerNull.Instance);
+			DigestInfo dInfo = new DigestInfo(algId, mac);
+
+			MacData mData = new MacData(dInfo, mSalt, MinIterations);
+
+			//
+			// output the Pfx
+			//
+			Pfx pfx = new Pfx(mainInfo, mData);
+
+			DerOutputStream derOut;
+			if (useDerEncoding)
+			{
+				derOut = new DerOutputStream(stream);
+			}
+			else
+			{
+				derOut = new BerOutputStream(stream);
+			}
+
+			derOut.WriteObject(pfx);
+		}
+
+		internal static byte[] CalculatePbeMac(
+			DerObjectIdentifier	oid,
+			byte[]				salt,
+			int					itCount,
+			char[]				password,
+			bool				wrongPkcs12Zero,
+			byte[]				data)
+		{
+			Asn1Encodable asn1Params = PbeUtilities.GenerateAlgorithmParameters(
+				oid, salt, itCount);
+			ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters(
+				oid, password, wrongPkcs12Zero, asn1Params);
+
+			IMac mac = (IMac) PbeUtilities.CreateEngine(oid);
+			mac.Init(cipherParams);
+			mac.BlockUpdate(data, 0, data.Length);
+			return MacUtilities.DoFinal(mac);
+		}
+
+		private static byte[] CryptPbeData(
+			bool				forEncryption,
+			AlgorithmIdentifier	algId,
+			char[]				password,
+			bool				wrongPkcs12Zero,
+			byte[]				data)
+		{
+			Pkcs12PbeParams pbeParams = Pkcs12PbeParams.GetInstance(algId.Parameters);
+			ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters(
+				algId.ObjectID, password, wrongPkcs12Zero, pbeParams);
+
+			IBufferedCipher cipher = PbeUtilities.CreateEngine(algId.ObjectID) as IBufferedCipher;
+
+			if (cipher == null)
+				throw new Exception("Unknown encryption algorithm: " + algId.ObjectID);
+
+			cipher.Init(forEncryption, cipherParams);
+
+			return cipher.DoFinal(data);
+		}
+
+		private class IgnoresCaseHashtable
+			: IEnumerable
+		{
+			private readonly IDictionary orig = Platform.CreateHashtable();
+            private readonly IDictionary keys = Platform.CreateHashtable();
+
+			public void Clear()
+			{
+				orig.Clear();
+				keys.Clear();
+			}
+
+			public IEnumerator GetEnumerator()
+			{
+				return orig.GetEnumerator();
+			}
+
+			public ICollection Keys
+			{
+				get { return orig.Keys; }
+			}
+
+			public object Remove(
+				string alias)
+			{
+				string lower = alias.ToLowerInvariant();
+				string k = (string) keys[lower];
+
+				if (k == null)
+					return null;
+
+				keys.Remove(lower);
+
+				object o = orig[k];
+				orig.Remove(k);
+				return o;
+			}
+
+			public object this[
+				string alias]
+			{
+				get
+				{
+					string lower = alias.ToLowerInvariant();
+					string k = (string) keys[lower];
+
+					if (k == null)
+						return null;
+
+					return orig[k];
+				}
+				set
+				{
+					string lower = alias.ToLowerInvariant();
+					string k = (string) keys[lower];
+					if (k != null)
+					{
+						orig.Remove(k);
+					}
+					keys[lower] = alias;
+					orig[alias] = value;
+				}
+			}
+
+			public ICollection Values
+			{
+				get { return orig.Values; }
+			}
+		}
+	}
+}
diff --git a/crypto/src/pkcs/Pkcs12Utilities.cs b/crypto/src/pkcs/Pkcs12Utilities.cs
new file mode 100644
index 000000000..d35c8b6a2
--- /dev/null
+++ b/crypto/src/pkcs/Pkcs12Utilities.cs
@@ -0,0 +1,77 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Pkcs
+{
+	/**
+	 * Utility class for reencoding PKCS#12 files to definite length.
+	 */
+	public class Pkcs12Utilities
+	{
+		/**
+		 * Just re-encode the outer layer of the PKCS#12 file to definite length encoding.
+		 *
+		 * @param berPKCS12File - original PKCS#12 file
+		 * @return a byte array representing the DER encoding of the PFX structure
+		 * @throws IOException
+		 */
+		public static byte[] ConvertToDefiniteLength(
+			byte[] berPkcs12File)
+		{
+			Pfx pfx = new Pfx(Asn1Sequence.GetInstance(Asn1Object.FromByteArray(berPkcs12File)));
+
+			return pfx.GetEncoded(Asn1Encodable.Der);
+		}
+
+		/**
+		* Re-encode the PKCS#12 structure to definite length encoding at the inner layer
+		* as well, recomputing the MAC accordingly.
+		*
+		* @param berPKCS12File - original PKCS12 file.
+		* @param provider - provider to use for MAC calculation.
+		* @return a byte array representing the DER encoding of the PFX structure.
+		* @throws IOException on parsing, encoding errors.
+		*/
+		public static byte[] ConvertToDefiniteLength(
+			byte[]	berPkcs12File,
+			char[]	passwd)
+		{
+			Pfx pfx = new Pfx(Asn1Sequence.GetInstance(Asn1Object.FromByteArray(berPkcs12File)));
+
+			ContentInfo info = pfx.AuthSafe;
+
+			Asn1OctetString content = Asn1OctetString.GetInstance(info.Content);
+			Asn1Object obj = Asn1Object.FromByteArray(content.GetOctets());
+
+			info = new ContentInfo(info.ContentType, new DerOctetString(obj.GetEncoded(Asn1Encodable.Der)));
+
+			MacData mData = pfx.MacData;
+
+			try
+			{
+				int itCount = mData.IterationCount.IntValue;
+				byte[] data = Asn1OctetString.GetInstance(info.Content).GetOctets();
+				byte[] res = Pkcs12Store.CalculatePbeMac(
+					mData.Mac.AlgorithmID.ObjectID, mData.GetSalt(), itCount, passwd, false, data);
+
+				AlgorithmIdentifier algId = new AlgorithmIdentifier(
+					mData.Mac.AlgorithmID.ObjectID, DerNull.Instance);
+				DigestInfo dInfo = new DigestInfo(algId, res);
+
+				mData = new MacData(dInfo, mData.GetSalt(), itCount);
+			}
+			catch (Exception e)
+			{
+				throw new IOException("error constructing MAC: " + e.ToString());
+			}
+
+			pfx = new Pfx(info, mData);
+
+			return pfx.GetEncoded(Asn1Encodable.Der);
+		}
+	}
+}
\ No newline at end of file
diff --git a/crypto/src/pkcs/PrivateKeyInfoFactory.cs b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
new file mode 100644
index 000000000..ed566cae9
--- /dev/null
+++ b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
@@ -0,0 +1,214 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.Sec;
+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.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pkcs
+{
+	public sealed class PrivateKeyInfoFactory
+	{
+		private PrivateKeyInfoFactory()
+		{
+		}
+
+		public static PrivateKeyInfo CreatePrivateKeyInfo(
+			AsymmetricKeyParameter key)
+		{
+			if (key == null)
+				throw new ArgumentNullException("key");
+			if (!key.IsPrivate)
+				throw new ArgumentException("Public key passed - private key expected", "key");
+
+			if (key is ElGamalPrivateKeyParameters)
+			{
+				ElGamalPrivateKeyParameters _key = (ElGamalPrivateKeyParameters)key;
+				return new PrivateKeyInfo(
+					new AlgorithmIdentifier(
+					OiwObjectIdentifiers.ElGamalAlgorithm,
+					new ElGamalParameter(
+					_key.Parameters.P,
+					_key.Parameters.G).ToAsn1Object()),
+					new DerInteger(_key.X));
+			}
+
+			if (key is DsaPrivateKeyParameters)
+			{
+				DsaPrivateKeyParameters _key = (DsaPrivateKeyParameters)key;
+				return new PrivateKeyInfo(
+					new AlgorithmIdentifier(
+					X9ObjectIdentifiers.IdDsa,
+					new DsaParameter(
+					_key.Parameters.P,
+					_key.Parameters.Q,
+					_key.Parameters.G).ToAsn1Object()),
+					new DerInteger(_key.X));
+			}
+
+			if (key is DHPrivateKeyParameters)
+			{
+				DHPrivateKeyParameters _key = (DHPrivateKeyParameters)key;
+
+				DHParameter p = new DHParameter(
+					_key.Parameters.P, _key.Parameters.G, _key.Parameters.L);
+
+				return new PrivateKeyInfo(
+					new AlgorithmIdentifier(_key.AlgorithmOid, p.ToAsn1Object()),
+					new DerInteger(_key.X));
+			}
+
+			if (key is RsaKeyParameters)
+			{
+				AlgorithmIdentifier algID = new AlgorithmIdentifier(
+					PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance);
+
+				RsaPrivateKeyStructure keyStruct;
+				if (key is RsaPrivateCrtKeyParameters)
+				{
+					RsaPrivateCrtKeyParameters _key = (RsaPrivateCrtKeyParameters)key;
+
+					keyStruct = new RsaPrivateKeyStructure(
+						_key.Modulus,
+						_key.PublicExponent,
+						_key.Exponent,
+						_key.P,
+						_key.Q,
+						_key.DP,
+						_key.DQ,
+						_key.QInv);
+				}
+				else
+				{
+					RsaKeyParameters _key = (RsaKeyParameters) key;
+
+					keyStruct = new RsaPrivateKeyStructure(
+						_key.Modulus,
+						BigInteger.Zero,
+						_key.Exponent,
+						BigInteger.Zero,
+						BigInteger.Zero,
+						BigInteger.Zero,
+						BigInteger.Zero,
+						BigInteger.Zero);
+				}
+
+				return new PrivateKeyInfo(algID, keyStruct.ToAsn1Object());
+			}
+
+			if (key is ECPrivateKeyParameters)
+			{
+				ECPrivateKeyParameters _key = (ECPrivateKeyParameters)key;
+				AlgorithmIdentifier algID;
+				ECPrivateKeyStructure ec;
+
+				if (_key.AlgorithmName == "ECGOST3410")
+				{
+					if (_key.PublicKeyParamSet == null)
+						throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set");
+
+					Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters(
+						_key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet);
+
+					algID = new AlgorithmIdentifier(
+						CryptoProObjectIdentifiers.GostR3410x2001,
+						gostParams.ToAsn1Object());
+
+					// TODO Do we need to pass any parameters here?
+					ec = new ECPrivateKeyStructure(_key.D);
+				}
+				else
+				{
+					X962Parameters x962;
+					if (_key.PublicKeyParamSet == null)
+					{
+						ECDomainParameters kp = _key.Parameters;
+						X9ECParameters ecP = new X9ECParameters(kp.Curve, kp.G, kp.N, kp.H, kp.GetSeed());
+
+						x962 = new X962Parameters(ecP);
+					}
+					else
+					{
+						x962 = new X962Parameters(_key.PublicKeyParamSet);
+					}
+
+					Asn1Object x962Object = x962.ToAsn1Object();
+
+					// TODO Possible to pass the publicKey bitstring here?
+					ec = new ECPrivateKeyStructure(_key.D, x962Object);
+
+					algID = new AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey, x962Object);
+				}
+
+				return new PrivateKeyInfo(algID, ec.ToAsn1Object());
+			}
+
+			if (key is Gost3410PrivateKeyParameters)
+			{
+				Gost3410PrivateKeyParameters _key = (Gost3410PrivateKeyParameters)key;
+
+				if (_key.PublicKeyParamSet == null)
+					throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set");
+
+				byte[] keyEnc = _key.X.ToByteArrayUnsigned();
+				byte[] keyBytes = new byte[keyEnc.Length];
+
+				for (int i = 0; i != keyBytes.Length; i++)
+				{
+					keyBytes[i] = keyEnc[keyEnc.Length - 1 - i]; // must be little endian
+				}
+
+				Gost3410PublicKeyAlgParameters algParams = new Gost3410PublicKeyAlgParameters(
+					_key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet, null);
+
+				AlgorithmIdentifier algID = new AlgorithmIdentifier(
+					CryptoProObjectIdentifiers.GostR3410x94,
+					algParams.ToAsn1Object());
+
+				return new PrivateKeyInfo(algID, new DerOctetString(keyBytes));
+			}
+
+			throw new ArgumentException("Class provided is not convertible: " + key.GetType().FullName);
+		}
+
+		public static PrivateKeyInfo CreatePrivateKeyInfo(
+			char[]					passPhrase,
+			EncryptedPrivateKeyInfo	encInfo)
+		{
+			return CreatePrivateKeyInfo(passPhrase, false, encInfo);
+		}
+
+		public static PrivateKeyInfo CreatePrivateKeyInfo(
+			char[]					passPhrase,
+			bool					wrongPkcs12Zero,
+			EncryptedPrivateKeyInfo	encInfo)
+		{
+			AlgorithmIdentifier algID = encInfo.EncryptionAlgorithm;
+			IBufferedCipher cipher = PbeUtilities.CreateEngine(algID) as IBufferedCipher;
+
+			if (cipher == null)
+			{
+				// TODO Throw exception?
+			}
+
+			ICipherParameters keyParameters = PbeUtilities.GenerateCipherParameters(
+				algID, passPhrase, wrongPkcs12Zero);
+
+			cipher.Init(false, keyParameters);
+
+			byte[] keyBytes = encInfo.GetEncryptedData();
+			byte[] encoding = cipher.DoFinal(keyBytes);
+			Asn1Object asn1Data = Asn1Object.FromByteArray(encoding);
+
+			return PrivateKeyInfo.GetInstance(asn1Data);
+		}
+	}
+}
diff --git a/crypto/src/pkcs/X509CertificateEntry.cs b/crypto/src/pkcs/X509CertificateEntry.cs
new file mode 100644
index 000000000..2f81dd87b
--- /dev/null
+++ b/crypto/src/pkcs/X509CertificateEntry.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Pkcs
+{
+    public class X509CertificateEntry
+        : Pkcs12Entry
+    {
+        private readonly X509Certificate cert;
+
+		public X509CertificateEntry(
+            X509Certificate cert)
+			: base(Platform.CreateHashtable())
+        {
+            this.cert = cert;
+        }
+
+#if !(SILVERLIGHT || PORTABLE)
+        [Obsolete]
+        public X509CertificateEntry(
+            X509Certificate	cert,
+            Hashtable		attributes)
+			: base(attributes)
+        {
+            this.cert = cert;
+        }
+#endif
+
+        public X509CertificateEntry(
+            X509Certificate cert,
+            IDictionary     attributes)
+			: base(attributes)
+        {
+            this.cert = cert;
+        }
+
+		public X509Certificate Certificate
+        {
+			get { return this.cert; }
+        }
+
+		public override bool Equals(object obj)
+		{
+			X509CertificateEntry other = obj as X509CertificateEntry;
+
+			if (other == null)
+				return false;
+
+			return cert.Equals(other.cert);
+		}
+
+		public override int GetHashCode()
+		{
+			return ~cert.GetHashCode();
+		}
+	}
+}