summary refs log tree commit diff
path: root/crypto/src/pkcs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/pkcs')
-rw-r--r--crypto/src/pkcs/AsymmetricKeyEntry.cs60
-rw-r--r--crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs64
-rw-r--r--crypto/src/pkcs/PKCS12StoreBuilder.cs41
-rw-r--r--crypto/src/pkcs/Pkcs10CertificationRequest.cs465
-rw-r--r--crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs150
-rw-r--r--crypto/src/pkcs/Pkcs12Entry.cs64
-rw-r--r--crypto/src/pkcs/Pkcs12Store.cs1228
-rw-r--r--crypto/src/pkcs/Pkcs12Utilities.cs77
-rw-r--r--crypto/src/pkcs/PrivateKeyInfoFactory.cs208
-rw-r--r--crypto/src/pkcs/X509CertificateEntry.cs60
10 files changed, 2417 insertions, 0 deletions
diff --git a/crypto/src/pkcs/AsymmetricKeyEntry.cs b/crypto/src/pkcs/AsymmetricKeyEntry.cs
new file mode 100644
index 000000000..1c37631d5
--- /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
+        [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..b6b7bac65
--- /dev/null
+++ b/crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs
@@ -0,0 +1,64 @@
+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)
+        {
+            IBufferedCipher cipher = PbeUtilities.CreateEngine(algorithm) as IBufferedCipher;
+            if (cipher == null)
+                throw new Exception("Unknown encryption algorithm: " + algorithm);
+
+            Asn1Encodable pbeParameters = PbeUtilities.GenerateAlgorithmParameters(
+                algorithm, salt, iterationCount);
+            ICipherParameters cipherParameters = PbeUtilities.GenerateCipherParameters(
+                algorithm, passPhrase, pbeParameters);
+            cipher.Init(true, cipherParameters);
+            byte[] encoding = cipher.DoFinal(keyInfo.GetEncoded());
+
+            DerObjectIdentifier oid = PbeUtilities.GetObjectIdentifier(algorithm);
+            AlgorithmIdentifier algID = new AlgorithmIdentifier(oid, pbeParameters);
+            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..9f24eb18a
--- /dev/null
+++ b/crypto/src/pkcs/Pkcs10CertificationRequest.cs
@@ -0,0 +1,465 @@
+using System;
+using System.Collections;
+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 = Platform.ToUpperInvariant(signatureAlgorithm);
+			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..ecbb4ab62
--- /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 = Platform.ToUpperInvariant(signatureAlgorithm);
+			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..6718aea10
--- /dev/null
+++ b/crypto/src/pkcs/Pkcs12Store.cs
@@ -0,0 +1,1228 @@
+using System;
+using System.Collections;
+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
+                            {
+                                Console.WriteLine("extra " + b.BagID);
+                                Console.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
+                            {
+                                Console.WriteLine("extra " + b.BagID);
+                                Console.WriteLine("extra " + Asn1Dump.DumpAsString(b));
+                            }
+                        }
+                    }
+                    else
+                    {
+                        Console.WriteLine("extra " + oid);
+                        Console.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)
+        {
+            IBufferedCipher cipher = PbeUtilities.CreateEngine(algId.ObjectID) as IBufferedCipher;
+
+            if (cipher == null)
+                throw new Exception("Unknown encryption algorithm: " + algId.ObjectID);
+
+            Pkcs12PbeParams pbeParameters = Pkcs12PbeParams.GetInstance(algId.Parameters);
+            ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters(
+                algId.ObjectID, password, wrongPkcs12Zero, pbeParameters);
+            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 = Platform.ToLowerInvariant(alias);
+                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 = Platform.ToLowerInvariant(alias);
+                    string k = (string)keys[lower];
+
+                    if (k == null)
+                        return null;
+
+                    return orig[k];
+                }
+                set
+                {
+                    string lower = Platform.ToLowerInvariant(alias);
+                    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..723d50f08
--- /dev/null
+++ b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
@@ -0,0 +1,208 @@
+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)
+                throw new Exception("Unknown encryption algorithm: " + algID.ObjectID);
+
+            ICipherParameters cipherParameters = PbeUtilities.GenerateCipherParameters(
+                algID, passPhrase, wrongPkcs12Zero);
+            cipher.Init(false, cipherParameters);
+            byte[] keyBytes = cipher.DoFinal(encInfo.GetEncryptedData());
+
+            return PrivateKeyInfo.GetInstance(keyBytes);
+        }
+    }
+}
diff --git a/crypto/src/pkcs/X509CertificateEntry.cs b/crypto/src/pkcs/X509CertificateEntry.cs
new file mode 100644
index 000000000..a621619fb
--- /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
+        [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();
+		}
+	}
+}