summary refs log tree commit diff
path: root/Crypto/src/x509
diff options
context:
space:
mode:
authorOren Novotny <oren@novotny.org>2014-02-26 10:08:50 -0500
committerOren Novotny <oren@novotny.org>2014-02-26 10:08:50 -0500
commit176743ab5faec2dd275b5efd3a2dd62c610f237a (patch)
tree1d2e50c534a479d749c266d7c52434d8f17f86aa /Crypto/src/x509
parentAdd git files (diff)
downloadBouncyCastle.NET-ed25519-654c26abd79e9451e5a9bd108f1358bc2849fdbf.tar.xz
Add BouncyCastle PCL files v1.7.0
Diffstat (limited to 'Crypto/src/x509')
-rw-r--r--Crypto/src/x509/AttributeCertificateHolder.cs442
-rw-r--r--Crypto/src/x509/AttributeCertificateIssuer.cs199
-rw-r--r--Crypto/src/x509/IX509AttributeCertificate.cs57
-rw-r--r--Crypto/src/x509/IX509Extension.cs27
-rw-r--r--Crypto/src/x509/PEMParser.cs94
-rw-r--r--Crypto/src/x509/PrincipalUtil.cs70
-rw-r--r--Crypto/src/x509/SubjectPublicKeyInfoFactory.cs187
-rw-r--r--Crypto/src/x509/X509AttrCertParser.cs173
-rw-r--r--Crypto/src/x509/X509Attribute.cs76
-rw-r--r--Crypto/src/x509/X509CertPairParser.cs95
-rw-r--r--Crypto/src/x509/X509Certificate.cs595
-rw-r--r--Crypto/src/x509/X509CertificatePair.cs123
-rw-r--r--Crypto/src/x509/X509CertificateParser.cs183
-rw-r--r--Crypto/src/x509/X509Crl.cs403
-rw-r--r--Crypto/src/x509/X509CrlEntry.cs201
-rw-r--r--Crypto/src/x509/X509CrlParser.cs195
-rw-r--r--Crypto/src/x509/X509ExtensionBase.cs82
-rw-r--r--Crypto/src/x509/X509KeyUsage.cs59
-rw-r--r--Crypto/src/x509/X509SignatureUtil.cs128
-rw-r--r--Crypto/src/x509/X509Utilities.cs188
-rw-r--r--Crypto/src/x509/X509V1CertificateGenerator.cs205
-rw-r--r--Crypto/src/x509/X509V2AttributeCertificate.cs255
-rw-r--r--Crypto/src/x509/X509V2AttributeCertificateGenerator.cs180
-rw-r--r--Crypto/src/x509/X509V2CRLGenerator.cs261
-rw-r--r--Crypto/src/x509/X509V3CertificateGenerator.cs346
-rw-r--r--Crypto/src/x509/extension/AuthorityKeyIdentifierStructure.cs102
-rw-r--r--Crypto/src/x509/extension/SubjectKeyIdentifierStructure.cs49
-rw-r--r--Crypto/src/x509/extension/X509ExtensionUtil.cs89
-rw-r--r--Crypto/src/x509/store/IX509Selector.cs15
-rw-r--r--Crypto/src/x509/store/IX509Store.cs11
-rw-r--r--Crypto/src/x509/store/IX509StoreParameters.cs8
-rw-r--r--Crypto/src/x509/store/NoSuchStoreException.cs25
-rw-r--r--Crypto/src/x509/store/X509AttrCertStoreSelector.cs376
-rw-r--r--Crypto/src/x509/store/X509CertPairStoreSelector.cs92
-rw-r--r--Crypto/src/x509/store/X509CertStoreSelector.cs337
-rw-r--r--Crypto/src/x509/store/X509CollectionStore.cs51
-rw-r--r--Crypto/src/x509/store/X509CollectionStoreParameters.cs60
-rw-r--r--Crypto/src/x509/store/X509CrlStoreSelector.cs283
-rw-r--r--Crypto/src/x509/store/X509StoreException.cs25
-rw-r--r--Crypto/src/x509/store/X509StoreFactory.cs61
40 files changed, 6408 insertions, 0 deletions
diff --git a/Crypto/src/x509/AttributeCertificateHolder.cs b/Crypto/src/x509/AttributeCertificateHolder.cs
new file mode 100644
index 000000000..3a6af4c20
--- /dev/null
+++ b/Crypto/src/x509/AttributeCertificateHolder.cs
@@ -0,0 +1,442 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.X509
+{
+	/// <remarks>
+	/// The Holder object.
+	/// <pre>
+ 	/// Holder ::= SEQUENCE {
+ 	///		baseCertificateID   [0] IssuerSerial OPTIONAL,
+ 	///			-- the issuer and serial number of
+ 	///			-- the holder's Public Key Certificate
+ 	///		entityName          [1] GeneralNames OPTIONAL,
+ 	///			-- the name of the claimant or role
+ 	///		objectDigestInfo    [2] ObjectDigestInfo OPTIONAL
+ 	///			-- used to directly authenticate the holder,
+ 	///			-- for example, an executable
+ 	/// }
+	/// </pre>
+	/// </remarks>
+	public class AttributeCertificateHolder
+		//: CertSelector, Selector
+		: IX509Selector
+	{
+		internal readonly Holder holder;
+
+		internal AttributeCertificateHolder(
+			Asn1Sequence seq)
+		{
+			holder = Holder.GetInstance(seq);
+		}
+
+		public AttributeCertificateHolder(
+			X509Name	issuerName,
+			BigInteger	serialNumber)
+		{
+			holder = new Holder(
+				new IssuerSerial(
+					GenerateGeneralNames(issuerName),
+					new DerInteger(serialNumber)));
+		}
+
+		public AttributeCertificateHolder(
+			X509Certificate	cert)
+		{
+			X509Name name;
+			try
+			{
+				name = PrincipalUtilities.GetIssuerX509Principal(cert);
+			}
+			catch (Exception e)
+			{
+				throw new CertificateParsingException(e.Message);
+			}
+
+			holder = new Holder(new IssuerSerial(GenerateGeneralNames(name), new DerInteger(cert.SerialNumber)));
+		}
+
+		public AttributeCertificateHolder(
+			X509Name principal)
+		{
+			holder = new Holder(GenerateGeneralNames(principal));
+		}
+
+		/**
+		 * Constructs a holder for v2 attribute certificates with a hash value for
+		 * some type of object.
+		 * <p>
+		 * <code>digestedObjectType</code> can be one of the following:
+		 * <ul>
+		 * <li>0 - publicKey - A hash of the public key of the holder must be
+		 * passed.</li>
+		 * <li>1 - publicKeyCert - A hash of the public key certificate of the
+		 * holder must be passed.</li>
+		 * <li>2 - otherObjectDigest - A hash of some other object type must be
+		 * passed. <code>otherObjectTypeID</code> must not be empty.</li>
+		 * </ul>
+		 * </p>
+		 * <p>This cannot be used if a v1 attribute certificate is used.</p>
+		 *
+		 * @param digestedObjectType The digest object type.
+		 * @param digestAlgorithm The algorithm identifier for the hash.
+		 * @param otherObjectTypeID The object type ID if
+		 *            <code>digestedObjectType</code> is
+		 *            <code>otherObjectDigest</code>.
+		 * @param objectDigest The hash value.
+		 */
+		public AttributeCertificateHolder(
+			int		digestedObjectType,
+			string	digestAlgorithm,
+			string	otherObjectTypeID,
+			byte[]	objectDigest)
+		{
+			// TODO Allow 'objectDigest' to be null?
+
+			holder = new Holder(new ObjectDigestInfo(digestedObjectType, otherObjectTypeID,
+				new AlgorithmIdentifier(digestAlgorithm), Arrays.Clone(objectDigest)));
+		}
+
+		/**
+		 * Returns the digest object type if an object digest info is used.
+		 * <p>
+		 * <ul>
+		 * <li>0 - publicKey - A hash of the public key of the holder must be
+		 * passed.</li>
+		 * <li>1 - publicKeyCert - A hash of the public key certificate of the
+		 * holder must be passed.</li>
+		 * <li>2 - otherObjectDigest - A hash of some other object type must be
+		 * passed. <code>otherObjectTypeID</code> must not be empty.</li>
+		 * </ul>
+		 * </p>
+		 *
+		 * @return The digest object type or -1 if no object digest info is set.
+		 */
+		public int DigestedObjectType
+		{
+			get
+			{
+				ObjectDigestInfo odi = holder.ObjectDigestInfo;
+
+				return odi == null
+					?	-1
+					:	odi.DigestedObjectType.Value.IntValue;
+			}
+		}
+
+		/**
+		 * Returns the other object type ID if an object digest info is used.
+		 *
+		 * @return The other object type ID or <code>null</code> if no object
+		 *         digest info is set.
+		 */
+		public string DigestAlgorithm
+		{
+			get
+			{
+				ObjectDigestInfo odi = holder.ObjectDigestInfo;
+
+				return odi == null
+					?	null
+					:	odi.DigestAlgorithm.ObjectID.Id;
+			}
+		}
+
+		/**
+		 * Returns the hash if an object digest info is used.
+		 *
+		 * @return The hash or <code>null</code> if no object digest info is set.
+		 */
+		public byte[] GetObjectDigest()
+		{
+			ObjectDigestInfo odi = holder.ObjectDigestInfo;
+
+			return odi == null
+				?	null
+				:	odi.ObjectDigest.GetBytes();
+		}
+
+		/**
+		 * Returns the digest algorithm ID if an object digest info is used.
+		 *
+		 * @return The digest algorithm ID or <code>null</code> if no object
+		 *         digest info is set.
+		 */
+		public string OtherObjectTypeID
+		{
+			get
+			{
+				ObjectDigestInfo odi = holder.ObjectDigestInfo;
+
+				return odi == null
+					?	null
+					:	odi.OtherObjectTypeID.Id;
+			}
+		}
+
+		private GeneralNames GenerateGeneralNames(
+			X509Name principal)
+		{
+//			return GeneralNames.GetInstance(new DerSequence(new GeneralName(principal)));
+			return new GeneralNames(new GeneralName(principal));
+		}
+
+		private bool MatchesDN(
+			X509Name		subject,
+			GeneralNames	targets)
+		{
+			GeneralName[] names = targets.GetNames();
+
+			for (int i = 0; i != names.Length; i++)
+			{
+				GeneralName gn = names[i];
+
+				if (gn.TagNo == GeneralName.DirectoryName)
+				{
+					try
+					{
+						if (X509Name.GetInstance(gn.Name).Equivalent(subject))
+						{
+							return true;
+						}
+					}
+					catch (Exception)
+					{
+					}
+				}
+			}
+
+			return false;
+		}
+
+		private object[] GetNames(
+			GeneralName[] names)
+		{
+            int count = 0;
+            for (int i = 0; i != names.Length; i++)
+            {
+                if (names[i].TagNo == GeneralName.DirectoryName)
+                {
+                    ++count;
+                }
+            }
+
+            object[] result = new object[count];
+
+            int pos = 0;
+            for (int i = 0; i != names.Length; i++)
+            {
+                if (names[i].TagNo == GeneralName.DirectoryName)
+                {
+                    result[pos++] = X509Name.GetInstance(names[i].Name);
+                }
+            }
+
+            return result;
+        }
+
+		private X509Name[] GetPrincipals(
+			GeneralNames names)
+		{
+			object[] p = this.GetNames(names.GetNames());
+
+            int count = 0;
+
+            for (int i = 0; i != p.Length; i++)
+			{
+				if (p[i] is X509Name)
+				{
+                    ++count;
+				}
+			}
+
+            X509Name[] result = new X509Name[count];
+
+            int pos = 0;
+            for (int i = 0; i != p.Length; i++)
+            {
+                if (p[i] is X509Name)
+                {
+                    result[pos++] = (X509Name)p[i];
+                }
+            }
+
+            return result;
+        }
+
+		/**
+		 * Return any principal objects inside the attribute certificate holder entity names field.
+		 *
+		 * @return an array of IPrincipal objects (usually X509Name), null if no entity names field is set.
+		 */
+		public X509Name[] GetEntityNames()
+		{
+			if (holder.EntityName != null)
+			{
+				return GetPrincipals(holder.EntityName);
+			}
+
+			return null;
+		}
+
+		/**
+		 * Return the principals associated with the issuer attached to this holder
+		 *
+		 * @return an array of principals, null if no BaseCertificateID is set.
+		 */
+		public X509Name[] GetIssuer()
+		{
+			if (holder.BaseCertificateID != null)
+			{
+				return GetPrincipals(holder.BaseCertificateID.Issuer);
+			}
+
+			return null;
+		}
+
+		/**
+		 * Return the serial number associated with the issuer attached to this holder.
+		 *
+		 * @return the certificate serial number, null if no BaseCertificateID is set.
+		 */
+		public BigInteger SerialNumber
+		{
+			get
+			{
+				if (holder.BaseCertificateID != null)
+				{
+					return holder.BaseCertificateID.Serial.Value;
+				}
+
+				return null;
+			}
+		}
+
+		public object Clone()
+		{
+			return new AttributeCertificateHolder((Asn1Sequence)holder.ToAsn1Object());
+		}
+
+		public bool Match(
+//			Certificate cert)
+			X509Certificate x509Cert)
+		{
+//			if (!(cert is X509Certificate))
+//			{
+//				return false;
+//			}
+//
+//			X509Certificate x509Cert = (X509Certificate)cert;
+
+			try
+			{
+				if (holder.BaseCertificateID != null)
+				{
+					return holder.BaseCertificateID.Serial.Value.Equals(x509Cert.SerialNumber)
+						&& MatchesDN(PrincipalUtilities.GetIssuerX509Principal(x509Cert), holder.BaseCertificateID.Issuer);
+				}
+
+				if (holder.EntityName != null)
+				{
+					if (MatchesDN(PrincipalUtilities.GetSubjectX509Principal(x509Cert), holder.EntityName))
+					{
+						return true;
+					}
+				}
+
+				if (holder.ObjectDigestInfo != null)
+				{
+					IDigest md = null;
+					try
+					{
+						md = DigestUtilities.GetDigest(DigestAlgorithm);
+					}
+					catch (Exception)
+					{
+						return false;
+					}
+
+					switch (DigestedObjectType)
+					{
+						case ObjectDigestInfo.PublicKey:
+						{
+							// TODO: DSA Dss-parms
+
+							//byte[] b = x509Cert.GetPublicKey().getEncoded();
+							// TODO Is this the right way to encode?
+							byte[] b = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(
+								x509Cert.GetPublicKey()).GetEncoded();
+							md.BlockUpdate(b, 0, b.Length);
+							break;
+						}
+
+						case ObjectDigestInfo.PublicKeyCert:
+						{
+							byte[] b = x509Cert.GetEncoded();
+							md.BlockUpdate(b, 0, b.Length);
+							break;
+						}
+
+						// TODO Default handler?
+					}
+
+					// TODO Shouldn't this be the other way around?
+					if (!Arrays.AreEqual(DigestUtilities.DoFinal(md), GetObjectDigest()))
+					{
+						return false;
+					}
+				}
+			}
+			catch (CertificateEncodingException)
+			{
+				return false;
+			}
+
+			return false;
+		}
+
+		public override bool Equals(
+			object obj)
+		{
+			if (obj == this)
+			{
+				return true;
+			}
+
+			if (!(obj is AttributeCertificateHolder))
+			{
+				return false;
+			}
+
+			AttributeCertificateHolder other = (AttributeCertificateHolder)obj;
+
+			return this.holder.Equals(other.holder);
+		}
+
+		public override int GetHashCode()
+		{
+			return this.holder.GetHashCode();
+		}
+
+		public bool Match(
+			object obj)
+		{
+			if (!(obj is X509Certificate))
+			{
+				return false;
+			}
+
+//			return Match((Certificate)obj);
+			return Match((X509Certificate)obj);
+		}
+	}
+}
diff --git a/Crypto/src/x509/AttributeCertificateIssuer.cs b/Crypto/src/x509/AttributeCertificateIssuer.cs
new file mode 100644
index 000000000..7df1416d3
--- /dev/null
+++ b/Crypto/src/x509/AttributeCertificateIssuer.cs
@@ -0,0 +1,199 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.X509
+{
+	/**
+	 * Carrying class for an attribute certificate issuer.
+	 */
+	public class AttributeCertificateIssuer
+		//: CertSelector, Selector
+		: IX509Selector
+	{
+		internal readonly Asn1Encodable form;
+
+		/**
+		 * Set the issuer directly with the ASN.1 structure.
+		 *
+		 * @param issuer The issuer
+		 */
+		public AttributeCertificateIssuer(
+			AttCertIssuer issuer)
+		{
+			form = issuer.Issuer;
+		}
+
+		public AttributeCertificateIssuer(
+			X509Name principal)
+		{
+//			form = new V2Form(GeneralNames.GetInstance(new DerSequence(new GeneralName(principal))));
+			form = new V2Form(new GeneralNames(new GeneralName(principal)));
+		}
+
+		private object[] GetNames()
+		{
+			GeneralNames name;
+			if (form is V2Form)
+			{
+				name = ((V2Form)form).IssuerName;
+			}
+			else
+			{
+				name = (GeneralNames)form;
+			}
+
+            GeneralName[] names = name.GetNames();
+
+            int count = 0;
+            for (int i = 0; i != names.Length; i++)
+            {
+                if (names[i].TagNo == GeneralName.DirectoryName)
+                {
+                    ++count;
+                }
+            }
+
+            object[] result = new object[count];
+
+            int pos = 0;
+            for (int i = 0; i != names.Length; i++)
+			{
+				if (names[i].TagNo == GeneralName.DirectoryName)
+				{
+                    result[pos++] = X509Name.GetInstance(names[i].Name);
+				}
+			}
+
+            return result;
+        }
+
+		/// <summary>Return any principal objects inside the attribute certificate issuer object.</summary>
+		/// <returns>An array of IPrincipal objects (usually X509Principal).</returns>
+		public X509Name[] GetPrincipals()
+		{
+			object[] p = this.GetNames();
+
+            int count = 0;
+            for (int i = 0; i != p.Length; i++)
+            {
+                if (p[i] is X509Name)
+                {
+                    ++count;
+                }
+            }
+
+            X509Name[] result = new X509Name[count];
+
+            int pos = 0;
+			for (int i = 0; i != p.Length; i++)
+			{
+				if (p[i] is X509Name)
+				{
+					result[pos++] = (X509Name)p[i];
+				}
+			}
+
+            return result;
+		}
+
+		private bool MatchesDN(
+			X509Name		subject,
+			GeneralNames	targets)
+		{
+			GeneralName[] names = targets.GetNames();
+
+			for (int i = 0; i != names.Length; i++)
+			{
+				GeneralName gn = names[i];
+
+				if (gn.TagNo == GeneralName.DirectoryName)
+				{
+					try
+					{
+						if (X509Name.GetInstance(gn.Name).Equivalent(subject))
+						{
+							return true;
+						}
+					}
+					catch (Exception)
+					{
+					}
+				}
+			}
+
+			return false;
+		}
+
+		public object Clone()
+		{
+			return new AttributeCertificateIssuer(AttCertIssuer.GetInstance(form));
+		}
+
+		public bool Match(
+//			Certificate cert)
+			X509Certificate x509Cert)
+		{
+//			if (!(cert is X509Certificate))
+//			{
+//				return false;
+//			}
+//
+//			X509Certificate x509Cert = (X509Certificate)cert;
+
+			if (form is V2Form)
+			{
+				V2Form issuer = (V2Form) form;
+				if (issuer.BaseCertificateID != null)
+				{
+					return issuer.BaseCertificateID.Serial.Value.Equals(x509Cert.SerialNumber)
+						&& MatchesDN(x509Cert.IssuerDN, issuer.BaseCertificateID.Issuer);
+				}
+
+				return MatchesDN(x509Cert.SubjectDN, issuer.IssuerName);
+			}
+
+			return MatchesDN(x509Cert.SubjectDN, (GeneralNames) form);
+		}
+
+		public override bool Equals(
+			object obj)
+		{
+			if (obj == this)
+			{
+				return true;
+			}
+
+			if (!(obj is AttributeCertificateIssuer))
+			{
+				return false;
+			}
+
+			AttributeCertificateIssuer other = (AttributeCertificateIssuer)obj;
+
+			return this.form.Equals(other.form);
+		}
+
+		public override int GetHashCode()
+		{
+			return this.form.GetHashCode();
+		}
+
+		public bool Match(
+			object obj)
+		{
+			if (!(obj is X509Certificate))
+			{
+				return false;
+			}
+
+			//return Match((Certificate)obj);
+			return Match((X509Certificate)obj);
+		}
+	}
+}
diff --git a/Crypto/src/x509/IX509AttributeCertificate.cs b/Crypto/src/x509/IX509AttributeCertificate.cs
new file mode 100644
index 000000000..9a3004e01
--- /dev/null
+++ b/Crypto/src/x509/IX509AttributeCertificate.cs
@@ -0,0 +1,57 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.X509
+{
+	/// <remarks>Interface for an X.509 Attribute Certificate.</remarks>
+	public interface IX509AttributeCertificate
+		: IX509Extension
+	{
+		/// <summary>The version number for the certificate.</summary>
+		int Version { get; }
+
+		/// <summary>The serial number for the certificate.</summary>
+		BigInteger SerialNumber { get; }
+
+		/// <summary>The UTC DateTime before which the certificate is not valid.</summary>
+		DateTime NotBefore { get; }
+
+		/// <summary>The UTC DateTime after which the certificate is not valid.</summary>
+		DateTime NotAfter { get; }
+
+		/// <summary>The holder of the certificate.</summary>
+		AttributeCertificateHolder Holder { get; }
+
+		/// <summary>The issuer details for the certificate.</summary>
+		AttributeCertificateIssuer Issuer { get; }
+
+		/// <summary>Return the attributes contained in the attribute block in the certificate.</summary>
+		/// <returns>An array of attributes.</returns>
+		X509Attribute[] GetAttributes();
+
+		/// <summary>Return the attributes with the same type as the passed in oid.</summary>
+		/// <param name="oid">The object identifier we wish to match.</param>
+		/// <returns>An array of matched attributes, null if there is no match.</returns>
+		X509Attribute[] GetAttributes(string oid);
+
+		bool[] GetIssuerUniqueID();
+
+		bool IsValidNow { get; }
+		bool IsValid(DateTime date);
+
+		void CheckValidity();
+		void CheckValidity(DateTime date);
+
+		byte[] GetSignature();
+
+		void Verify(AsymmetricKeyParameter publicKey);
+
+		/// <summary>Return an ASN.1 encoded byte array representing the attribute certificate.</summary>
+		/// <returns>An ASN.1 encoded byte array.</returns>
+		/// <exception cref="IOException">If the certificate cannot be encoded.</exception>
+		byte[] GetEncoded();
+	}
+}
diff --git a/Crypto/src/x509/IX509Extension.cs b/Crypto/src/x509/IX509Extension.cs
new file mode 100644
index 000000000..e861e8736
--- /dev/null
+++ b/Crypto/src/x509/IX509Extension.cs
@@ -0,0 +1,27 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.X509
+{
+	public interface IX509Extension
+	{
+		/// <summary>
+		/// Get all critical extension values, by oid
+		/// </summary>
+		/// <returns>IDictionary with string (OID) keys and Asn1OctetString values</returns>
+		ISet GetCriticalExtensionOids();
+
+		/// <summary>
+		/// Get all non-critical extension values, by oid
+		/// </summary>
+		/// <returns>IDictionary with string (OID) keys and Asn1OctetString values</returns>
+		ISet GetNonCriticalExtensionOids();
+
+		[Obsolete("Use version taking a DerObjectIdentifier instead")]
+		Asn1OctetString GetExtensionValue(string oid);
+
+		Asn1OctetString GetExtensionValue(DerObjectIdentifier oid);
+	}
+}
diff --git a/Crypto/src/x509/PEMParser.cs b/Crypto/src/x509/PEMParser.cs
new file mode 100644
index 000000000..8c117f323
--- /dev/null
+++ b/Crypto/src/x509/PEMParser.cs
@@ -0,0 +1,94 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.X509
+{
+	class PemParser
+	{
+		private readonly string _header1;
+		private readonly string _header2;
+		private readonly string _footer1;
+		private readonly string _footer2;
+
+		internal PemParser(
+			string type)
+		{
+			_header1 = "-----BEGIN " + type + "-----";
+			_header2 = "-----BEGIN X509 " + type + "-----";
+			_footer1 = "-----END " + type + "-----";
+			_footer2 = "-----END X509 " + type + "-----";
+		}
+
+		private string ReadLine(
+			Stream inStream)
+		{
+			int c;
+			StringBuilder l = new StringBuilder();
+
+			do
+			{
+				while (((c = inStream.ReadByte()) != '\r') && c != '\n' && (c >= 0))
+				{
+					if (c == '\r')
+					{
+						continue;
+					}
+
+					l.Append((char)c);
+				}
+			}
+			while (c >= 0 && l.Length == 0);
+
+			if (c < 0)
+			{
+				return null;
+			}
+
+			return l.ToString();
+		}
+
+		internal Asn1Sequence ReadPemObject(
+			Stream inStream)
+		{
+			string line;
+			StringBuilder pemBuf = new StringBuilder();
+
+			while ((line = ReadLine(inStream)) != null)
+			{
+				if (line.StartsWith(_header1) || line.StartsWith(_header2))
+				{
+					break;
+				}
+			}
+
+			while ((line = ReadLine(inStream)) != null)
+			{
+				if (line.StartsWith(_footer1) || line.StartsWith(_footer2))
+				{
+					break;
+				}
+
+				pemBuf.Append(line);
+			}
+
+			if (pemBuf.Length != 0)
+			{
+				Asn1Object o = Asn1Object.FromByteArray(Base64.Decode(pemBuf.ToString()));
+
+				if (!(o is Asn1Sequence))
+				{
+					throw new IOException("malformed PEM data encountered");
+				}
+
+				return (Asn1Sequence) o;
+			}
+
+			return null;
+		}
+	}
+}
+
diff --git a/Crypto/src/x509/PrincipalUtil.cs b/Crypto/src/x509/PrincipalUtil.cs
new file mode 100644
index 000000000..0edc4a395
--- /dev/null
+++ b/Crypto/src/x509/PrincipalUtil.cs
@@ -0,0 +1,70 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+
+namespace Org.BouncyCastle.X509
+{
+	/// <remarks>
+	/// A utility class that will extract X509Principal objects from X.509 certificates.
+	/// <p>
+	/// Use this in preference to trying to recreate a principal from a string, not all
+	/// DNs are what they should be, so it's best to leave them encoded where they
+	/// can be.</p>
+	/// </remarks>
+	public class PrincipalUtilities
+	{
+		/// <summary>Return the issuer of the given cert as an X509Principal.</summary>
+		public static X509Name GetIssuerX509Principal(
+			X509Certificate cert)
+		{
+			try
+			{
+				TbsCertificateStructure tbsCert = TbsCertificateStructure.GetInstance(
+					Asn1Object.FromByteArray(cert.GetTbsCertificate()));
+
+				return tbsCert.Issuer;
+			}
+			catch (Exception e)
+			{
+				throw new CertificateEncodingException("Could not extract issuer", e);
+			}
+		}
+
+		/// <summary>Return the subject of the given cert as an X509Principal.</summary>
+		public static X509Name GetSubjectX509Principal(
+			X509Certificate cert)
+		{
+			try
+			{
+				TbsCertificateStructure tbsCert = TbsCertificateStructure.GetInstance(
+					Asn1Object.FromByteArray(cert.GetTbsCertificate()));
+
+				return tbsCert.Subject;
+			}
+			catch (Exception e)
+			{
+				throw new CertificateEncodingException("Could not extract subject", e);
+			}
+		}
+
+		/// <summary>Return the issuer of the given CRL as an X509Principal.</summary>
+		public static X509Name GetIssuerX509Principal(
+			X509Crl crl)
+		{
+			try
+			{
+				TbsCertificateList tbsCertList = TbsCertificateList.GetInstance(
+					Asn1Object.FromByteArray(crl.GetTbsCertList()));
+
+				return tbsCertList.Issuer;
+			}
+			catch (Exception e)
+			{
+				throw new CrlException("Could not extract issuer", e);
+			}
+		}
+	}
+}
diff --git a/Crypto/src/x509/SubjectPublicKeyInfoFactory.cs b/Crypto/src/x509/SubjectPublicKeyInfoFactory.cs
new file mode 100644
index 000000000..54ca78090
--- /dev/null
+++ b/Crypto/src/x509/SubjectPublicKeyInfoFactory.cs
@@ -0,0 +1,187 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Text;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.CryptoPro;
+using Org.BouncyCastle.Asn1.Oiw;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.X509
+{
+    /// <summary>
+    /// A factory to produce Public Key Info Objects.
+    /// </summary>
+    public sealed class SubjectPublicKeyInfoFactory
+    {
+        private SubjectPublicKeyInfoFactory()
+        {
+        }
+
+		/// <summary>
+        /// Create a Subject Public Key Info object for a given public key.
+        /// </summary>
+        /// <param name="key">One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters</param>
+        /// <returns>A subject public key info object.</returns>
+        /// <exception cref="Exception">Throw exception if object provided is not one of the above.</exception>
+        public static SubjectPublicKeyInfo CreateSubjectPublicKeyInfo(
+			AsymmetricKeyParameter key)
+        {
+			if (key == null)
+				throw new ArgumentNullException("key");
+            if (key.IsPrivate)
+                throw new ArgumentException("Private key passed - public key expected.", "key");
+
+			if (key is ElGamalPublicKeyParameters)
+            {
+				ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)key;
+				ElGamalParameters kp = _key.Parameters;
+
+				SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
+					new AlgorithmIdentifier(
+						OiwObjectIdentifiers.ElGamalAlgorithm,
+						new ElGamalParameter(kp.P, kp.G).ToAsn1Object()),
+						new DerInteger(_key.Y));
+
+				return info;
+            }
+
+			if (key is DsaPublicKeyParameters)
+            {
+                DsaPublicKeyParameters _key = (DsaPublicKeyParameters) key;
+				DsaParameters kp = _key.Parameters;
+				Asn1Encodable ae = kp == null
+					?	null
+					:	new DsaParameter(kp.P, kp.Q, kp.G).ToAsn1Object();
+
+				return new SubjectPublicKeyInfo(
+                    new AlgorithmIdentifier(X9ObjectIdentifiers.IdDsa, ae),
+					new DerInteger(_key.Y));
+            }
+
+			if (key is DHPublicKeyParameters)
+            {
+                DHPublicKeyParameters _key = (DHPublicKeyParameters) key;
+				DHParameters kp = _key.Parameters;
+
+				SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
+                    new AlgorithmIdentifier(
+						_key.AlgorithmOid,
+						new DHParameter(kp.P, kp.G, kp.L).ToAsn1Object()),
+						new DerInteger(_key.Y));
+
+				return info;
+            } // End of DH
+
+            if (key is RsaKeyParameters)
+            {
+                RsaKeyParameters _key = (RsaKeyParameters) key;
+
+				SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
+					new AlgorithmIdentifier(PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance),
+					new RsaPublicKeyStructure(_key.Modulus, _key.Exponent).ToAsn1Object());
+
+				return info;
+            } // End of RSA.
+
+			if (key is ECPublicKeyParameters)
+            {
+                ECPublicKeyParameters _key = (ECPublicKeyParameters) key;
+
+				if (_key.AlgorithmName == "ECGOST3410")
+				{
+					if (_key.PublicKeyParamSet == null)
+						throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set");
+
+					ECPoint q = _key.Q;
+					BigInteger bX = q.X.ToBigInteger();
+					BigInteger bY = q.Y.ToBigInteger();
+
+					byte[] encKey = new byte[64];
+					ExtractBytes(encKey, 0, bX);
+					ExtractBytes(encKey, 32, bY);
+
+					Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters(
+						_key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet);
+
+					AlgorithmIdentifier algID = new AlgorithmIdentifier(
+						CryptoProObjectIdentifiers.GostR3410x2001,
+						gostParams.ToAsn1Object());
+
+					return new SubjectPublicKeyInfo(algID, new DerOctetString(encKey));
+				}
+				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);
+					}
+
+					Asn1OctetString p = (Asn1OctetString)(new X9ECPoint(_key.Q).ToAsn1Object());
+
+					AlgorithmIdentifier algID = new AlgorithmIdentifier(
+						X9ObjectIdentifiers.IdECPublicKey, x962.ToAsn1Object());
+
+					return new SubjectPublicKeyInfo(algID, p.GetOctets());
+				}
+			} // End of EC
+
+			if (key is Gost3410PublicKeyParameters)
+			{
+				Gost3410PublicKeyParameters _key = (Gost3410PublicKeyParameters) key;
+
+				if (_key.PublicKeyParamSet == null)
+					throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set");
+
+				byte[] keyEnc = _key.Y.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);
+
+				AlgorithmIdentifier algID = new AlgorithmIdentifier(
+					CryptoProObjectIdentifiers.GostR3410x94,
+					algParams.ToAsn1Object());
+
+				return new SubjectPublicKeyInfo(algID, new DerOctetString(keyBytes));
+			}
+
+			throw new ArgumentException("Class provided no convertible: " + key.GetType().FullName);
+		}
+
+		private static void ExtractBytes(
+			byte[]		encKey,
+			int			offset,
+			BigInteger	bI)
+		{
+			byte[] val = bI.ToByteArray();
+			int n = (bI.BitLength + 7) / 8;
+
+			for (int i = 0; i < n; ++i)
+			{
+				encKey[offset + i] = val[val.Length - 1 - i];
+			}
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509AttrCertParser.cs b/Crypto/src/x509/X509AttrCertParser.cs
new file mode 100644
index 000000000..a5c07362e
--- /dev/null
+++ b/Crypto/src/x509/X509AttrCertParser.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.X509
+{
+	public class X509AttrCertParser
+	{
+		private static readonly PemParser PemAttrCertParser = new PemParser("ATTRIBUTE CERTIFICATE");
+
+		private Asn1Set	sData;
+		private int		sDataObjectCount;
+		private Stream	currentStream;
+
+		private IX509AttributeCertificate ReadDerCertificate(
+			Asn1InputStream dIn)
+		{
+			Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject();
+
+			if (seq.Count > 1 && seq[0] is DerObjectIdentifier)
+			{
+				if (seq[0].Equals(PkcsObjectIdentifiers.SignedData))
+				{
+					sData = SignedData.GetInstance(
+						Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Certificates;
+
+					return GetCertificate();
+				}
+			}
+
+//			return new X509V2AttributeCertificate(seq.getEncoded());
+			return new X509V2AttributeCertificate(AttributeCertificate.GetInstance(seq));
+		}
+
+		private IX509AttributeCertificate GetCertificate()
+		{
+			if (sData != null)
+			{
+				while (sDataObjectCount < sData.Count)
+				{
+					object obj = sData[sDataObjectCount++];
+
+					if (obj is Asn1TaggedObject && ((Asn1TaggedObject)obj).TagNo == 2)
+					{
+						//return new X509V2AttributeCertificate(
+						//	Asn1Sequence.GetInstance((Asn1TaggedObject)obj, false).GetEncoded());
+						return new X509V2AttributeCertificate(
+							AttributeCertificate.GetInstance(
+								Asn1Sequence.GetInstance((Asn1TaggedObject)obj, false)));
+					}
+				}
+			}
+
+			return null;
+		}
+
+		private IX509AttributeCertificate ReadPemCertificate(
+			Stream inStream)
+		{
+			Asn1Sequence seq = PemAttrCertParser.ReadPemObject(inStream);
+
+			return seq == null
+				?	null
+				//:	new X509V2AttributeCertificate(seq.getEncoded());
+				:	new X509V2AttributeCertificate(AttributeCertificate.GetInstance(seq));
+		}
+
+		/// <summary>
+		/// Create loading data from byte array.
+		/// </summary>
+		/// <param name="input"></param>
+		public IX509AttributeCertificate ReadAttrCert(
+			byte[] input)
+		{
+			return ReadAttrCert(new MemoryStream(input, false));
+		}
+
+		/// <summary>
+		/// Create loading data from byte array.
+		/// </summary>
+		/// <param name="input"></param>
+		public ICollection ReadAttrCerts(
+			byte[] input)
+		{
+			return ReadAttrCerts(new MemoryStream(input, false));
+		}
+
+		/**
+		 * Generates a certificate object and initializes it with the data
+		 * read from the input stream inStream.
+		 */
+		public IX509AttributeCertificate ReadAttrCert(
+			Stream inStream)
+		{
+			if (inStream == null)
+				throw new ArgumentNullException("inStream");
+			if (!inStream.CanRead)
+				throw new ArgumentException("inStream must be read-able", "inStream");
+
+			if (currentStream == null)
+			{
+				currentStream = inStream;
+				sData = null;
+				sDataObjectCount = 0;
+			}
+			else if (currentStream != inStream) // reset if input stream has changed
+			{
+				currentStream = inStream;
+				sData = null;
+				sDataObjectCount = 0;
+			}
+
+			try
+			{
+				if (sData != null)
+				{
+					if (sDataObjectCount != sData.Count)
+					{
+						return GetCertificate();
+					}
+
+					sData = null;
+					sDataObjectCount = 0;
+					return null;
+				}
+
+				PushbackStream pis = new PushbackStream(inStream);
+				int tag = pis.ReadByte();
+
+				if (tag < 0)
+					return null;
+
+				pis.Unread(tag);
+
+				if (tag != 0x30)  // assume ascii PEM encoded.
+				{
+					return ReadPemCertificate(pis);
+				}
+
+				return ReadDerCertificate(new Asn1InputStream(pis));
+			}
+			catch (Exception e)
+			{
+				throw new CertificateException(e.ToString());
+			}
+		}
+
+		/**
+		 * Returns a (possibly empty) collection view of the certificates
+		 * read from the given input stream inStream.
+		 */
+		public ICollection ReadAttrCerts(
+			Stream inStream)
+		{
+			IX509AttributeCertificate attrCert;
+            IList attrCerts = Platform.CreateArrayList();
+
+			while ((attrCert = ReadAttrCert(inStream)) != null)
+			{
+				attrCerts.Add(attrCert);
+			}
+
+			return attrCerts;
+		}
+	}
+}
\ No newline at end of file
diff --git a/Crypto/src/x509/X509Attribute.cs b/Crypto/src/x509/X509Attribute.cs
new file mode 100644
index 000000000..248d66cc4
--- /dev/null
+++ b/Crypto/src/x509/X509Attribute.cs
@@ -0,0 +1,76 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.X509
+{
+	/**
+	 * Class for carrying the values in an X.509 Attribute.
+	 */
+	public class X509Attribute
+		: Asn1Encodable
+	{
+		private readonly AttributeX509 attr;
+
+		/**
+		 * @param at an object representing an attribute.
+		 */
+		internal X509Attribute(
+			Asn1Encodable at)
+		{
+			this.attr = AttributeX509.GetInstance(at);
+		}
+
+		/**
+		 * Create an X.509 Attribute with the type given by the passed in oid and
+		 * the value represented by an ASN.1 Set containing value.
+		 *
+		 * @param oid type of the attribute
+		 * @param value value object to go into the atribute's value set.
+		 */
+		public X509Attribute(
+			string			oid,
+			Asn1Encodable	value)
+		{
+			this.attr = new AttributeX509(new DerObjectIdentifier(oid), new DerSet(value));
+		}
+
+		/**
+		 * Create an X.59 Attribute with the type given by the passed in oid and the
+		 * value represented by an ASN.1 Set containing the objects in value.
+		 *
+		 * @param oid type of the attribute
+		 * @param value vector of values to go in the attribute's value set.
+		 */
+		public X509Attribute(
+			string              oid,
+			Asn1EncodableVector value)
+		{
+			this.attr = new AttributeX509(new DerObjectIdentifier(oid), new DerSet(value));
+		}
+
+		public string Oid
+		{
+			get { return attr.AttrType.Id; }
+		}
+
+		public Asn1Encodable[] GetValues()
+		{
+			Asn1Set s = attr.AttrValues;
+			Asn1Encodable[] values = new Asn1Encodable[s.Count];
+
+			for (int i = 0; i != s.Count; i++)
+			{
+				values[i] = (Asn1Encodable)s[i];
+			}
+
+			return values;
+		}
+
+		public override Asn1Object ToAsn1Object()
+		{
+			return attr.ToAsn1Object();
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509CertPairParser.cs b/Crypto/src/x509/X509CertPairParser.cs
new file mode 100644
index 000000000..82612599b
--- /dev/null
+++ b/Crypto/src/x509/X509CertPairParser.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.X509
+{
+	public class X509CertPairParser
+	{
+		private Stream currentStream;
+
+		private X509CertificatePair ReadDerCrossCertificatePair(
+			Stream inStream)
+		{
+			Asn1InputStream dIn = new Asn1InputStream(inStream);//, ProviderUtil.getReadLimit(in));
+			Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject();
+			CertificatePair pair = CertificatePair.GetInstance(seq);
+			return new X509CertificatePair(pair);
+		}
+
+		/// <summary>
+		/// Create loading data from byte array.
+		/// </summary>
+		/// <param name="input"></param>
+		public X509CertificatePair ReadCertPair(
+			byte[] input)
+		{
+			return ReadCertPair(new MemoryStream(input, false));
+		}
+
+		/// <summary>
+		/// Create loading data from byte array.
+		/// </summary>
+		/// <param name="input"></param>
+		public ICollection ReadCertPairs(
+			byte[] input)
+		{
+			return ReadCertPairs(new MemoryStream(input, false));
+		}
+
+		public X509CertificatePair ReadCertPair(
+			Stream inStream)
+		{
+			if (inStream == null)
+				throw new ArgumentNullException("inStream");
+			if (!inStream.CanRead)
+				throw new ArgumentException("inStream must be read-able", "inStream");
+
+			if (currentStream == null)
+			{
+				currentStream = inStream;
+			}
+			else if (currentStream != inStream) // reset if input stream has changed
+			{
+				currentStream = inStream;
+			}
+
+			try
+			{
+				PushbackStream pis = new PushbackStream(inStream);
+				int tag = pis.ReadByte();
+
+				if (tag < 0)
+					return null;
+
+				pis.Unread(tag);
+
+				return ReadDerCrossCertificatePair(pis);
+			}
+			catch (Exception e)
+			{
+				throw new CertificateException(e.ToString());
+			}
+		}
+
+		public ICollection ReadCertPairs(
+			Stream inStream)
+		{
+			X509CertificatePair certPair;
+			IList certPairs = Platform.CreateArrayList();
+
+			while ((certPair = ReadCertPair(inStream)) != null)
+			{
+				certPairs.Add(certPair);
+			}
+
+			return certPairs;
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509Certificate.cs b/Crypto/src/x509/X509Certificate.cs
new file mode 100644
index 000000000..f156f3147
--- /dev/null
+++ b/Crypto/src/x509/X509Certificate.cs
@@ -0,0 +1,595 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Misc;
+using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.X509.Extension;
+
+namespace Org.BouncyCastle.X509
+{
+    /// <summary>
+    /// An Object representing an X509 Certificate.
+    /// Has static methods for loading Certificates encoded in many forms that return X509Certificate Objects.
+    /// </summary>
+    public class X509Certificate
+		:	X509ExtensionBase
+//		, PKCS12BagAttributeCarrier
+    {
+        private readonly X509CertificateStructure c;
+//        private Hashtable pkcs12Attributes = new Hashtable();
+//        private ArrayList pkcs12Ordering = new ArrayList();
+		private readonly BasicConstraints basicConstraints;
+		private readonly bool[] keyUsage;
+
+		private bool hashValueSet;
+		private int hashValue;
+
+		protected X509Certificate()
+		{
+		}
+
+		public X509Certificate(
+			X509CertificateStructure c)
+		{
+			this.c = c;
+
+			try
+			{
+				Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.19"));
+
+				if (str != null)
+				{
+					basicConstraints = BasicConstraints.GetInstance(
+						X509ExtensionUtilities.FromExtensionValue(str));
+				}
+			}
+			catch (Exception e)
+			{
+				throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
+			}
+
+			try
+			{
+				Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.15"));
+
+				if (str != null)
+				{
+					DerBitString bits = DerBitString.GetInstance(
+						X509ExtensionUtilities.FromExtensionValue(str));
+
+					byte[] bytes = bits.GetBytes();
+					int length = (bytes.Length * 8) - bits.PadBits;
+
+					keyUsage = new bool[(length < 9) ? 9 : length];
+
+					for (int i = 0; i != length; i++)
+					{
+//						keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+						keyUsage[i] = (bytes[i / 8] & (0x80 >> (i % 8))) != 0;
+					}
+				}
+				else
+				{
+					keyUsage = null;
+				}
+			}
+			catch (Exception e)
+			{
+				throw new CertificateParsingException("cannot construct KeyUsage: " + e);
+			}
+		}
+
+//		internal X509Certificate(
+//			Asn1Sequence seq)
+//        {
+//            this.c = X509CertificateStructure.GetInstance(seq);
+//        }
+
+//		/// <summary>
+//        /// Load certificate from byte array.
+//        /// </summary>
+//        /// <param name="encoded">Byte array containing encoded X509Certificate.</param>
+//        public X509Certificate(
+//            byte[] encoded)
+//			: this((Asn1Sequence) new Asn1InputStream(encoded).ReadObject())
+//		{
+//        }
+//
+//        /// <summary>
+//        /// Load certificate from Stream.
+//        /// Must be positioned at start of certificate.
+//        /// </summary>
+//        /// <param name="input"></param>
+//        public X509Certificate(
+//            Stream input)
+//			: this((Asn1Sequence) new Asn1InputStream(input).ReadObject())
+//        {
+//        }
+
+		public virtual X509CertificateStructure CertificateStructure
+		{
+			get { return c; }
+		}
+
+		/// <summary>
+        /// Return true if the current time is within the start and end times nominated on the certificate.
+        /// </summary>
+        /// <returns>true id certificate is valid for the current time.</returns>
+        public virtual bool IsValidNow
+        {
+			get { return IsValid(DateTime.UtcNow); }
+        }
+
+		/// <summary>
+        /// Return true if the nominated time is within the start and end times nominated on the certificate.
+        /// </summary>
+        /// <param name="time">The time to test validity against.</param>
+        /// <returns>True if certificate is valid for nominated time.</returns>
+        public virtual bool IsValid(
+			DateTime time)
+        {
+            return time.CompareTo(NotBefore) >= 0 && time.CompareTo(NotAfter) <= 0;
+        }
+
+		/// <summary>
+		/// Checks if the current date is within certificate's validity period.
+		/// </summary>
+		public virtual void CheckValidity()
+		{
+			this.CheckValidity(DateTime.UtcNow);
+		}
+
+		/// <summary>
+		/// Checks if the given date is within certificate's validity period.
+		/// </summary>
+		/// <exception cref="CertificateExpiredException">if the certificate is expired by given date</exception>
+		/// <exception cref="CertificateNotYetValidException">if the certificate is not yet valid on given date</exception>
+		public virtual void CheckValidity(
+			DateTime time)
+		{
+			if (time.CompareTo(NotAfter) > 0)
+				throw new CertificateExpiredException("certificate expired on " + c.EndDate.GetTime());
+			if (time.CompareTo(NotBefore) < 0)
+				throw new CertificateNotYetValidException("certificate not valid until " + c.StartDate.GetTime());
+		}
+
+		/// <summary>
+        /// Return the certificate's version.
+        /// </summary>
+        /// <returns>An integer whose value Equals the version of the cerficate.</returns>
+        public virtual int Version
+        {
+            get { return c.Version; }
+        }
+
+		/// <summary>
+        /// Return a <see cref="Org.BouncyCastle.Math.BigInteger">BigInteger</see> containing the serial number.
+        /// </summary>
+        /// <returns>The Serial number.</returns>
+        public virtual BigInteger SerialNumber
+        {
+            get { return c.SerialNumber.Value; }
+        }
+
+		/// <summary>
+        /// Get the Issuer Distinguished Name. (Who signed the certificate.)
+        /// </summary>
+        /// <returns>And X509Object containing name and value pairs.</returns>
+//        public IPrincipal IssuerDN
+		public virtual X509Name IssuerDN
+		{
+            get { return c.Issuer; }
+        }
+
+		/// <summary>
+        /// Get the subject of this certificate.
+        /// </summary>
+        /// <returns>An X509Name object containing name and value pairs.</returns>
+//        public IPrincipal SubjectDN
+		public virtual X509Name SubjectDN
+		{
+            get { return c.Subject; }
+        }
+
+		/// <summary>
+		/// The time that this certificate is valid from.
+		/// </summary>
+		/// <returns>A DateTime object representing that time in the local time zone.</returns>
+		public virtual DateTime NotBefore
+		{
+			get { return c.StartDate.ToDateTime(); }
+		}
+
+		/// <summary>
+        /// The time that this certificate is valid up to.
+        /// </summary>
+        /// <returns>A DateTime object representing that time in the local time zone.</returns>
+        public virtual DateTime NotAfter
+        {
+			get { return c.EndDate.ToDateTime(); }
+        }
+
+		/// <summary>
+		/// Return the Der encoded TbsCertificate data.
+		/// This is the certificate component less the signature.
+		/// To Get the whole certificate call the GetEncoded() member.
+		/// </summary>
+		/// <returns>A byte array containing the Der encoded Certificate component.</returns>
+		public virtual byte[] GetTbsCertificate()
+		{
+			return c.TbsCertificate.GetDerEncoded();
+		}
+
+		/// <summary>
+		/// The signature.
+		/// </summary>
+		/// <returns>A byte array containg the signature of the certificate.</returns>
+		public virtual byte[] GetSignature()
+		{
+			return c.Signature.GetBytes();
+		}
+
+		/// <summary>
+		/// A meaningful version of the Signature Algorithm. (EG SHA1WITHRSA)
+		/// </summary>
+		/// <returns>A sting representing the signature algorithm.</returns>
+		public virtual string SigAlgName
+		{
+			get { return SignerUtilities.GetEncodingName(c.SignatureAlgorithm.ObjectID); }
+		}
+
+		/// <summary>
+		/// Get the Signature Algorithms Object ID.
+		/// </summary>
+		/// <returns>A string containg a '.' separated object id.</returns>
+		public virtual string SigAlgOid
+		{
+			get { return c.SignatureAlgorithm.ObjectID.Id; }
+		}
+
+		/// <summary>
+		/// Get the signature algorithms parameters. (EG DSA Parameters)
+		/// </summary>
+		/// <returns>A byte array containing the Der encoded version of the parameters or null if there are none.</returns>
+		public virtual byte[] GetSigAlgParams()
+		{
+			if (c.SignatureAlgorithm.Parameters != null)
+			{
+				return c.SignatureAlgorithm.Parameters.GetDerEncoded();
+			}
+
+			return null;
+		}
+
+		/// <summary>
+		/// Get the issuers UID.
+		/// </summary>
+		/// <returns>A DerBitString.</returns>
+		public virtual DerBitString IssuerUniqueID
+		{
+			get { return c.TbsCertificate.IssuerUniqueID; }
+		}
+
+		/// <summary>
+		/// Get the subjects UID.
+		/// </summary>
+		/// <returns>A DerBitString.</returns>
+		public virtual DerBitString SubjectUniqueID
+		{
+			get { return c.TbsCertificate.SubjectUniqueID; }
+		}
+
+		/// <summary>
+		/// Get a key usage guidlines.
+		/// </summary>
+		public virtual bool[] GetKeyUsage()
+		{
+			return keyUsage == null ? null : (bool[]) keyUsage.Clone();
+		}
+
+		// TODO Replace with something that returns a list of DerObjectIdentifier
+		public virtual IList GetExtendedKeyUsage()
+		{
+			Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.37"));
+
+			if (str == null)
+				return null;
+
+			try
+			{
+				Asn1Sequence seq = Asn1Sequence.GetInstance(
+					X509ExtensionUtilities.FromExtensionValue(str));
+
+                IList list = Platform.CreateArrayList();
+
+				foreach (DerObjectIdentifier oid in seq)
+				{
+					list.Add(oid.Id);
+				}
+
+				return list;
+			}
+			catch (Exception e)
+			{
+				throw new CertificateParsingException("error processing extended key usage extension", e);
+			}
+		}
+
+		public virtual int GetBasicConstraints()
+		{
+			if (basicConstraints != null && basicConstraints.IsCA())
+			{
+				if (basicConstraints.PathLenConstraint == null)
+				{
+					return int.MaxValue;
+				}
+
+				return basicConstraints.PathLenConstraint.IntValue;
+			}
+
+			return -1;
+		}
+
+		public virtual ICollection GetSubjectAlternativeNames()
+		{
+			return GetAlternativeNames("2.5.29.17");
+		}
+
+		public virtual ICollection GetIssuerAlternativeNames()
+		{
+			return GetAlternativeNames("2.5.29.18");
+		}
+
+		protected virtual ICollection GetAlternativeNames(
+			string oid)
+		{
+			Asn1OctetString altNames = GetExtensionValue(new DerObjectIdentifier(oid));
+
+			if (altNames == null)
+				return null;
+
+			Asn1Object asn1Object = X509ExtensionUtilities.FromExtensionValue(altNames);
+
+			GeneralNames gns = GeneralNames.GetInstance(asn1Object);
+
+            IList result = Platform.CreateArrayList();
+			foreach (GeneralName gn in gns.GetNames())
+			{
+                IList entry = Platform.CreateArrayList();
+				entry.Add(gn.TagNo);
+				entry.Add(gn.Name.ToString());
+				result.Add(entry);
+			}
+			return result;
+		}
+
+		protected override X509Extensions GetX509Extensions()
+		{
+			return c.Version == 3
+				?	c.TbsCertificate.Extensions
+				:	null;
+		}
+
+		/// <summary>
+		/// Get the public key of the subject of the certificate.
+		/// </summary>
+		/// <returns>The public key parameters.</returns>
+		public virtual AsymmetricKeyParameter GetPublicKey()
+		{
+			return PublicKeyFactory.CreateKey(c.SubjectPublicKeyInfo);
+		}
+
+		/// <summary>
+		/// Return a Der encoded version of this certificate.
+		/// </summary>
+		/// <returns>A byte array.</returns>
+		public virtual byte[] GetEncoded()
+		{
+			return c.GetDerEncoded();
+		}
+
+		public override bool Equals(
+			object obj)
+		{
+			if (obj == this)
+				return true;
+
+			X509Certificate other = obj as X509Certificate;
+
+			if (other == null)
+				return false;
+
+			return c.Equals(other.c);
+
+			// NB: May prefer this implementation of Equals if more than one certificate implementation in play
+//			return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded());
+		}
+
+		public override int GetHashCode()
+		{
+			lock (this)
+			{
+				if (!hashValueSet)
+				{
+					hashValue = c.GetHashCode();
+					hashValueSet = true;
+				}
+			}
+
+			return hashValue;
+		}
+
+//		public void setBagAttribute(
+//			DERObjectIdentifier oid,
+//			DEREncodable        attribute)
+//		{
+//			pkcs12Attributes.put(oid, attribute);
+//			pkcs12Ordering.addElement(oid);
+//		}
+//
+//		public DEREncodable getBagAttribute(
+//			DERObjectIdentifier oid)
+//		{
+//			return (DEREncodable)pkcs12Attributes.get(oid);
+//		}
+//
+//		public Enumeration getBagAttributeKeys()
+//		{
+//			return pkcs12Ordering.elements();
+//		}
+
+		public override string ToString()
+		{
+			StringBuilder buf = new StringBuilder();
+			string nl = Platform.NewLine;
+
+			buf.Append("  [0]         Version: ").Append(this.Version).Append(nl);
+			buf.Append("         SerialNumber: ").Append(this.SerialNumber).Append(nl);
+			buf.Append("             IssuerDN: ").Append(this.IssuerDN).Append(nl);
+			buf.Append("           Start Date: ").Append(this.NotBefore).Append(nl);
+			buf.Append("           Final Date: ").Append(this.NotAfter).Append(nl);
+			buf.Append("            SubjectDN: ").Append(this.SubjectDN).Append(nl);
+			buf.Append("           Public Key: ").Append(this.GetPublicKey()).Append(nl);
+			buf.Append("  Signature Algorithm: ").Append(this.SigAlgName).Append(nl);
+
+			byte[] sig = this.GetSignature();
+			buf.Append("            Signature: ").Append(Hex.ToHexString(sig, 0, 20)).Append(nl);
+
+			for (int i = 20; i < sig.Length; i += 20)
+			{
+				int len = System.Math.Min(20, sig.Length - i);
+				buf.Append("                       ").Append(Hex.ToHexString(sig, i, len)).Append(nl);
+			}
+
+			X509Extensions extensions = c.TbsCertificate.Extensions;
+
+			if (extensions != null)
+			{
+				IEnumerator e = extensions.ExtensionOids.GetEnumerator();
+
+				if (e.MoveNext())
+				{
+					buf.Append("       Extensions: \n");
+				}
+
+				do
+				{
+					DerObjectIdentifier oid = (DerObjectIdentifier)e.Current;
+					X509Extension ext = extensions.GetExtension(oid);
+
+					if (ext.Value != null)
+					{
+						byte[] octs = ext.Value.GetOctets();
+						Asn1Object obj = Asn1Object.FromByteArray(octs);
+						buf.Append("                       critical(").Append(ext.IsCritical).Append(") ");
+						try
+						{
+							if (oid.Equals(X509Extensions.BasicConstraints))
+							{
+								buf.Append(BasicConstraints.GetInstance(obj));
+							}
+							else if (oid.Equals(X509Extensions.KeyUsage))
+							{
+								buf.Append(KeyUsage.GetInstance(obj));
+							}
+							else if (oid.Equals(MiscObjectIdentifiers.NetscapeCertType))
+							{
+								buf.Append(new NetscapeCertType((DerBitString) obj));
+							}
+							else if (oid.Equals(MiscObjectIdentifiers.NetscapeRevocationUrl))
+							{
+								buf.Append(new NetscapeRevocationUrl((DerIA5String) obj));
+							}
+							else if (oid.Equals(MiscObjectIdentifiers.VerisignCzagExtension))
+							{
+								buf.Append(new VerisignCzagExtension((DerIA5String) obj));
+							}
+							else
+							{
+								buf.Append(oid.Id);
+								buf.Append(" value = ").Append(Asn1Dump.DumpAsString(obj));
+								//buf.Append(" value = ").Append("*****").Append(nl);
+							}
+						}
+						catch (Exception)
+						{
+							buf.Append(oid.Id);
+							//buf.Append(" value = ").Append(new string(Hex.encode(ext.getValue().getOctets()))).Append(nl);
+							buf.Append(" value = ").Append("*****");
+						}
+					}
+
+					buf.Append(nl);
+				}
+				while (e.MoveNext());
+			}
+
+			return buf.ToString();
+		}
+
+		/// <summary>
+		/// Verify the certificate's signature using the nominated public key.
+		/// </summary>
+		/// <param name="key">An appropriate public key parameter object, RsaPublicKeyParameters, DsaPublicKeyParameters or ECDsaPublicKeyParameters</param>
+		/// <returns>True if the signature is valid.</returns>
+		/// <exception cref="Exception">If key submitted is not of the above nominated types.</exception>
+		public virtual void Verify(
+			AsymmetricKeyParameter key)
+		{
+			string sigName = X509SignatureUtilities.GetSignatureName(c.SignatureAlgorithm);
+			ISigner signature = SignerUtilities.GetSigner(sigName);
+
+			CheckSignature(key, signature);
+		}
+
+		protected virtual void CheckSignature(
+			AsymmetricKeyParameter	publicKey,
+			ISigner					signature)
+		{
+			if (!IsAlgIDEqual(c.SignatureAlgorithm, c.TbsCertificate.Signature))
+				throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
+
+			Asn1Encodable parameters = c.SignatureAlgorithm.Parameters;
+
+			X509SignatureUtilities.SetSignatureParameters(signature, parameters);
+
+			signature.Init(false, publicKey);
+
+			byte[] b = this.GetTbsCertificate();
+			signature.BlockUpdate(b, 0, b.Length);
+
+			byte[] sig = this.GetSignature();
+			if (!signature.VerifySignature(sig))
+			{
+				throw new InvalidKeyException("Public key presented not for certificate signature");
+			}
+		}
+
+		private static bool IsAlgIDEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2)
+		{
+			if (!id1.ObjectID.Equals(id2.ObjectID))
+				return false;
+
+			Asn1Encodable p1 = id1.Parameters;
+			Asn1Encodable p2 = id2.Parameters;
+
+			if ((p1 == null) == (p2 == null))
+				return Platform.Equals(p1, p2);
+
+			// Exactly one of p1, p2 is null at this point
+			return p1 == null
+				?	p2.ToAsn1Object() is Asn1Null
+				:	p1.ToAsn1Object() is Asn1Null;
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509CertificatePair.cs b/Crypto/src/x509/X509CertificatePair.cs
new file mode 100644
index 000000000..fbeba4dc6
--- /dev/null
+++ b/Crypto/src/x509/X509CertificatePair.cs
@@ -0,0 +1,123 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.X509
+{
+	/// <remarks>
+	/// This class contains a cross certificate pair. Cross certificates pairs may
+	/// contain two cross signed certificates from two CAs. A certificate from the
+	/// other CA to this CA is contained in the forward certificate, the certificate
+	/// from this CA to the other CA is contained in the reverse certificate.
+	/// </remarks>
+	public class X509CertificatePair
+	{
+		private readonly X509Certificate forward;
+		private readonly X509Certificate reverse;
+
+		/// <summary>Constructor</summary>
+		/// <param name="forward">Certificate from the other CA to this CA.</param>
+		/// <param name="reverse">Certificate from this CA to the other CA.</param>
+		public X509CertificatePair(
+			X509Certificate	forward,
+			X509Certificate	reverse)
+		{
+			this.forward = forward;
+			this.reverse = reverse;
+		}
+
+		/// <summary>Constructor from a ASN.1 CertificatePair structure.</summary>
+		/// <param name="pair">The <c>CertificatePair</c> ASN.1 object.</param>
+		public X509CertificatePair(
+			CertificatePair pair)
+		{
+			if (pair.Forward != null)
+			{
+				this.forward = new X509Certificate(pair.Forward);
+			}
+			if (pair.Reverse != null)
+			{
+				this.reverse = new X509Certificate(pair.Reverse);
+			}
+		}
+
+		public byte[] GetEncoded()
+		{
+			try
+			{
+				X509CertificateStructure f = null, r = null;
+
+				if (forward != null)
+				{
+					f = X509CertificateStructure.GetInstance(
+						Asn1Object.FromByteArray(forward.GetEncoded()));
+
+					if (f == null)
+						throw new CertificateEncodingException("unable to get encoding for forward");
+				}
+
+				if (reverse != null)
+				{
+					r = X509CertificateStructure.GetInstance(
+						Asn1Object.FromByteArray(reverse.GetEncoded()));
+
+					if (r == null)
+						throw new CertificateEncodingException("unable to get encoding for reverse");
+				}
+
+				return new CertificatePair(f, r).GetDerEncoded();
+			}
+			catch (Exception e)
+			{
+				// TODO
+//				throw new ExtCertificateEncodingException(e.toString(), e);
+				throw new CertificateEncodingException(e.Message, e);
+			}
+		}
+
+		/// <summary>Returns the certificate from the other CA to this CA.</summary>
+		public X509Certificate Forward
+		{
+			get { return forward; }
+		}
+
+		/// <summary>Returns the certificate from this CA to the other CA.</summary>
+		public X509Certificate Reverse
+		{
+			get { return reverse; }
+		}
+
+		public override bool Equals(
+			object obj)
+		{
+			if (obj == this)
+				return true;
+
+			X509CertificatePair other = obj as X509CertificatePair;
+
+			if (other == null)
+				return false;
+
+			return Platform.Equals(this.forward, other.forward)
+				&& Platform.Equals(this.reverse, other.reverse);
+		}
+
+		public override int GetHashCode()
+		{
+			int hash = -1;
+			if (forward != null)
+			{
+				hash ^= forward.GetHashCode();
+			}
+			if (reverse != null)
+			{
+				hash *= 17;
+				hash ^= reverse.GetHashCode();
+			}
+			return hash;
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509CertificateParser.cs b/Crypto/src/x509/X509CertificateParser.cs
new file mode 100644
index 000000000..8f0e7406c
--- /dev/null
+++ b/Crypto/src/x509/X509CertificateParser.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.X509
+{
+	/**
+	 * class for dealing with X509 certificates.
+	 * <p>
+	 * At the moment this will deal with "-----BEGIN CERTIFICATE-----" to "-----END CERTIFICATE-----"
+	 * base 64 encoded certs, as well as the BER binaries of certificates and some classes of PKCS#7
+	 * objects.</p>
+	 */
+	public class X509CertificateParser
+	{
+		private static readonly PemParser PemCertParser = new PemParser("CERTIFICATE");
+
+		private Asn1Set	sData;
+		private int		sDataObjectCount;
+		private Stream	currentStream;
+
+		private X509Certificate ReadDerCertificate(
+			Asn1InputStream dIn)
+		{
+			Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject();
+
+			if (seq.Count > 1 && seq[0] is DerObjectIdentifier)
+			{
+				if (seq[0].Equals(PkcsObjectIdentifiers.SignedData))
+				{
+					sData = SignedData.GetInstance(
+						Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Certificates;
+
+					return GetCertificate();
+				}
+			}
+
+			return CreateX509Certificate(X509CertificateStructure.GetInstance(seq));
+		}
+
+		private X509Certificate GetCertificate()
+		{
+			if (sData != null)
+			{
+				while (sDataObjectCount < sData.Count)
+				{
+					object obj = sData[sDataObjectCount++];
+
+					if (obj is Asn1Sequence)
+					{
+						return CreateX509Certificate(
+							X509CertificateStructure.GetInstance(obj));
+					}
+				}
+			}
+
+			return null;
+		}
+
+		private X509Certificate ReadPemCertificate(
+			Stream inStream)
+		{
+			Asn1Sequence seq = PemCertParser.ReadPemObject(inStream);
+
+			return seq == null
+				?	null
+				:	CreateX509Certificate(X509CertificateStructure.GetInstance(seq));
+		}
+
+		protected virtual X509Certificate CreateX509Certificate(
+			X509CertificateStructure c)
+		{
+			return new X509Certificate(c);
+		}
+
+		/// <summary>
+		/// Create loading data from byte array.
+		/// </summary>
+		/// <param name="input"></param>
+		public X509Certificate ReadCertificate(
+			byte[] input)
+		{
+			return ReadCertificate(new MemoryStream(input, false));
+		}
+
+		/// <summary>
+		/// Create loading data from byte array.
+		/// </summary>
+		/// <param name="input"></param>
+		public ICollection ReadCertificates(
+			byte[] input)
+		{
+			return ReadCertificates(new MemoryStream(input, false));
+		}
+
+		/**
+		 * Generates a certificate object and initializes it with the data
+		 * read from the input stream inStream.
+		 */
+		public X509Certificate ReadCertificate(
+			Stream inStream)
+		{
+			if (inStream == null)
+				throw new ArgumentNullException("inStream");
+			if (!inStream.CanRead)
+				throw new ArgumentException("inStream must be read-able", "inStream");
+
+			if (currentStream == null)
+			{
+				currentStream = inStream;
+				sData = null;
+				sDataObjectCount = 0;
+			}
+			else if (currentStream != inStream) // reset if input stream has changed
+			{
+				currentStream = inStream;
+				sData = null;
+				sDataObjectCount = 0;
+			}
+
+			try
+			{
+				if (sData != null)
+				{
+					if (sDataObjectCount != sData.Count)
+					{
+						return GetCertificate();
+					}
+
+					sData = null;
+					sDataObjectCount = 0;
+					return null;
+				}
+
+				PushbackStream pis = new PushbackStream(inStream);
+				int tag = pis.ReadByte();
+
+				if (tag < 0)
+					return null;
+
+				pis.Unread(tag);
+
+				if (tag != 0x30)  // assume ascii PEM encoded.
+				{
+					return ReadPemCertificate(pis);
+				}
+
+				return ReadDerCertificate(new Asn1InputStream(pis));
+			}
+			catch (Exception e)
+			{
+				throw new CertificateException("Failed to read certificate", e);
+			}
+		}
+
+		/**
+		 * Returns a (possibly empty) collection view of the certificates
+		 * read from the given input stream inStream.
+		 */
+		public ICollection ReadCertificates(
+			Stream inStream)
+		{
+			X509Certificate cert;
+            IList certs = Platform.CreateArrayList();
+
+			while ((cert = ReadCertificate(inStream)) != null)
+			{
+				certs.Add(cert);
+			}
+
+			return certs;
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509Crl.cs b/Crypto/src/x509/X509Crl.cs
new file mode 100644
index 000000000..7d0e7aa72
--- /dev/null
+++ b/Crypto/src/x509/X509Crl.cs
@@ -0,0 +1,403 @@
+using System;
+using System.Collections;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.X509.Extension;
+
+namespace Org.BouncyCastle.X509
+{
+	/**
+	 * The following extensions are listed in RFC 2459 as relevant to CRLs
+	 *
+	 * Authority Key Identifier
+	 * Issuer Alternative Name
+	 * CRL Number
+	 * Delta CRL Indicator (critical)
+	 * Issuing Distribution Point (critical)
+	 */
+	public class X509Crl
+		: X509ExtensionBase
+		// TODO Add interface Crl?
+	{
+		private readonly CertificateList c;
+		private readonly string sigAlgName;
+		private readonly byte[] sigAlgParams;
+		private readonly bool isIndirect;
+
+		public X509Crl(
+			CertificateList c)
+		{
+			this.c = c;
+
+			try
+			{
+				this.sigAlgName = X509SignatureUtilities.GetSignatureName(c.SignatureAlgorithm);
+
+				if (c.SignatureAlgorithm.Parameters != null)
+				{
+					this.sigAlgParams = ((Asn1Encodable)c.SignatureAlgorithm.Parameters).GetDerEncoded();
+				}
+				else
+				{
+					this.sigAlgParams = null;
+				}
+
+				this.isIndirect = IsIndirectCrl;
+			}
+			catch (Exception e)
+			{
+				throw new CrlException("CRL contents invalid: " + e);
+			}
+		}
+
+		protected override X509Extensions GetX509Extensions()
+		{
+			return Version == 2
+				?	c.TbsCertList.Extensions
+				:	null;
+		}
+
+		public virtual byte[] GetEncoded()
+		{
+			try
+			{
+				return c.GetDerEncoded();
+			}
+			catch (Exception e)
+			{
+				throw new CrlException(e.ToString());
+			}
+		}
+
+		public virtual void Verify(
+			AsymmetricKeyParameter publicKey)
+		{
+			if (!c.SignatureAlgorithm.Equals(c.TbsCertList.Signature))
+			{
+				throw new CrlException("Signature algorithm on CertificateList does not match TbsCertList.");
+			}
+
+			ISigner sig = SignerUtilities.GetSigner(SigAlgName);
+			sig.Init(false, publicKey);
+
+			byte[] encoded = this.GetTbsCertList();
+			sig.BlockUpdate(encoded, 0, encoded.Length);
+
+			if (!sig.VerifySignature(this.GetSignature()))
+			{
+				throw new SignatureException("CRL does not verify with supplied public key.");
+			}
+		}
+
+		public virtual int Version
+		{
+			get { return c.Version; }
+		}
+
+		public virtual X509Name IssuerDN
+		{
+			get { return c.Issuer; }
+		}
+
+		public virtual DateTime ThisUpdate
+		{
+			get { return c.ThisUpdate.ToDateTime(); }
+		}
+
+		public virtual DateTimeObject NextUpdate
+		{
+			get
+			{
+				return c.NextUpdate == null
+					?	null
+					:	new DateTimeObject(c.NextUpdate.ToDateTime());
+			}
+		}
+
+		private ISet LoadCrlEntries()
+		{
+			ISet entrySet = new HashSet();
+			IEnumerable certs = c.GetRevokedCertificateEnumeration();
+
+			X509Name previousCertificateIssuer = IssuerDN;
+			foreach (CrlEntry entry in certs)
+			{
+				X509CrlEntry crlEntry = new X509CrlEntry(entry, isIndirect, previousCertificateIssuer);
+				entrySet.Add(crlEntry);
+				previousCertificateIssuer = crlEntry.GetCertificateIssuer();
+			}
+
+			return entrySet;
+		}
+
+		public virtual X509CrlEntry GetRevokedCertificate(
+			BigInteger serialNumber)
+		{
+			IEnumerable certs = c.GetRevokedCertificateEnumeration();
+
+			X509Name previousCertificateIssuer = IssuerDN;
+			foreach (CrlEntry entry in certs)
+			{
+				X509CrlEntry crlEntry = new X509CrlEntry(entry, isIndirect, previousCertificateIssuer);
+
+				if (serialNumber.Equals(entry.UserCertificate.Value))
+				{
+					return crlEntry;
+				}
+
+				previousCertificateIssuer = crlEntry.GetCertificateIssuer();
+			}
+
+			return null;
+		}
+
+		public virtual ISet GetRevokedCertificates()
+		{
+			ISet entrySet = LoadCrlEntries();
+
+			if (entrySet.Count > 0)
+			{
+				return entrySet; // TODO? Collections.unmodifiableSet(entrySet);
+			}
+
+			return null;
+		}
+
+		public virtual byte[] GetTbsCertList()
+		{
+			try
+			{
+				return c.TbsCertList.GetDerEncoded();
+			}
+			catch (Exception e)
+			{
+				throw new CrlException(e.ToString());
+			}
+		}
+
+		public virtual byte[] GetSignature()
+		{
+			return c.Signature.GetBytes();
+		}
+
+		public virtual string SigAlgName
+		{
+			get { return sigAlgName; }
+		}
+
+		public virtual string SigAlgOid
+		{
+			get { return c.SignatureAlgorithm.ObjectID.Id; }
+		}
+
+		public virtual byte[] GetSigAlgParams()
+		{
+			return Arrays.Clone(sigAlgParams);
+		}
+
+		public override bool Equals(
+			object obj)
+		{
+			if (obj == this)
+				return true;
+
+			X509Crl other = obj as X509Crl;
+
+			if (other == null)
+				return false;
+
+			return c.Equals(other.c);
+
+			// NB: May prefer this implementation of Equals if more than one certificate implementation in play
+			//return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded());
+		}
+
+		public override int GetHashCode()
+		{
+			return c.GetHashCode();
+		}
+
+		/**
+		 * Returns a string representation of this CRL.
+		 *
+		 * @return a string representation of this CRL.
+		 */
+		public override string ToString()
+		{
+			StringBuilder buf = new StringBuilder();
+			string nl = Platform.NewLine;
+
+			buf.Append("              Version: ").Append(this.Version).Append(nl);
+			buf.Append("             IssuerDN: ").Append(this.IssuerDN).Append(nl);
+			buf.Append("          This update: ").Append(this.ThisUpdate).Append(nl);
+			buf.Append("          Next update: ").Append(this.NextUpdate).Append(nl);
+			buf.Append("  Signature Algorithm: ").Append(this.SigAlgName).Append(nl);
+
+			byte[] sig = this.GetSignature();
+
+			buf.Append("            Signature: ");
+			buf.Append(Hex.ToHexString(sig, 0, 20)).Append(nl);
+
+			for (int i = 20; i < sig.Length; i += 20)
+			{
+				int count = System.Math.Min(20, sig.Length - i);
+				buf.Append("                       ");
+				buf.Append(Hex.ToHexString(sig, i, count)).Append(nl);
+			}
+
+			X509Extensions extensions = c.TbsCertList.Extensions;
+
+			if (extensions != null)
+			{
+				IEnumerator e = extensions.ExtensionOids.GetEnumerator();
+
+				if (e.MoveNext())
+				{
+					buf.Append("           Extensions: ").Append(nl);
+				}
+
+				do
+				{
+					DerObjectIdentifier oid = (DerObjectIdentifier) e.Current;
+					X509Extension ext = extensions.GetExtension(oid);
+
+					if (ext.Value != null)
+					{
+						Asn1Object asn1Value = X509ExtensionUtilities.FromExtensionValue(ext.Value);
+
+						buf.Append("                       critical(").Append(ext.IsCritical).Append(") ");
+						try
+						{
+							if (oid.Equals(X509Extensions.CrlNumber))
+							{
+								buf.Append(new CrlNumber(DerInteger.GetInstance(asn1Value).PositiveValue)).Append(nl);
+							}
+							else if (oid.Equals(X509Extensions.DeltaCrlIndicator))
+							{
+								buf.Append(
+									"Base CRL: "
+									+ new CrlNumber(DerInteger.GetInstance(
+									asn1Value).PositiveValue))
+									.Append(nl);
+							}
+							else if (oid.Equals(X509Extensions.IssuingDistributionPoint))
+							{
+								buf.Append(IssuingDistributionPoint.GetInstance((Asn1Sequence) asn1Value)).Append(nl);
+							}
+							else if (oid.Equals(X509Extensions.CrlDistributionPoints))
+							{
+								buf.Append(CrlDistPoint.GetInstance((Asn1Sequence) asn1Value)).Append(nl);
+							}
+							else if (oid.Equals(X509Extensions.FreshestCrl))
+							{
+								buf.Append(CrlDistPoint.GetInstance((Asn1Sequence) asn1Value)).Append(nl);
+							}
+							else
+							{
+								buf.Append(oid.Id);
+								buf.Append(" value = ").Append(
+									Asn1Dump.DumpAsString(asn1Value))
+									.Append(nl);
+							}
+						}
+						catch (Exception)
+						{
+							buf.Append(oid.Id);
+							buf.Append(" value = ").Append("*****").Append(nl);
+						}
+					}
+					else
+					{
+						buf.Append(nl);
+					}
+				}
+				while (e.MoveNext());
+			}
+
+			ISet certSet = GetRevokedCertificates();
+			if (certSet != null)
+			{
+				foreach (X509CrlEntry entry in certSet)
+				{
+					buf.Append(entry);
+					buf.Append(nl);
+				}
+			}
+
+			return buf.ToString();
+		}
+
+		/**
+		 * Checks whether the given certificate is on this CRL.
+		 *
+		 * @param cert the certificate to check for.
+		 * @return true if the given certificate is on this CRL,
+		 * false otherwise.
+		 */
+//		public bool IsRevoked(
+//			Certificate cert)
+//		{
+//			if (!cert.getType().Equals("X.509"))
+//			{
+//				throw new RuntimeException("X.509 CRL used with non X.509 Cert");
+//			}
+		public virtual bool IsRevoked(
+			X509Certificate cert)
+		{
+			CrlEntry[] certs = c.GetRevokedCertificates();
+
+			if (certs != null)
+			{
+//				BigInteger serial = ((X509Certificate)cert).SerialNumber;
+				BigInteger serial = cert.SerialNumber;
+
+				for (int i = 0; i < certs.Length; i++)
+				{
+					if (certs[i].UserCertificate.Value.Equals(serial))
+					{
+						return true;
+					}
+				}
+			}
+
+			return false;
+		}
+
+		protected virtual bool IsIndirectCrl
+		{
+			get
+			{
+				Asn1OctetString idp = GetExtensionValue(X509Extensions.IssuingDistributionPoint);
+				bool isIndirect = false;
+
+				try
+				{
+					if (idp != null)
+					{
+						isIndirect = IssuingDistributionPoint.GetInstance(
+							X509ExtensionUtilities.FromExtensionValue(idp)).IsIndirectCrl;
+					}
+				}
+				catch (Exception e)
+				{
+					// TODO
+//					throw new ExtCrlException("Exception reading IssuingDistributionPoint", e);
+					throw new CrlException("Exception reading IssuingDistributionPoint" + e);
+				}
+
+				return isIndirect;
+			}
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509CrlEntry.cs b/Crypto/src/x509/X509CrlEntry.cs
new file mode 100644
index 000000000..caca29470
--- /dev/null
+++ b/Crypto/src/x509/X509CrlEntry.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Utilities;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509.Extension;
+
+namespace Org.BouncyCastle.X509
+{
+	/**
+	 * The following extensions are listed in RFC 2459 as relevant to CRL Entries
+	 *
+	 * ReasonCode Hode Instruction Code Invalidity Date Certificate Issuer
+	 * (critical)
+	 */
+	public class X509CrlEntry
+		: X509ExtensionBase
+	{
+		private CrlEntry	c;
+		private bool		isIndirect;
+		private X509Name	previousCertificateIssuer;
+		private X509Name	certificateIssuer;
+
+		public X509CrlEntry(
+			CrlEntry c)
+		{
+			this.c = c;
+			this.certificateIssuer = loadCertificateIssuer();
+		}
+
+		/**
+		* Constructor for CRLEntries of indirect CRLs. If <code>isIndirect</code>
+		* is <code>false</code> {@link #getCertificateIssuer()} will always
+		* return <code>null</code>, <code>previousCertificateIssuer</code> is
+		* ignored. If this <code>isIndirect</code> is specified and this CrlEntry
+		* has no certificate issuer CRL entry extension
+		* <code>previousCertificateIssuer</code> is returned by
+		* {@link #getCertificateIssuer()}.
+		*
+		* @param c
+		*            TbsCertificateList.CrlEntry object.
+		* @param isIndirect
+		*            <code>true</code> if the corresponding CRL is a indirect
+		*            CRL.
+		* @param previousCertificateIssuer
+		*            Certificate issuer of the previous CrlEntry.
+		*/
+		public X509CrlEntry(
+			CrlEntry		c,
+			bool			isIndirect,
+			X509Name		previousCertificateIssuer)
+		{
+			this.c = c;
+			this.isIndirect = isIndirect;
+			this.previousCertificateIssuer = previousCertificateIssuer;
+			this.certificateIssuer = loadCertificateIssuer();
+		}
+
+		private X509Name loadCertificateIssuer()
+		{
+			if (!isIndirect)
+			{
+				return null;
+			}
+
+			Asn1OctetString ext = GetExtensionValue(X509Extensions.CertificateIssuer);
+			if (ext == null)
+			{
+				return previousCertificateIssuer;
+			}
+
+			try
+			{
+				GeneralName[] names = GeneralNames.GetInstance(
+					X509ExtensionUtilities.FromExtensionValue(ext)).GetNames();
+
+				for (int i = 0; i < names.Length; i++)
+				{
+					if (names[i].TagNo == GeneralName.DirectoryName)
+					{
+						return X509Name.GetInstance(names[i].Name);
+					}
+				}
+			}
+			catch (Exception)
+			{
+			}
+
+			return null;
+		}
+
+		public X509Name GetCertificateIssuer()
+		{
+			return certificateIssuer;
+		}
+
+		protected override X509Extensions GetX509Extensions()
+		{
+			return c.Extensions;
+		}
+
+		public byte[] GetEncoded()
+		{
+			try
+			{
+				return c.GetDerEncoded();
+			}
+			catch (Exception e)
+			{
+				throw new CrlException(e.ToString());
+			}
+		}
+
+		public BigInteger SerialNumber
+		{
+			get { return c.UserCertificate.Value; }
+		}
+
+		public DateTime RevocationDate
+		{
+			get { return c.RevocationDate.ToDateTime(); }
+		}
+
+		public bool HasExtensions
+		{
+			get { return c.Extensions != null; }
+		}
+
+		public override string ToString()
+		{
+			StringBuilder buf = new StringBuilder();
+			string nl = Platform.NewLine;
+
+			buf.Append("        userCertificate: ").Append(this.SerialNumber).Append(nl);
+			buf.Append("         revocationDate: ").Append(this.RevocationDate).Append(nl);
+			buf.Append("      certificateIssuer: ").Append(this.GetCertificateIssuer()).Append(nl);
+
+			X509Extensions extensions = c.Extensions;
+
+			if (extensions != null)
+			{
+				IEnumerator e = extensions.ExtensionOids.GetEnumerator();
+				if (e.MoveNext())
+				{
+					buf.Append("   crlEntryExtensions:").Append(nl);
+
+					do
+					{
+						DerObjectIdentifier oid = (DerObjectIdentifier)e.Current;
+						X509Extension ext = extensions.GetExtension(oid);
+
+						if (ext.Value != null)
+						{
+							Asn1Object obj = Asn1Object.FromByteArray(ext.Value.GetOctets());
+
+							buf.Append("                       critical(")
+								.Append(ext.IsCritical)
+								.Append(") ");
+							try
+							{
+								if (oid.Equals(X509Extensions.ReasonCode))
+								{
+									buf.Append(new CrlReason(DerEnumerated.GetInstance(obj)));
+								}
+								else if (oid.Equals(X509Extensions.CertificateIssuer))
+								{
+									buf.Append("Certificate issuer: ").Append(
+										GeneralNames.GetInstance((Asn1Sequence)obj));
+								}
+								else 
+								{
+									buf.Append(oid.Id);
+									buf.Append(" value = ").Append(Asn1Dump.DumpAsString(obj));
+								}
+								buf.Append(nl);
+							}
+							catch (Exception)
+							{
+								buf.Append(oid.Id);
+								buf.Append(" value = ").Append("*****").Append(nl);
+							}
+						}
+						else
+						{
+							buf.Append(nl);
+						}
+					}
+					while (e.MoveNext());
+				}
+			}
+
+			return buf.ToString();
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509CrlParser.cs b/Crypto/src/x509/X509CrlParser.cs
new file mode 100644
index 000000000..d830bb9a6
--- /dev/null
+++ b/Crypto/src/x509/X509CrlParser.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.X509
+{
+	public class X509CrlParser
+	{
+		private static readonly PemParser PemCrlParser = new PemParser("CRL");
+
+		private readonly bool lazyAsn1;
+
+		private Asn1Set	sCrlData;
+		private int		sCrlDataObjectCount;
+		private Stream	currentCrlStream;
+
+		public X509CrlParser()
+			: this(false)
+		{
+		}
+
+		public X509CrlParser(
+			bool lazyAsn1)
+		{
+			this.lazyAsn1 = lazyAsn1;
+		}
+
+		private X509Crl ReadPemCrl(
+			Stream inStream)
+		{
+			Asn1Sequence seq = PemCrlParser.ReadPemObject(inStream);
+
+			return seq == null
+				?	null
+				:	CreateX509Crl(CertificateList.GetInstance(seq));
+		}
+
+		private X509Crl ReadDerCrl(
+			Asn1InputStream dIn)
+		{
+			Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject();
+
+			if (seq.Count > 1 && seq[0] is DerObjectIdentifier)
+			{
+				if (seq[0].Equals(PkcsObjectIdentifiers.SignedData))
+				{
+					sCrlData = SignedData.GetInstance(
+						Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Crls;
+
+					return GetCrl();
+				}
+			}
+
+			return CreateX509Crl(CertificateList.GetInstance(seq));
+		}
+
+		private X509Crl GetCrl()
+		{
+			if (sCrlData == null || sCrlDataObjectCount >= sCrlData.Count)
+			{
+				return null;
+			}
+
+			return CreateX509Crl(
+				CertificateList.GetInstance(
+					sCrlData[sCrlDataObjectCount++]));
+		}
+
+		protected virtual X509Crl CreateX509Crl(
+			CertificateList c)
+		{
+			return new X509Crl(c);
+		}
+
+		/// <summary>
+		/// Create loading data from byte array.
+		/// </summary>
+		/// <param name="input"></param>
+		public X509Crl ReadCrl(
+			byte[] input)
+		{
+			return ReadCrl(new MemoryStream(input, false));
+		}
+
+		/// <summary>
+		/// Create loading data from byte array.
+		/// </summary>
+		/// <param name="input"></param>
+		public ICollection ReadCrls(
+			byte[] input)
+		{
+			return ReadCrls(new MemoryStream(input, false));
+		}
+
+		/**
+		 * Generates a certificate revocation list (CRL) object and initializes
+		 * it with the data read from the input stream inStream.
+		 */
+		public X509Crl ReadCrl(
+			Stream inStream)
+		{
+			if (inStream == null)
+				throw new ArgumentNullException("inStream");
+			if (!inStream.CanRead)
+				throw new ArgumentException("inStream must be read-able", "inStream");
+
+			if (currentCrlStream == null)
+			{
+				currentCrlStream = inStream;
+				sCrlData = null;
+				sCrlDataObjectCount = 0;
+			}
+			else if (currentCrlStream != inStream) // reset if input stream has changed
+			{
+				currentCrlStream = inStream;
+				sCrlData = null;
+				sCrlDataObjectCount = 0;
+			}
+
+			try
+			{
+				if (sCrlData != null)
+				{
+					if (sCrlDataObjectCount != sCrlData.Count)
+					{
+						return GetCrl();
+					}
+
+					sCrlData = null;
+					sCrlDataObjectCount = 0;
+					return null;
+				}
+
+				PushbackStream pis = new PushbackStream(inStream);
+				int tag = pis.ReadByte();
+
+				if (tag < 0)
+					return null;
+
+				pis.Unread(tag);
+
+				if (tag != 0x30)	// assume ascii PEM encoded.
+				{
+					return ReadPemCrl(pis);
+				}
+
+				Asn1InputStream asn1 = lazyAsn1
+					?	new LazyAsn1InputStream(pis)
+					:	new Asn1InputStream(pis);
+
+				return ReadDerCrl(asn1);
+			}
+			catch (CrlException e)
+			{
+				throw e;
+			}
+			catch (Exception e)
+			{
+				throw new CrlException(e.ToString());
+			}
+		}
+
+		/**
+		 * Returns a (possibly empty) collection view of the CRLs read from
+		 * the given input stream inStream.
+		 *
+		 * The inStream may contain a sequence of DER-encoded CRLs, or
+		 * a PKCS#7 CRL set.  This is a PKCS#7 SignedData object, with the
+		 * only significant field being crls.  In particular the signature
+		 * and the contents are ignored.
+		 */
+		public ICollection ReadCrls(
+			Stream inStream)
+		{
+			X509Crl crl;
+			IList crls = Platform.CreateArrayList();
+
+			while ((crl = ReadCrl(inStream)) != null)
+			{
+				crls.Add(crl);
+			}
+
+			return crls;
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509ExtensionBase.cs b/Crypto/src/x509/X509ExtensionBase.cs
new file mode 100644
index 000000000..aaf6695c0
--- /dev/null
+++ b/Crypto/src/x509/X509ExtensionBase.cs
@@ -0,0 +1,82 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.X509
+{
+	public abstract class X509ExtensionBase
+		: IX509Extension
+	{
+		protected abstract X509Extensions GetX509Extensions();
+
+		protected virtual ISet GetExtensionOids(
+			bool critical)
+		{
+			X509Extensions extensions = GetX509Extensions();
+			if (extensions != null)
+			{
+				HashSet set = new HashSet();
+				foreach (DerObjectIdentifier oid in extensions.ExtensionOids)
+				{
+					X509Extension ext = extensions.GetExtension(oid);
+					if (ext.IsCritical == critical)
+					{
+						set.Add(oid.Id);
+					}
+				}
+
+				return set;
+			}
+
+			return null;
+		}
+
+		/// <summary>
+		/// Get non critical extensions.
+		/// </summary>
+		/// <returns>A set of non critical extension oids.</returns>
+		public virtual ISet GetNonCriticalExtensionOids()
+		{
+			return GetExtensionOids(false);
+		}
+
+		/// <summary>
+		/// Get any critical extensions.
+		/// </summary>
+		/// <returns>A sorted list of critical entension.</returns>
+		public virtual ISet GetCriticalExtensionOids()
+		{
+			return GetExtensionOids(true);
+		}
+
+		/// <summary>
+		/// Get the value of a given extension.
+		/// </summary>
+		/// <param name="oid">The object ID of the extension. </param>
+		/// <returns>An Asn1OctetString object if that extension is found or null if not.</returns>
+		[Obsolete("Use version taking a DerObjectIdentifier instead")]
+		public Asn1OctetString GetExtensionValue(
+			string oid)
+		{
+			return GetExtensionValue(new DerObjectIdentifier(oid));
+		}
+
+		public virtual Asn1OctetString GetExtensionValue(
+			DerObjectIdentifier oid)
+		{
+			X509Extensions exts = GetX509Extensions();
+			if (exts != null)
+			{
+				X509Extension ext = exts.GetExtension(oid);
+				if (ext != null)
+				{
+					return ext.Value;
+				}
+			}
+
+			return null;
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509KeyUsage.cs b/Crypto/src/x509/X509KeyUsage.cs
new file mode 100644
index 000000000..e0a7b4939
--- /dev/null
+++ b/Crypto/src/x509/X509KeyUsage.cs
@@ -0,0 +1,59 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.X509
+{
+	/**
+	 * A holding class for constructing an X509 Key Usage extension.
+	 *
+	 * <pre>
+	 *    id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
+	 *
+	 *    KeyUsage ::= BIT STRING {
+	 *         digitalSignature        (0),
+	 *         nonRepudiation          (1),
+	 *         keyEncipherment         (2),
+	 *         dataEncipherment        (3),
+	 *         keyAgreement            (4),
+	 *         keyCertSign             (5),
+	 *         cRLSign                 (6),
+	 *         encipherOnly            (7),
+	 *         decipherOnly            (8) }
+	 * </pre>
+	 */
+	public class X509KeyUsage
+		: Asn1Encodable
+	{
+		public const int DigitalSignature = 1 << 7;
+		public const int NonRepudiation   = 1 << 6;
+		public const int KeyEncipherment  = 1 << 5;
+		public const int DataEncipherment = 1 << 4;
+		public const int KeyAgreement     = 1 << 3;
+		public const int KeyCertSign      = 1 << 2;
+		public const int CrlSign          = 1 << 1;
+		public const int EncipherOnly     = 1 << 0;
+		public const int DecipherOnly     = 1 << 15;
+
+		private readonly int usage;
+
+		/**
+		 * Basic constructor.
+		 *
+		 * @param usage - the bitwise OR of the Key Usage flags giving the
+		 * allowed uses for the key.
+		 * e.g. (X509KeyUsage.keyEncipherment | X509KeyUsage.dataEncipherment)
+		 */
+		public X509KeyUsage(
+			int usage)
+		{
+			this.usage = usage;
+		}
+
+		public override Asn1Object ToAsn1Object()
+		{
+			return new KeyUsage(usage);
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509SignatureUtil.cs b/Crypto/src/x509/X509SignatureUtil.cs
new file mode 100644
index 000000000..7a4ab1448
--- /dev/null
+++ b/Crypto/src/x509/X509SignatureUtil.cs
@@ -0,0 +1,128 @@
+using System;
+
+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;
+
+namespace Org.BouncyCastle.X509
+{
+	internal class X509SignatureUtilities
+	{
+		private static readonly Asn1Null derNull = DerNull.Instance;
+
+		internal static void SetSignatureParameters(
+			ISigner			signature,
+			Asn1Encodable	parameters)
+		{
+			if (parameters != null && !derNull.Equals(parameters))
+			{
+				// TODO Put back in
+//				AlgorithmParameters sigParams = AlgorithmParameters.GetInstance(signature.getAlgorithm());
+//
+//				try
+//				{
+//					sigParams.Init(parameters.ToAsn1Object().GetDerEncoded());
+//				}
+//				catch (IOException e)
+//				{
+//					throw new SignatureException("IOException decoding parameters: " + e.Message);
+//				}
+//
+//				if (signature.getAlgorithm().EndsWith("MGF1"))
+//				{
+//					try
+//					{
+//						signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class));
+//					}
+//					catch (GeneralSecurityException e)
+//					{
+//						throw new SignatureException("Exception extracting parameters: " + e.Message);
+//					}
+//				}
+			}
+		}
+
+		internal static string GetSignatureName(
+			AlgorithmIdentifier sigAlgId)
+		{
+			Asn1Encodable parameters = sigAlgId.Parameters;
+
+			if (parameters != null && !derNull.Equals(parameters))
+			{
+				if (sigAlgId.ObjectID.Equals(PkcsObjectIdentifiers.IdRsassaPss))
+				{
+					RsassaPssParameters rsaParams = RsassaPssParameters.GetInstance(parameters);
+
+					return GetDigestAlgName(rsaParams.HashAlgorithm.ObjectID) + "withRSAandMGF1";
+				}
+				if (sigAlgId.ObjectID.Equals(X9ObjectIdentifiers.ECDsaWithSha2))
+				{
+					Asn1Sequence ecDsaParams = Asn1Sequence.GetInstance(parameters);
+
+					return GetDigestAlgName((DerObjectIdentifier)ecDsaParams[0]) + "withECDSA";
+				}
+			}
+
+			return sigAlgId.ObjectID.Id;
+		}
+
+		/**
+		 * Return the digest algorithm using one of the standard JCA string
+		 * representations rather than the algorithm identifier (if possible).
+		 */
+		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/x509/X509Utilities.cs b/Crypto/src/x509/X509Utilities.cs
new file mode 100644
index 000000000..000958340
--- /dev/null
+++ b/Crypto/src/x509/X509Utilities.cs
@@ -0,0 +1,188 @@
+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.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.X509
+{
+	internal class X509Utilities
+	{
+        private static readonly IDictionary algorithms = Platform.CreateHashtable();
+        private static readonly IDictionary exParams = Platform.CreateHashtable();
+		private static readonly ISet        noParams = new HashSet();
+
+		static X509Utilities()
+		{
+			algorithms.Add("MD2WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD2WithRsaEncryption);
+			algorithms.Add("MD2WITHRSA", PkcsObjectIdentifiers.MD2WithRsaEncryption);
+			algorithms.Add("MD5WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD5WithRsaEncryption);
+			algorithms.Add("MD5WITHRSA", PkcsObjectIdentifiers.MD5WithRsaEncryption);
+			algorithms.Add("SHA1WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha1WithRsaEncryption);
+			algorithms.Add("SHA1WITHRSA", PkcsObjectIdentifiers.Sha1WithRsaEncryption);
+			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("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+			algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+			algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+			algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+			algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+			algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+			algorithms.Add("SHA1WITHDSA", X9ObjectIdentifiers.IdDsaWithSha1);
+			algorithms.Add("DSAWITHSHA1", X9ObjectIdentifiers.IdDsaWithSha1);
+			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("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1);
+			algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224);
+			algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256);
+			algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384);
+			algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512);
+			algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+			algorithms.Add("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+			algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+			algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+			algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+
+			//
+			// 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);
+			noParams.Add(NistObjectIdentifiers.DsaWithSha384);
+			noParams.Add(NistObjectIdentifiers.DsaWithSha512);
+
+			//
+			// 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));
+		}
+
+		internal static DerObjectIdentifier GetAlgorithmOid(
+			string algorithmName)
+		{
+			algorithmName = algorithmName.ToUpperInvariant();
+
+			if (algorithms.Contains(algorithmName))
+			{
+				return (DerObjectIdentifier) algorithms[algorithmName];
+			}
+
+			return new DerObjectIdentifier(algorithmName);
+		}
+
+		internal static AlgorithmIdentifier GetSigAlgID(
+			DerObjectIdentifier sigOid,
+			string				algorithmName)
+		{
+			if (noParams.Contains(sigOid))
+			{
+				return new AlgorithmIdentifier(sigOid);
+			}
+
+			algorithmName = algorithmName.ToUpperInvariant();
+
+			if (exParams.Contains(algorithmName))
+			{
+				return new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]);
+			}
+
+			return new AlgorithmIdentifier(sigOid, DerNull.Instance);
+		}
+
+		internal static IEnumerable GetAlgNames()
+		{
+			return new EnumerableProxy(algorithms.Keys);
+		}
+
+		internal static byte[] GetSignatureForObject(
+			DerObjectIdentifier		sigOid, // TODO Redundant now?
+			string					sigName,
+			AsymmetricKeyParameter	privateKey,
+			SecureRandom			random,
+			Asn1Encodable			ae)
+		{
+			if (sigOid == null)
+				throw new ArgumentNullException("sigOid");
+
+			ISigner sig = SignerUtilities.GetSigner(sigName);
+
+			if (random != null)
+			{
+				sig.Init(true, new ParametersWithRandom(privateKey, random));
+			}
+			else
+			{
+				sig.Init(true, privateKey);
+			}
+
+			byte[] encoded = ae.GetDerEncoded();
+			sig.BlockUpdate(encoded, 0, encoded.Length);
+
+			return sig.GenerateSignature();
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509V1CertificateGenerator.cs b/Crypto/src/x509/X509V1CertificateGenerator.cs
new file mode 100644
index 000000000..02b58a198
--- /dev/null
+++ b/Crypto/src/x509/X509V1CertificateGenerator.cs
@@ -0,0 +1,205 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+
+namespace Org.BouncyCastle.X509
+{
+	/// <summary>
+	/// Class to Generate X509V1 Certificates.
+	/// </summary>
+	public class X509V1CertificateGenerator
+	{
+		private V1TbsCertificateGenerator   tbsGen;
+		private DerObjectIdentifier         sigOID;
+		private AlgorithmIdentifier         sigAlgId;
+		private string                      signatureAlgorithm;
+
+		/// <summary>
+		/// Default Constructor.
+		/// </summary>
+		public X509V1CertificateGenerator()
+		{
+			tbsGen = new V1TbsCertificateGenerator();
+		}
+
+		/// <summary>
+		/// Reset the generator.
+		/// </summary>
+		public void Reset()
+		{
+			tbsGen = new V1TbsCertificateGenerator();
+		}
+
+		/// <summary>
+		/// Set the certificate's serial number.
+		/// </summary>
+		/// <remarks>Make serial numbers long, if you have no serial number policy make sure the number is at least 16 bytes of secure random data.
+		/// You will be surprised how ugly a serial number collision can get.</remarks>
+		/// <param name="serialNumber">The serial number.</param>
+		public void SetSerialNumber(
+			BigInteger serialNumber)
+		{
+			if (serialNumber.SignValue <= 0)
+			{
+				throw new ArgumentException("serial number must be a positive integer", "serialNumber");
+			}
+
+			tbsGen.SetSerialNumber(new DerInteger(serialNumber));
+		}
+
+		/// <summary>
+		/// Set the issuer distinguished name.
+		/// The issuer is the entity whose private key is used to sign the certificate.
+		/// </summary>
+		/// <param name="issuer">The issuers DN.</param>
+		public void SetIssuerDN(
+			X509Name issuer)
+		{
+			tbsGen.SetIssuer(issuer);
+		}
+
+		/// <summary>
+		/// Set the date that this certificate is to be valid from.
+		/// </summary>
+		/// <param name="date"/>
+		public void SetNotBefore(
+			DateTime date)
+		{
+			tbsGen.SetStartDate(new Time(date));
+		}
+
+		/// <summary>
+		/// Set the date after which this certificate will no longer be valid.
+		/// </summary>
+		/// <param name="date"/>
+		public void SetNotAfter(
+			DateTime date)
+		{
+			tbsGen.SetEndDate(new Time(date));
+		}
+
+		/// <summary>
+		/// Set the subject distinguished name.
+		/// The subject describes the entity associated with the public key.
+		/// </summary>
+		/// <param name="subject"/>
+		public void SetSubjectDN(
+			X509Name subject)
+		{
+			tbsGen.SetSubject(subject);
+		}
+
+        /// <summary>
+        /// Set the public key that this certificate identifies.
+        /// </summary>
+        /// <param name="publicKey"/>
+		public void SetPublicKey(
+			AsymmetricKeyParameter publicKey)
+		{
+			try
+			{
+				tbsGen.SetSubjectPublicKeyInfo(
+					SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey));
+			}
+			catch (Exception e)
+			{
+				throw new ArgumentException("unable to process key - " + e.ToString());
+			}
+		}
+
+		/// <summary>
+		/// Set the signature algorithm that will be used to sign this certificate.
+		/// This can be either a name or an OID, names are treated as case insensitive.
+		/// </summary>
+		/// <param name="signatureAlgorithm">string representation of the algorithm name</param>
+		public void SetSignatureAlgorithm(
+			string signatureAlgorithm)
+		{
+			this.signatureAlgorithm = signatureAlgorithm;
+
+			try
+			{
+				sigOID = X509Utilities.GetAlgorithmOid(signatureAlgorithm);
+			}
+			catch (Exception)
+			{
+				throw new ArgumentException("Unknown signature type requested", "signatureAlgorithm");
+			}
+
+			sigAlgId = X509Utilities.GetSigAlgID(sigOID, signatureAlgorithm);
+
+			tbsGen.SetSignature(sigAlgId);
+		}
+
+		/// <summary>
+		/// Generate a new X509Certificate.
+		/// </summary>
+		/// <param name="privateKey">The private key of the issuer used to sign this certificate.</param>
+		/// <returns>An X509Certificate.</returns>
+		public X509Certificate Generate(
+			AsymmetricKeyParameter privateKey)
+		{
+			return Generate(privateKey, null);
+		}
+
+        /// <summary>
+        /// Generate a new X509Certificate specifying a SecureRandom instance that you would like to use.
+        /// </summary>
+        /// <param name="privateKey">The private key of the issuer used to sign this certificate.</param>
+        /// <param name="random">The Secure Random you want to use.</param>
+        /// <returns>An X509Certificate.</returns>
+		public X509Certificate Generate(
+			AsymmetricKeyParameter	privateKey,
+			SecureRandom			random)
+		{
+			TbsCertificateStructure tbsCert = tbsGen.GenerateTbsCertificate();
+			byte[] signature;
+
+			try
+			{
+				signature = X509Utilities.GetSignatureForObject(
+					sigOID, signatureAlgorithm, privateKey, random, tbsCert);
+			}
+			catch (Exception e)
+			{
+				// TODO
+//				throw new ExtCertificateEncodingException("exception encoding TBS cert", e);
+				throw new CertificateEncodingException("exception encoding TBS cert", e);
+			}
+
+			try
+			{
+				return GenerateJcaObject(tbsCert, signature);
+			}
+			catch (CertificateParsingException e)
+			{
+				// TODO
+				// throw new ExtCertificateEncodingException("exception producing certificate object", e);
+				throw new CertificateEncodingException("exception producing certificate object", e);
+			}
+		}
+
+		private X509Certificate GenerateJcaObject(
+			TbsCertificateStructure	tbsCert,
+			byte[]					signature)
+		{
+			return new X509Certificate(
+				new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature)));
+		}
+
+		/// <summary>
+		/// Allows enumeration of the signature names supported by the generator.
+		/// </summary>
+		public IEnumerable SignatureAlgNames
+		{
+			get { return X509Utilities.GetAlgNames(); }
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509V2AttributeCertificate.cs b/Crypto/src/x509/X509V2AttributeCertificate.cs
new file mode 100644
index 000000000..117ac4cc2
--- /dev/null
+++ b/Crypto/src/x509/X509V2AttributeCertificate.cs
@@ -0,0 +1,255 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.X509
+{
+	/// <summary>An implementation of a version 2 X.509 Attribute Certificate.</summary>
+	public class X509V2AttributeCertificate
+		: X509ExtensionBase, IX509AttributeCertificate
+	{
+		private readonly AttributeCertificate cert;
+		private readonly DateTime notBefore;
+		private readonly DateTime notAfter;
+
+		private static AttributeCertificate GetObject(Stream input)
+		{
+			try
+			{
+				return AttributeCertificate.GetInstance(Asn1Object.FromStream(input));
+			}
+			catch (IOException e)
+			{
+				throw e;
+			}
+			catch (Exception e)
+			{
+				throw new IOException("exception decoding certificate structure", e);
+			}
+		}
+
+		public X509V2AttributeCertificate(
+			Stream encIn)
+			: this(GetObject(encIn))
+		{
+		}
+
+		public X509V2AttributeCertificate(
+			byte[] encoded)
+			: this(new MemoryStream(encoded, false))
+		{
+		}
+
+		internal X509V2AttributeCertificate(
+			AttributeCertificate cert)
+		{
+			this.cert = cert;
+
+			try
+			{
+				this.notAfter = cert.ACInfo.AttrCertValidityPeriod.NotAfterTime.ToDateTime();
+				this.notBefore = cert.ACInfo.AttrCertValidityPeriod.NotBeforeTime.ToDateTime();
+			}
+			catch (Exception e)
+			{
+				throw new IOException("invalid data structure in certificate!", e);
+			}
+		}
+
+		public virtual int Version
+		{
+			get { return cert.ACInfo.Version.Value.IntValue + 1; }
+		}
+
+		public virtual BigInteger SerialNumber
+		{
+			get { return cert.ACInfo.SerialNumber.Value; }
+		}
+
+		public virtual AttributeCertificateHolder Holder
+		{
+			get
+			{
+				return new AttributeCertificateHolder((Asn1Sequence)cert.ACInfo.Holder.ToAsn1Object());
+			}
+		}
+
+		public virtual AttributeCertificateIssuer Issuer
+		{
+			get
+			{
+				return new AttributeCertificateIssuer(cert.ACInfo.Issuer);
+			}
+		}
+
+		public virtual DateTime NotBefore
+		{
+			get { return notBefore; }
+		}
+
+		public virtual DateTime NotAfter
+		{
+			get { return notAfter; }
+		}
+
+		public virtual bool[] GetIssuerUniqueID()
+		{
+			DerBitString id = cert.ACInfo.IssuerUniqueID;
+
+			if (id != null)
+			{
+				byte[] bytes = id.GetBytes();
+				bool[] boolId = new bool[bytes.Length * 8 - id.PadBits];
+
+				for (int i = 0; i != boolId.Length; i++)
+				{
+					//boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
+					boolId[i] = (bytes[i / 8] & (0x80 >> (i % 8))) != 0;
+				}
+
+				return boolId;
+			}
+
+			return null;
+		}
+
+		public virtual bool IsValidNow
+		{
+			get { return IsValid(DateTime.UtcNow); }
+		}
+
+		public virtual bool IsValid(
+			DateTime date)
+		{
+			return date.CompareTo(NotBefore) >= 0 && date.CompareTo(NotAfter) <= 0;
+		}
+
+		public virtual void CheckValidity()
+		{
+			this.CheckValidity(DateTime.UtcNow);
+		}
+
+		public virtual void CheckValidity(
+			DateTime date)
+		{
+			if (date.CompareTo(NotAfter) > 0)
+				throw new CertificateExpiredException("certificate expired on " + NotAfter);
+			if (date.CompareTo(NotBefore) < 0)
+				throw new CertificateNotYetValidException("certificate not valid until " + NotBefore);
+		}
+
+		public virtual byte[] GetSignature()
+		{
+			return cert.SignatureValue.GetBytes();
+		}
+
+		public virtual void Verify(
+			AsymmetricKeyParameter publicKey)
+		{
+			if (!cert.SignatureAlgorithm.Equals(cert.ACInfo.Signature))
+			{
+				throw new CertificateException("Signature algorithm in certificate info not same as outer certificate");
+			}
+
+			ISigner signature = SignerUtilities.GetSigner(cert.SignatureAlgorithm.ObjectID.Id);
+
+			signature.Init(false, publicKey);
+
+			try
+			{
+				byte[] b = cert.ACInfo.GetEncoded();
+				signature.BlockUpdate(b, 0, b.Length);
+			}
+			catch (IOException e)
+			{
+				throw new SignatureException("Exception encoding certificate info object", e);
+			}
+
+			if (!signature.VerifySignature(this.GetSignature()))
+			{
+				throw new InvalidKeyException("Public key presented not for certificate signature");
+			}
+		}
+
+		public virtual byte[] GetEncoded()
+		{
+			return cert.GetEncoded();
+		}
+
+		protected override X509Extensions GetX509Extensions()
+		{
+			return cert.ACInfo.Extensions;
+		}
+
+		public virtual X509Attribute[] GetAttributes()
+		{
+			Asn1Sequence seq = cert.ACInfo.Attributes;
+			X509Attribute[] attrs = new X509Attribute[seq.Count];
+
+			for (int i = 0; i != seq.Count; i++)
+			{
+				attrs[i] = new X509Attribute((Asn1Encodable)seq[i]);
+			}
+
+			return attrs;
+		}
+
+		public virtual X509Attribute[] GetAttributes(
+			string oid)
+		{
+			Asn1Sequence seq = cert.ACInfo.Attributes;
+			IList list = Platform.CreateArrayList();
+
+			for (int i = 0; i != seq.Count; i++)
+			{
+				X509Attribute attr = new X509Attribute((Asn1Encodable)seq[i]);
+				if (attr.Oid.Equals(oid))
+				{
+					list.Add(attr);
+				}
+			}
+
+			if (list.Count < 1)
+			{
+				return null;
+			}
+
+            X509Attribute[] result = new X509Attribute[list.Count];
+            for (int i = 0; i < list.Count; ++i)
+            {
+                result[i] = (X509Attribute)list[i];
+            }
+            return result;
+		}
+
+		public override bool Equals(
+			object obj)
+		{
+			if (obj == this)
+				return true;
+
+			X509V2AttributeCertificate other = obj as X509V2AttributeCertificate;
+
+			if (other == null)
+				return false;
+
+			return cert.Equals(other.cert);
+
+			// NB: May prefer this implementation of Equals if more than one certificate implementation in play
+			//return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded());
+		}
+
+		public override int GetHashCode()
+		{
+			return cert.GetHashCode();
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509V2AttributeCertificateGenerator.cs b/Crypto/src/x509/X509V2AttributeCertificateGenerator.cs
new file mode 100644
index 000000000..a683d5e20
--- /dev/null
+++ b/Crypto/src/x509/X509V2AttributeCertificateGenerator.cs
@@ -0,0 +1,180 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.X509
+{
+	/// <remarks>Class to produce an X.509 Version 2 AttributeCertificate.</remarks>
+	public class X509V2AttributeCertificateGenerator
+	{
+		private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator();
+
+		private V2AttributeCertificateInfoGenerator	acInfoGen;
+		private DerObjectIdentifier sigOID;
+		private AlgorithmIdentifier sigAlgId;
+		private string signatureAlgorithm;
+
+		public X509V2AttributeCertificateGenerator()
+		{
+			acInfoGen = new V2AttributeCertificateInfoGenerator();
+		}
+
+		/// <summary>Reset the generator</summary>
+		public void Reset()
+		{
+			acInfoGen = new V2AttributeCertificateInfoGenerator();
+			extGenerator.Reset();
+		}
+
+		/// <summary>Set the Holder of this Attribute Certificate.</summary>
+		public void SetHolder(
+			AttributeCertificateHolder holder)
+		{
+			acInfoGen.SetHolder(holder.holder);
+		}
+
+		/// <summary>Set the issuer.</summary>
+		public void SetIssuer(
+			AttributeCertificateIssuer issuer)
+		{
+			acInfoGen.SetIssuer(AttCertIssuer.GetInstance(issuer.form));
+		}
+
+		/// <summary>Set the serial number for the certificate.</summary>
+		public void SetSerialNumber(
+			BigInteger serialNumber)
+		{
+			acInfoGen.SetSerialNumber(new DerInteger(serialNumber));
+		}
+
+		public void SetNotBefore(
+			DateTime date)
+		{
+			acInfoGen.SetStartDate(new DerGeneralizedTime(date));
+		}
+
+		public void SetNotAfter(
+			DateTime date)
+		{
+			acInfoGen.SetEndDate(new DerGeneralizedTime(date));
+		}
+
+		/// <summary>
+		/// Set the signature algorithm. This can be either a name or an OID, names
+		/// are treated as case insensitive.
+		/// </summary>
+		/// <param name="signatureAlgorithm">The algorithm name.</param>
+		public void SetSignatureAlgorithm(
+			string signatureAlgorithm)
+		{
+			this.signatureAlgorithm = signatureAlgorithm;
+
+			try
+			{
+				sigOID = X509Utilities.GetAlgorithmOid(signatureAlgorithm);
+			}
+			catch (Exception)
+			{
+				throw new ArgumentException("Unknown signature type requested");
+			}
+
+			sigAlgId = X509Utilities.GetSigAlgID(sigOID, signatureAlgorithm);
+
+			acInfoGen.SetSignature(sigAlgId);
+		}
+
+		/// <summary>Add an attribute.</summary>
+		public void AddAttribute(
+			X509Attribute attribute)
+		{
+			acInfoGen.AddAttribute(AttributeX509.GetInstance(attribute.ToAsn1Object()));
+		}
+
+		public void SetIssuerUniqueId(
+			bool[] iui)
+		{
+			// TODO convert bool array to bit string
+			//acInfoGen.SetIssuerUniqueID(iui);
+			throw Platform.CreateNotImplementedException("SetIssuerUniqueId()");
+		}
+
+		/// <summary>Add a given extension field for the standard extensions tag.</summary>
+		public void AddExtension(
+			string			oid,
+			bool			critical,
+			Asn1Encodable	extensionValue)
+		{
+			extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue);
+		}
+
+		/// <summary>
+		/// Add a given extension field for the standard extensions tag.
+		/// The value parameter becomes the contents of the octet string associated
+		/// with the extension.
+		/// </summary>
+		public void AddExtension(
+			string	oid,
+			bool	critical,
+			byte[]	extensionValue)
+		{
+			extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue);
+		}
+
+		/// <summary>
+		/// Generate an X509 certificate, based on the current issuer and subject.
+		/// </summary>
+		public IX509AttributeCertificate Generate(
+			AsymmetricKeyParameter publicKey)
+		{
+			return Generate(publicKey, null);
+		}
+
+		/// <summary>
+		/// Generate an X509 certificate, based on the current issuer and subject,
+		/// using the supplied source of randomness, if required.
+		/// </summary>
+		public IX509AttributeCertificate Generate(
+			AsymmetricKeyParameter	publicKey,
+			SecureRandom			random)
+		{
+			if (!extGenerator.IsEmpty)
+			{
+				acInfoGen.SetExtensions(extGenerator.Generate());
+			}
+
+			AttributeCertificateInfo acInfo = acInfoGen.GenerateAttributeCertificateInfo();
+
+			Asn1EncodableVector v = new Asn1EncodableVector();
+
+			v.Add(acInfo, sigAlgId);
+
+			try
+			{
+				v.Add(new DerBitString(X509Utilities.GetSignatureForObject(sigOID, signatureAlgorithm, publicKey, random, acInfo)));
+
+				return new X509V2AttributeCertificate(AttributeCertificate.GetInstance(new DerSequence(v)));
+			}
+			catch (Exception e)
+			{
+				// TODO
+//				throw new ExtCertificateEncodingException("constructed invalid certificate", e);
+				throw new CertificateEncodingException("constructed invalid certificate", e);
+			}
+		}
+
+		/// <summary>
+		/// Allows enumeration of the signature names supported by the generator.
+		/// </summary>
+		public IEnumerable SignatureAlgNames
+		{
+			get { return X509Utilities.GetAlgNames(); }
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509V2CRLGenerator.cs b/Crypto/src/x509/X509V2CRLGenerator.cs
new file mode 100644
index 000000000..a2293b333
--- /dev/null
+++ b/Crypto/src/x509/X509V2CRLGenerator.cs
@@ -0,0 +1,261 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.X509
+{
+	/**
+	* class to produce an X.509 Version 2 CRL.
+	*/
+	public class X509V2CrlGenerator
+	{
+		private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator();
+
+		private V2TbsCertListGenerator	tbsGen;
+		private DerObjectIdentifier		sigOID;
+		private AlgorithmIdentifier		sigAlgId;
+		private string					signatureAlgorithm;
+
+		public X509V2CrlGenerator()
+		{
+			tbsGen = new V2TbsCertListGenerator();
+		}
+
+		/**
+		* reset the generator
+		*/
+		public void Reset()
+		{
+			tbsGen = new V2TbsCertListGenerator();
+			extGenerator.Reset();
+		}
+
+		/**
+		* Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
+		* certificate.
+		*/
+		public void SetIssuerDN(
+			X509Name issuer)
+		{
+			tbsGen.SetIssuer(issuer);
+		}
+
+		public void SetThisUpdate(
+			DateTime date)
+		{
+			tbsGen.SetThisUpdate(new Time(date));
+		}
+
+		public void SetNextUpdate(
+			DateTime date)
+		{
+			tbsGen.SetNextUpdate(new Time(date));
+		}
+
+		/**
+		* Reason being as indicated by CrlReason, i.e. CrlReason.KeyCompromise
+		* or 0 if CrlReason is not to be used
+		**/
+		public void AddCrlEntry(
+			BigInteger	userCertificate,
+			DateTime	revocationDate,
+			int			reason)
+		{
+			tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), reason);
+		}
+
+		/**
+		* Add a CRL entry with an Invalidity Date extension as well as a CrlReason extension.
+		* Reason being as indicated by CrlReason, i.e. CrlReason.KeyCompromise
+		* or 0 if CrlReason is not to be used
+		**/
+		public void AddCrlEntry(
+			BigInteger	userCertificate,
+			DateTime	revocationDate,
+			int			reason,
+			DateTime	invalidityDate)
+		{
+			tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), reason, new DerGeneralizedTime(invalidityDate));
+		}
+
+		/**
+		* Add a CRL entry with extensions.
+		**/
+		public void AddCrlEntry(
+			BigInteger		userCertificate,
+			DateTime		revocationDate,
+			X509Extensions	extensions)
+		{
+			tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), extensions);
+		}
+
+		/**
+		* Add the CRLEntry objects contained in a previous CRL.
+		*
+		* @param other the X509Crl to source the other entries from.
+		*/
+		public void AddCrl(
+			X509Crl other)
+		{
+			if (other == null)
+				throw new ArgumentNullException("other");
+
+			ISet revocations = other.GetRevokedCertificates();
+
+			if (revocations != null)
+			{
+				foreach (X509CrlEntry entry in revocations)
+				{
+					try
+					{
+						tbsGen.AddCrlEntry(
+							Asn1Sequence.GetInstance(
+							Asn1Object.FromByteArray(entry.GetEncoded())));
+					}
+					catch (IOException e)
+					{
+						throw new CrlException("exception processing encoding of CRL", e);
+					}
+				}
+			}
+		}
+
+		/**
+		* Set the signature algorithm. This can be either a name or an oid, names
+		* are treated as case insensitive.
+		*
+		* @param signatureAlgorithm string representation of the algorithm name.
+		*/
+		public void SetSignatureAlgorithm(
+			string signatureAlgorithm)
+		{
+			this.signatureAlgorithm = signatureAlgorithm;
+
+			try
+			{
+				sigOID = X509Utilities.GetAlgorithmOid(signatureAlgorithm);
+			}
+			catch (Exception e)
+			{
+				throw new ArgumentException("Unknown signature type requested", e);
+			}
+
+			sigAlgId = X509Utilities.GetSigAlgID(sigOID, signatureAlgorithm);
+
+			tbsGen.SetSignature(sigAlgId);
+		}
+
+		/**
+		* add a given extension field for the standard extensions tag (tag 0)
+		*/
+		public void AddExtension(
+			string			oid,
+			bool			critical,
+			Asn1Encodable	extensionValue)
+		{
+			extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue);
+		}
+
+		/**
+		* add a given extension field for the standard extensions tag (tag 0)
+		*/
+		public void AddExtension(
+			DerObjectIdentifier	oid,
+			bool				critical,
+			Asn1Encodable		extensionValue)
+		{
+			extGenerator.AddExtension(oid, critical, extensionValue);
+		}
+
+		/**
+		* add a given extension field for the standard extensions tag (tag 0)
+		*/
+		public void AddExtension(
+			string	oid,
+			bool	critical,
+			byte[]	extensionValue)
+		{
+			extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, new DerOctetString(extensionValue));
+		}
+
+		/**
+		* add a given extension field for the standard extensions tag (tag 0)
+		*/
+		public void AddExtension(
+			DerObjectIdentifier	oid,
+			bool				critical,
+			byte[]				extensionValue)
+		{
+			extGenerator.AddExtension(oid, critical, new DerOctetString(extensionValue));
+		}
+
+		/// <summary>Generate an X509 CRL, based on the current issuer and subject.</summary>
+		/// <param name="privateKey">The key used for signing.</param>
+		public X509Crl Generate(
+			AsymmetricKeyParameter privateKey)
+		{
+			return Generate(privateKey, null);
+		}
+
+		/// <summary>Generate an X509 CRL, based on the current issuer and subject.</summary>
+		/// <param name="privateKey">The key used for signing.</param>
+		/// <param name="random">A user-defined source of randomness.</param>
+		public X509Crl Generate(
+			AsymmetricKeyParameter	privateKey,
+			SecureRandom			random)
+		{
+			TbsCertificateList tbsCrl = GenerateCertList();
+			byte[] signature;
+
+			try
+			{
+				signature = X509Utilities.GetSignatureForObject(
+					sigOID, signatureAlgorithm, privateKey, random, tbsCrl);
+			}
+			catch (IOException e)
+			{
+				// TODO
+//				throw new ExtCrlException("cannot generate CRL encoding", e);
+				throw new CrlException("cannot generate CRL encoding", e);
+			}
+
+			return GenerateJcaObject(tbsCrl, signature);
+		}
+
+		private TbsCertificateList GenerateCertList()
+		{
+			if (!extGenerator.IsEmpty)
+			{
+				tbsGen.SetExtensions(extGenerator.Generate());
+			}
+
+			return tbsGen.GenerateTbsCertList();
+		}
+
+		private X509Crl GenerateJcaObject(
+			TbsCertificateList	tbsCrl,
+			byte[]				signature)
+		{
+			return new X509Crl(
+				CertificateList.GetInstance(
+					new DerSequence(tbsCrl, sigAlgId, new DerBitString(signature))));
+		}
+
+		/// <summary>
+		/// Allows enumeration of the signature names supported by the generator.
+		/// </summary>
+		public IEnumerable SignatureAlgNames
+		{
+			get { return X509Utilities.GetAlgNames(); }
+		}
+	}
+}
diff --git a/Crypto/src/x509/X509V3CertificateGenerator.cs b/Crypto/src/x509/X509V3CertificateGenerator.cs
new file mode 100644
index 000000000..bb0dd9cbc
--- /dev/null
+++ b/Crypto/src/x509/X509V3CertificateGenerator.cs
@@ -0,0 +1,346 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.X509.Extension;
+
+namespace Org.BouncyCastle.X509
+{
+    /// <summary>
+    /// A class to Generate Version 3 X509Certificates.
+    /// </summary>
+    public class X509V3CertificateGenerator
+    {
+		private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator();
+
+		private V3TbsCertificateGenerator	tbsGen;
+        private DerObjectIdentifier			sigOid;
+        private AlgorithmIdentifier			sigAlgId;
+        private string						signatureAlgorithm;
+
+		public X509V3CertificateGenerator()
+        {
+            tbsGen = new V3TbsCertificateGenerator();
+        }
+
+		/// <summary>
+		/// Reset the Generator.
+		/// </summary>
+		public void Reset()
+		{
+			tbsGen = new V3TbsCertificateGenerator();
+			extGenerator.Reset();
+		}
+
+		/// <summary>
+        /// Set the certificate's serial number.
+        /// </summary>
+        /// <remarks>Make serial numbers long, if you have no serial number policy make sure the number is at least 16 bytes of secure random data.
+        /// You will be surprised how ugly a serial number collision can Get.</remarks>
+        /// <param name="serialNumber">The serial number.</param>
+        public void SetSerialNumber(
+			BigInteger serialNumber)
+        {
+			if (serialNumber.SignValue <= 0)
+			{
+				throw new ArgumentException("serial number must be a positive integer", "serialNumber");
+			}
+
+			tbsGen.SetSerialNumber(new DerInteger(serialNumber));
+        }
+
+		/// <summary>
+        /// Set the distinguished name of the issuer.
+        /// The issuer is the entity which is signing the certificate.
+        /// </summary>
+        /// <param name="issuer">The issuer's DN.</param>
+        public void SetIssuerDN(
+            X509Name issuer)
+        {
+            tbsGen.SetIssuer(issuer);
+        }
+
+		/// <summary>
+        /// Set the date that this certificate is to be valid from.
+        /// </summary>
+        /// <param name="date"/>
+        public void SetNotBefore(
+            DateTime date)
+        {
+            tbsGen.SetStartDate(new Time(date));
+        }
+
+        /// <summary>
+        /// Set the date after which this certificate will no longer be valid.
+        /// </summary>
+        /// <param name="date"/>
+        public void SetNotAfter(
+			DateTime date)
+        {
+            tbsGen.SetEndDate(new Time(date));
+        }
+
+		/// <summary>
+		/// Set the DN of the entity that this certificate is about.
+		/// </summary>
+		/// <param name="subject"/>
+        public void SetSubjectDN(
+			X509Name subject)
+        {
+            tbsGen.SetSubject(subject);
+        }
+
+		/// <summary>
+        /// Set the public key that this certificate identifies.
+        /// </summary>
+        /// <param name="publicKey"/>
+        public void SetPublicKey(
+			AsymmetricKeyParameter publicKey)
+        {
+            tbsGen.SetSubjectPublicKeyInfo(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey));
+        }
+
+		/// <summary>
+        /// Set the signature algorithm that will be used to sign this certificate.
+        /// </summary>
+        /// <param name="signatureAlgorithm"/>
+        public void SetSignatureAlgorithm(
+			string signatureAlgorithm)
+        {
+			this.signatureAlgorithm = signatureAlgorithm;
+
+			try
+			{
+				sigOid = X509Utilities.GetAlgorithmOid(signatureAlgorithm);
+			}
+			catch (Exception)
+			{
+				throw new ArgumentException("Unknown signature type requested: " + signatureAlgorithm);
+			}
+
+			sigAlgId = X509Utilities.GetSigAlgID(sigOid, signatureAlgorithm);
+
+			tbsGen.SetSignature(sigAlgId);
+		}
+
+		/// <summary>
+		/// Set the subject unique ID - note: it is very rare that it is correct to do this.
+		/// </summary>
+		/// <param name="uniqueID"/>
+		public void SetSubjectUniqueID(
+			bool[] uniqueID)
+		{
+			tbsGen.SetSubjectUniqueID(booleanToBitString(uniqueID));
+		}
+
+		/// <summary>
+		/// Set the issuer unique ID - note: it is very rare that it is correct to do this.
+		/// </summary>
+		/// <param name="uniqueID"/>
+		public void SetIssuerUniqueID(
+			bool[] uniqueID)
+		{
+			tbsGen.SetIssuerUniqueID(booleanToBitString(uniqueID));
+		}
+
+		private DerBitString booleanToBitString(
+			bool[] id)
+		{
+			byte[] bytes = new byte[(id.Length + 7) / 8];
+
+			for (int i = 0; i != id.Length; i++)
+			{
+				if (id[i])
+				{
+					bytes[i / 8] |= (byte)(1 << ((7 - (i % 8))));
+				}
+			}
+
+			int pad = id.Length % 8;
+
+			if (pad == 0)
+			{
+				return new DerBitString(bytes);
+			}
+
+			return new DerBitString(bytes, 8 - pad);
+		}
+
+		/// <summary>
+		/// Add a given extension field for the standard extensions tag (tag 3).
+		/// </summary>
+		/// <param name="oid">string containing a dotted decimal Object Identifier.</param>
+		/// <param name="critical">Is it critical.</param>
+		/// <param name="extensionValue">The value.</param>
+		public void AddExtension(
+			string			oid,
+			bool			critical,
+			Asn1Encodable	extensionValue)
+		{
+			extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue);
+		}
+
+		/// <summary>
+        /// Add an extension to this certificate.
+        /// </summary>
+        /// <param name="oid">Its Object Identifier.</param>
+        /// <param name="critical">Is it critical.</param>
+        /// <param name="extensionValue">The value.</param>
+        public void AddExtension(
+			DerObjectIdentifier	oid,
+			bool				critical,
+			Asn1Encodable		extensionValue)
+        {
+			extGenerator.AddExtension(oid, critical, extensionValue);
+        }
+
+		/// <summary>
+		/// Add an extension using a string with a dotted decimal OID.
+		/// </summary>
+		/// <param name="oid">string containing a dotted decimal Object Identifier.</param>
+		/// <param name="critical">Is it critical.</param>
+		/// <param name="extensionValue">byte[] containing the value of this extension.</param>
+		public void AddExtension(
+			string	oid,
+			bool	critical,
+			byte[]	extensionValue)
+		{
+			extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, new DerOctetString(extensionValue));
+		}
+
+		/// <summary>
+        /// Add an extension to this certificate.
+        /// </summary>
+        /// <param name="oid">Its Object Identifier.</param>
+        /// <param name="critical">Is it critical.</param>
+        /// <param name="extensionValue">byte[] containing the value of this extension.</param>
+        public void AddExtension(
+			DerObjectIdentifier	oid,
+			bool				critical,
+			byte[]				extensionValue)
+        {
+			extGenerator.AddExtension(oid, critical, new DerOctetString(extensionValue));
+        }
+
+		/// <summary>
+		/// Add a given extension field for the standard extensions tag (tag 3),
+		/// copying the extension value from another certificate.
+		/// </summary>
+		public void CopyAndAddExtension(
+			string			oid,
+			bool			critical,
+			X509Certificate	cert)
+		{
+			CopyAndAddExtension(new DerObjectIdentifier(oid), critical, cert);
+		}
+
+		/**
+		 * add a given extension field for the standard extensions tag (tag 3)
+		 * copying the extension value from another certificate.
+		 * @throws CertificateParsingException if the extension cannot be extracted.
+		 */
+		public void CopyAndAddExtension(
+			DerObjectIdentifier	oid,
+			bool				critical,
+			X509Certificate		cert)
+		{
+			Asn1OctetString extValue = cert.GetExtensionValue(oid);
+
+			if (extValue == null)
+			{
+				throw new CertificateParsingException("extension " + oid + " not present");
+			}
+
+			try
+			{
+				Asn1Encodable value = X509ExtensionUtilities.FromExtensionValue(extValue);
+
+				this.AddExtension(oid, critical, value);
+			}
+			catch (Exception e)
+			{
+				throw new CertificateParsingException(e.Message, e);
+			}
+		}
+
+		/// <summary>
+        /// Generate an X509Certificate.
+        /// </summary>
+        /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param>
+        /// <returns>An X509Certificate.</returns>
+        public X509Certificate Generate(
+			AsymmetricKeyParameter privateKey)
+        {
+            return Generate(privateKey, null);
+        }
+
+		/// <summary>
+		/// Generate an X509Certificate using your own SecureRandom.
+		/// </summary>
+		/// <param name="privateKey">The private key of the issuer that is signing this certificate.</param>
+		/// <param name="random">You Secure Random instance.</param>
+		/// <returns>An X509Certificate.</returns>
+		public X509Certificate Generate(
+			AsymmetricKeyParameter	privateKey,
+			SecureRandom			random)
+		{
+			TbsCertificateStructure tbsCert = GenerateTbsCert();
+			byte[] signature;
+
+			try
+			{
+				signature = X509Utilities.GetSignatureForObject(
+					sigOid, signatureAlgorithm, privateKey, random, tbsCert);
+			}
+			catch (Exception e)
+			{
+				// TODO
+//				throw new ExtCertificateEncodingException("exception encoding TBS cert", e);
+				throw new CertificateEncodingException("exception encoding TBS cert", e);
+			}
+
+			try
+			{
+				return GenerateJcaObject(tbsCert, signature);
+			}
+			catch (CertificateParsingException e)
+			{
+				// TODO
+				// throw new ExtCertificateEncodingException("exception producing certificate object", e);
+				throw new CertificateEncodingException("exception producing certificate object", e);
+			}
+		}
+
+		private TbsCertificateStructure GenerateTbsCert()
+		{
+			if (!extGenerator.IsEmpty)
+			{
+				tbsGen.SetExtensions(extGenerator.Generate());
+			}
+
+			return tbsGen.GenerateTbsCertificate();
+		}
+
+		private X509Certificate GenerateJcaObject(
+			TbsCertificateStructure	tbsCert,
+			byte[]					signature)
+		{
+			return new X509Certificate(
+				new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature)));
+		}
+
+		/// <summary>
+		/// Allows enumeration of the signature names supported by the generator.
+		/// </summary>
+		public IEnumerable SignatureAlgNames
+		{
+			get { return X509Utilities.GetAlgNames(); }
+		}
+	}
+}
diff --git a/Crypto/src/x509/extension/AuthorityKeyIdentifierStructure.cs b/Crypto/src/x509/extension/AuthorityKeyIdentifierStructure.cs
new file mode 100644
index 000000000..006dc009b
--- /dev/null
+++ b/Crypto/src/x509/extension/AuthorityKeyIdentifierStructure.cs
@@ -0,0 +1,102 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Security.Certificates;
+
+namespace Org.BouncyCastle.X509.Extension
+{
+	/// <remarks>A high level authority key identifier.</remarks>
+	public class AuthorityKeyIdentifierStructure
+		: AuthorityKeyIdentifier
+	{
+		/**
+		 * Constructor which will take the byte[] returned from getExtensionValue()
+		 *
+		 * @param encodedValue a DER octet encoded string with the extension structure in it.
+		 * @throws IOException on parsing errors.
+		 */
+		// TODO Add a functional constructor from byte[]?
+		public AuthorityKeyIdentifierStructure(
+			Asn1OctetString encodedValue)
+			: base((Asn1Sequence) X509ExtensionUtilities.FromExtensionValue(encodedValue))
+		{
+		}
+
+		private static Asn1Sequence FromCertificate(
+			X509Certificate certificate)
+		{
+			try
+			{
+				GeneralName genName = new GeneralName(
+					PrincipalUtilities.GetIssuerX509Principal(certificate));
+
+				if (certificate.Version == 3)
+				{
+					Asn1OctetString ext = certificate.GetExtensionValue(X509Extensions.SubjectKeyIdentifier);
+
+					if (ext != null)
+					{
+						Asn1OctetString str = (Asn1OctetString) X509ExtensionUtilities.FromExtensionValue(ext);
+
+						return (Asn1Sequence) new AuthorityKeyIdentifier(
+							str.GetOctets(), new GeneralNames(genName), certificate.SerialNumber).ToAsn1Object();
+					}
+				}
+
+				SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(
+					certificate.GetPublicKey());
+
+				return (Asn1Sequence) new AuthorityKeyIdentifier(
+					info, new GeneralNames(genName), certificate.SerialNumber).ToAsn1Object();
+			}
+			catch (Exception e)
+			{
+				throw new CertificateParsingException("Exception extracting certificate details", e);
+			}
+		}
+
+		private static Asn1Sequence FromKey(
+			AsymmetricKeyParameter pubKey)
+		{
+			try
+			{
+				SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey);
+
+				return (Asn1Sequence) new AuthorityKeyIdentifier(info).ToAsn1Object();
+			}
+			catch (Exception e)
+			{
+				throw new InvalidKeyException("can't process key: " + e);
+			}
+		}
+
+		/**
+		 * Create an AuthorityKeyIdentifier using the passed in certificate's public
+		 * key, issuer and serial number.
+		 *
+		 * @param certificate the certificate providing the information.
+		 * @throws CertificateParsingException if there is a problem processing the certificate
+		 */
+		public AuthorityKeyIdentifierStructure(
+			X509Certificate certificate)
+			: base(FromCertificate(certificate))
+		{
+		}
+
+		/**
+		 * Create an AuthorityKeyIdentifier using just the hash of the
+		 * public key.
+		 *
+		 * @param pubKey the key to generate the hash from.
+		 * @throws InvalidKeyException if there is a problem using the key.
+		 */
+		public AuthorityKeyIdentifierStructure(
+			AsymmetricKeyParameter pubKey)
+			: base(FromKey(pubKey))
+		{
+		}
+	}
+}
diff --git a/Crypto/src/x509/extension/SubjectKeyIdentifierStructure.cs b/Crypto/src/x509/extension/SubjectKeyIdentifierStructure.cs
new file mode 100644
index 000000000..4c7b79ab8
--- /dev/null
+++ b/Crypto/src/x509/extension/SubjectKeyIdentifierStructure.cs
@@ -0,0 +1,49 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security.Certificates;
+
+namespace Org.BouncyCastle.X509.Extension
+{
+	/**
+	 * A high level subject key identifier.
+	 */
+	public class SubjectKeyIdentifierStructure
+		: SubjectKeyIdentifier
+	{
+		/**
+		 * Constructor which will take the byte[] returned from getExtensionValue()
+		 *
+		 * @param encodedValue a DER octet encoded string with the extension structure in it.
+		 * @throws IOException on parsing errors.
+		 */
+		public SubjectKeyIdentifierStructure(
+			Asn1OctetString encodedValue)
+			: base((Asn1OctetString) X509ExtensionUtilities.FromExtensionValue(encodedValue))
+		{
+		}
+
+		private static Asn1OctetString FromPublicKey(
+			AsymmetricKeyParameter pubKey)
+		{
+			try
+			{
+				SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey);
+
+				return (Asn1OctetString) new SubjectKeyIdentifier(info).ToAsn1Object();
+			}
+			catch (Exception e)
+			{
+				throw new CertificateParsingException("Exception extracting certificate details: " + e.ToString());
+			}
+		}
+
+		public SubjectKeyIdentifierStructure(
+			AsymmetricKeyParameter pubKey)
+			: base(FromPublicKey(pubKey))
+		{
+		}
+	}
+}
diff --git a/Crypto/src/x509/extension/X509ExtensionUtil.cs b/Crypto/src/x509/extension/X509ExtensionUtil.cs
new file mode 100644
index 000000000..845a87bad
--- /dev/null
+++ b/Crypto/src/x509/extension/X509ExtensionUtil.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.X509.Extension
+{
+	public class X509ExtensionUtilities
+	{
+		public static Asn1Object FromExtensionValue(
+			Asn1OctetString extensionValue)
+		{
+			return Asn1Object.FromByteArray(extensionValue.GetOctets());
+		}
+
+		public static ICollection GetIssuerAlternativeNames(
+			X509Certificate cert)
+		{
+			Asn1OctetString extVal = cert.GetExtensionValue(X509Extensions.IssuerAlternativeName);
+
+			return GetAlternativeName(extVal);
+		}
+
+		public static ICollection GetSubjectAlternativeNames(
+			X509Certificate cert)
+		{
+			Asn1OctetString extVal = cert.GetExtensionValue(X509Extensions.SubjectAlternativeName);
+
+			return GetAlternativeName(extVal);
+		}
+
+		private static ICollection GetAlternativeName(
+			Asn1OctetString extVal)
+		{
+			IList temp = Platform.CreateArrayList();
+
+			if (extVal != null)
+			{
+				try
+				{
+					Asn1Sequence seq = DerSequence.GetInstance(FromExtensionValue(extVal));
+
+					foreach (GeneralName genName in seq)
+					{
+                        IList list = Platform.CreateArrayList();
+						list.Add(genName.TagNo);
+
+						switch (genName.TagNo)
+						{
+							case GeneralName.EdiPartyName:
+							case GeneralName.X400Address:
+							case GeneralName.OtherName:
+								list.Add(genName.Name.ToAsn1Object());
+								break;
+							case GeneralName.DirectoryName:
+								list.Add(X509Name.GetInstance(genName.Name).ToString());
+								break;
+							case GeneralName.DnsName:
+							case GeneralName.Rfc822Name:
+							case GeneralName.UniformResourceIdentifier:
+								list.Add(((IAsn1String)genName.Name).GetString());
+								break;
+							case GeneralName.RegisteredID:
+								list.Add(DerObjectIdentifier.GetInstance(genName.Name).Id);
+								break;
+							case GeneralName.IPAddress:
+								list.Add(DerOctetString.GetInstance(genName.Name).GetOctets());
+								break;
+							default:
+								throw new IOException("Bad tag number: " + genName.TagNo);
+						}
+
+						temp.Add(list);
+					}
+				}
+				catch (Exception e)
+				{
+					throw new CertificateParsingException(e.Message);
+				}
+			}
+
+			return temp;
+		}
+	}
+}
diff --git a/Crypto/src/x509/store/IX509Selector.cs b/Crypto/src/x509/store/IX509Selector.cs
new file mode 100644
index 000000000..3f37fcd66
--- /dev/null
+++ b/Crypto/src/x509/store/IX509Selector.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	public interface IX509Selector
+#if !(SILVERLIGHT || PORTABLE)
+		: ICloneable
+#endif
+    {
+#if SILVERLIGHT || PORTABLE
+        object Clone();
+#endif
+        bool Match(object obj);
+	}
+}
diff --git a/Crypto/src/x509/store/IX509Store.cs b/Crypto/src/x509/store/IX509Store.cs
new file mode 100644
index 000000000..e5c3a462a
--- /dev/null
+++ b/Crypto/src/x509/store/IX509Store.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	public interface IX509Store
+	{
+//		void Init(IX509StoreParameters parameters);
+		ICollection GetMatches(IX509Selector selector);
+	}
+}
diff --git a/Crypto/src/x509/store/IX509StoreParameters.cs b/Crypto/src/x509/store/IX509StoreParameters.cs
new file mode 100644
index 000000000..aee3036c2
--- /dev/null
+++ b/Crypto/src/x509/store/IX509StoreParameters.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	public interface IX509StoreParameters
+	{
+	}
+}
diff --git a/Crypto/src/x509/store/NoSuchStoreException.cs b/Crypto/src/x509/store/NoSuchStoreException.cs
new file mode 100644
index 000000000..3df26de79
--- /dev/null
+++ b/Crypto/src/x509/store/NoSuchStoreException.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	public class NoSuchStoreException
+		: X509StoreException
+	{
+		public NoSuchStoreException()
+		{
+		}
+
+		public NoSuchStoreException(
+			string message)
+			: base(message)
+		{
+		}
+
+		public NoSuchStoreException(
+			string		message,
+			Exception	e)
+			: base(message, e)
+		{
+		}
+	}
+}
diff --git a/Crypto/src/x509/store/X509AttrCertStoreSelector.cs b/Crypto/src/x509/store/X509AttrCertStoreSelector.cs
new file mode 100644
index 000000000..9f1dc20d1
--- /dev/null
+++ b/Crypto/src/x509/store/X509AttrCertStoreSelector.cs
@@ -0,0 +1,376 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.X509.Extension;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	/**
+	* This class is an <code>Selector</code> like implementation to select
+	* attribute certificates from a given set of criteria.
+	*
+	* @see org.bouncycastle.x509.X509AttributeCertificate
+	* @see org.bouncycastle.x509.X509Store
+	*/
+	public class X509AttrCertStoreSelector
+		: IX509Selector
+	{
+		// TODO: name constraints???
+
+		private IX509AttributeCertificate attributeCert;
+		private DateTimeObject attributeCertificateValid;
+		private AttributeCertificateHolder holder;
+		private AttributeCertificateIssuer issuer;
+		private BigInteger serialNumber;
+		private ISet targetNames = new HashSet();
+		private ISet targetGroups = new HashSet();
+
+		public X509AttrCertStoreSelector()
+		{
+		}
+
+		private X509AttrCertStoreSelector(
+			X509AttrCertStoreSelector o)
+		{
+			this.attributeCert = o.attributeCert;
+			this.attributeCertificateValid = o.attributeCertificateValid;
+			this.holder = o.holder;
+			this.issuer = o.issuer;
+			this.serialNumber = o.serialNumber;
+			this.targetGroups = new HashSet(o.targetGroups);
+			this.targetNames = new HashSet(o.targetNames);
+		}
+
+		/// <summary>
+		/// Decides if the given attribute certificate should be selected.
+		/// </summary>
+		/// <param name="obj">The attribute certificate to be checked.</param>
+		/// <returns><code>true</code> if the object matches this selector.</returns>
+		public bool Match(
+			object obj)
+		{
+			if (obj == null)
+				throw new ArgumentNullException("obj");
+
+			IX509AttributeCertificate attrCert = obj as IX509AttributeCertificate;
+
+			if (attrCert == null)
+				return false;
+
+			if (this.attributeCert != null && !this.attributeCert.Equals(attrCert))
+				return false;
+
+			if (serialNumber != null && !attrCert.SerialNumber.Equals(serialNumber))
+				return false;
+
+			if (holder != null && !attrCert.Holder.Equals(holder))
+				return false;
+
+			if (issuer != null && !attrCert.Issuer.Equals(issuer))
+				return false;
+
+			if (attributeCertificateValid != null && !attrCert.IsValid(attributeCertificateValid.Value))
+				return false;
+
+			if (targetNames.Count > 0 || targetGroups.Count > 0)
+			{
+				Asn1OctetString targetInfoExt = attrCert.GetExtensionValue(
+					X509Extensions.TargetInformation);
+
+				if (targetInfoExt != null)
+				{
+					TargetInformation targetinfo;
+					try
+					{
+						targetinfo = TargetInformation.GetInstance(
+							X509ExtensionUtilities.FromExtensionValue(targetInfoExt));
+					}
+					catch (Exception)
+					{
+						return false;
+					}
+
+					Targets[] targetss = targetinfo.GetTargetsObjects();
+
+					if (targetNames.Count > 0)
+					{
+						bool found = false;
+
+						for (int i = 0; i < targetss.Length && !found; i++)
+						{
+							Target[] targets = targetss[i].GetTargets();
+
+							for (int j = 0; j < targets.Length; j++)
+							{
+								GeneralName targetName = targets[j].TargetName;
+
+								if (targetName != null && targetNames.Contains(targetName))
+								{
+									found = true;
+									break;
+								}
+							}
+						}
+						if (!found)
+						{
+							return false;
+						}
+					}
+
+					if (targetGroups.Count > 0)
+					{
+						bool found = false;
+
+						for (int i = 0; i < targetss.Length && !found; i++)
+						{
+							Target[] targets = targetss[i].GetTargets();
+
+							for (int j = 0; j < targets.Length; j++)
+							{
+								GeneralName targetGroup = targets[j].TargetGroup;
+
+								if (targetGroup != null && targetGroups.Contains(targetGroup))
+								{
+									found = true;
+									break;
+								}
+							}
+						}
+
+						if (!found)
+						{
+							return false;
+						}
+					}
+				}
+			}
+
+			return true;
+		}
+
+		public object Clone()
+		{
+			return new X509AttrCertStoreSelector(this);
+		}
+
+		/// <summary>The attribute certificate which must be matched.</summary>
+		/// <remarks>If <c>null</c> is given, any will do.</remarks>
+		public IX509AttributeCertificate AttributeCert
+		{
+			get { return attributeCert; }
+			set { this.attributeCert = value; }
+		}
+
+		[Obsolete("Use AttributeCertificateValid instead")]
+		public DateTimeObject AttribueCertificateValid
+		{
+			get { return attributeCertificateValid; }
+			set { this.attributeCertificateValid = value; }
+		}
+
+		/// <summary>The criteria for validity</summary>
+		/// <remarks>If <c>null</c> is given any will do.</remarks>
+		public DateTimeObject AttributeCertificateValid
+		{
+			get { return attributeCertificateValid; }
+			set { this.attributeCertificateValid = value; }
+		}
+
+		/// <summary>The holder.</summary>
+		/// <remarks>If <c>null</c> is given any will do.</remarks>
+		public AttributeCertificateHolder Holder
+		{
+			get { return holder; }
+			set { this.holder = value; }
+		}
+
+		/// <summary>The issuer.</summary>
+		/// <remarks>If <c>null</c> is given any will do.</remarks>
+		public AttributeCertificateIssuer Issuer
+		{
+			get { return issuer; }
+			set { this.issuer = value; }
+		}
+
+		/// <summary>The serial number.</summary>
+		/// <remarks>If <c>null</c> is given any will do.</remarks>
+		public BigInteger SerialNumber
+		{
+			get { return serialNumber; }
+			set { this.serialNumber = value; }
+		}
+
+		/**
+		* Adds a target name criterion for the attribute certificate to the target
+		* information extension criteria. The <code>X509AttributeCertificate</code>
+		* must contain at least one of the specified target names.
+		* <p>
+		* Each attribute certificate may contain a target information extension
+		* limiting the servers where this attribute certificate can be used. If
+		* this extension is not present, the attribute certificate is not targeted
+		* and may be accepted by any server.
+		* </p>
+		*
+		* @param name The name as a GeneralName (not <code>null</code>)
+		*/
+		public void AddTargetName(
+			GeneralName name)
+		{
+			targetNames.Add(name);
+		}
+
+		/**
+		* Adds a target name criterion for the attribute certificate to the target
+		* information extension criteria. The <code>X509AttributeCertificate</code>
+		* must contain at least one of the specified target names.
+		* <p>
+		* Each attribute certificate may contain a target information extension
+		* limiting the servers where this attribute certificate can be used. If
+		* this extension is not present, the attribute certificate is not targeted
+		* and may be accepted by any server.
+		* </p>
+		*
+		* @param name a byte array containing the name in ASN.1 DER encoded form of a GeneralName
+		* @throws IOException if a parsing error occurs.
+		*/
+		public void AddTargetName(
+			byte[] name)
+		{
+			AddTargetName(GeneralName.GetInstance(Asn1Object.FromByteArray(name)));
+		}
+
+		/**
+		* Adds a collection with target names criteria. If <code>null</code> is
+		* given any will do.
+		* <p>
+		* The collection consists of either GeneralName objects or byte[] arrays representing
+		* DER encoded GeneralName structures.
+		* </p>
+		* 
+		* @param names A collection of target names.
+		* @throws IOException if a parsing error occurs.
+		* @see #AddTargetName(byte[])
+		* @see #AddTargetName(GeneralName)
+		*/
+		public void SetTargetNames(
+			IEnumerable names)
+		{
+			targetNames = ExtractGeneralNames(names);
+		}
+
+		/**
+		* Gets the target names. The collection consists of <code>List</code>s
+		* made up of an <code>Integer</code> in the first entry and a DER encoded
+		* byte array or a <code>String</code> in the second entry.
+		* <p>The returned collection is immutable.</p>
+		* 
+		* @return The collection of target names
+		* @see #setTargetNames(Collection)
+		*/
+		public IEnumerable GetTargetNames()
+		{
+			return new EnumerableProxy(targetNames);
+		}
+
+		/**
+		* Adds a target group criterion for the attribute certificate to the target
+		* information extension criteria. The <code>X509AttributeCertificate</code>
+		* must contain at least one of the specified target groups.
+		* <p>
+		* Each attribute certificate may contain a target information extension
+		* limiting the servers where this attribute certificate can be used. If
+		* this extension is not present, the attribute certificate is not targeted
+		* and may be accepted by any server.
+		* </p>
+		*
+		* @param group The group as GeneralName form (not <code>null</code>)
+		*/
+		public void AddTargetGroup(
+			GeneralName group)
+		{
+			targetGroups.Add(group);
+		}
+
+		/**
+		* Adds a target group criterion for the attribute certificate to the target
+		* information extension criteria. The <code>X509AttributeCertificate</code>
+		* must contain at least one of the specified target groups.
+		* <p>
+		* Each attribute certificate may contain a target information extension
+		* limiting the servers where this attribute certificate can be used. If
+		* this extension is not present, the attribute certificate is not targeted
+		* and may be accepted by any server.
+		* </p>
+		*
+		* @param name a byte array containing the group in ASN.1 DER encoded form of a GeneralName
+		* @throws IOException if a parsing error occurs.
+		*/
+		public void AddTargetGroup(
+			byte[] name)
+		{
+			AddTargetGroup(GeneralName.GetInstance(Asn1Object.FromByteArray(name)));
+		}
+
+		/**
+		* Adds a collection with target groups criteria. If <code>null</code> is
+		* given any will do.
+		* <p>
+		* The collection consists of <code>GeneralName</code> objects or <code>byte[]</code>
+		* representing DER encoded GeneralNames.
+		* </p>
+		*
+		* @param names A collection of target groups.
+		* @throws IOException if a parsing error occurs.
+		* @see #AddTargetGroup(byte[])
+		* @see #AddTargetGroup(GeneralName)
+		*/
+		public void SetTargetGroups(
+			IEnumerable names)
+		{
+			targetGroups = ExtractGeneralNames(names);
+		}
+
+		/**
+		* Gets the target groups. The collection consists of <code>List</code>s
+		* made up of an <code>Integer</code> in the first entry and a DER encoded
+		* byte array or a <code>String</code> in the second entry.
+		* <p>The returned collection is immutable.</p>
+		*
+		* @return The collection of target groups.
+		* @see #setTargetGroups(Collection)
+		*/
+		public IEnumerable GetTargetGroups()
+		{
+			return new EnumerableProxy(targetGroups);
+		}
+
+		private ISet ExtractGeneralNames(
+			IEnumerable names)
+		{
+			ISet result = new HashSet();
+
+			if (names != null)
+			{
+				foreach (object o in names)
+				{
+					if (o is GeneralName)
+					{
+						result.Add(o);
+					}
+					else
+					{
+						result.Add(GeneralName.GetInstance(Asn1Object.FromByteArray((byte[]) o)));
+					}
+				}
+			}
+
+			return result;
+		}
+	}
+}
diff --git a/Crypto/src/x509/store/X509CertPairStoreSelector.cs b/Crypto/src/x509/store/X509CertPairStoreSelector.cs
new file mode 100644
index 000000000..2796971c7
--- /dev/null
+++ b/Crypto/src/x509/store/X509CertPairStoreSelector.cs
@@ -0,0 +1,92 @@
+using System;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	/// <remarks>
+	/// This class is an <code>IX509Selector</code> implementation to select
+	/// certificate pairs, which are e.g. used for cross certificates. The set of
+	/// criteria is given from two <code>X509CertStoreSelector</code> objects,
+	/// each of which, if present, must match the respective component of a pair.
+	/// </remarks>
+	public class X509CertPairStoreSelector
+		: IX509Selector
+	{
+		private static X509CertStoreSelector CloneSelector(
+			X509CertStoreSelector s)
+		{
+			return s == null ? null : (X509CertStoreSelector) s.Clone();
+		}
+
+		private X509CertificatePair certPair;
+		private X509CertStoreSelector forwardSelector;
+		private X509CertStoreSelector reverseSelector;
+
+		public X509CertPairStoreSelector()
+		{
+		}
+
+		private X509CertPairStoreSelector(
+			X509CertPairStoreSelector o)
+		{
+			this.certPair = o.CertPair;
+			this.forwardSelector = o.ForwardSelector;
+			this.reverseSelector = o.ReverseSelector;
+		}
+
+		/// <summary>The certificate pair which is used for testing on equality.</summary>
+		public X509CertificatePair CertPair
+		{
+			get { return certPair; }
+			set { this.certPair = value; }
+		}
+
+		/// <summary>The certificate selector for the forward part.</summary>
+		public X509CertStoreSelector ForwardSelector
+		{
+			get { return CloneSelector(forwardSelector); }
+			set { this.forwardSelector = CloneSelector(value); }
+		}
+
+		/// <summary>The certificate selector for the reverse part.</summary>
+		public X509CertStoreSelector ReverseSelector
+		{
+			get { return CloneSelector(reverseSelector); }
+			set { this.reverseSelector = CloneSelector(value); }
+		}
+
+		/// <summary>
+		/// Decides if the given certificate pair should be selected. If
+		/// <c>obj</c> is not a <code>X509CertificatePair</code>, this method
+		/// returns <code>false</code>.
+		/// </summary>
+		/// <param name="obj">The <code>X509CertificatePair</code> to be tested.</param>
+		/// <returns><code>true</code> if the object matches this selector.</returns>
+		public bool Match(
+			object obj)
+		{
+			if (obj == null)
+				throw new ArgumentNullException("obj");
+
+			X509CertificatePair pair = obj as X509CertificatePair;
+
+			if (pair == null)
+				return false;
+
+			if (certPair != null && !certPair.Equals(pair))
+				return false;
+
+			if (forwardSelector != null && !forwardSelector.Match(pair.Forward))
+				return false;
+
+			if (reverseSelector != null && !reverseSelector.Match(pair.Reverse))
+				return false;
+
+			return true;
+		}
+
+		public object Clone()
+		{
+			return new X509CertPairStoreSelector(this);
+		}
+	}
+}
diff --git a/Crypto/src/x509/store/X509CertStoreSelector.cs b/Crypto/src/x509/store/X509CertStoreSelector.cs
new file mode 100644
index 000000000..3874edf1d
--- /dev/null
+++ b/Crypto/src/x509/store/X509CertStoreSelector.cs
@@ -0,0 +1,337 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.X509.Extension;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	public class X509CertStoreSelector
+		: IX509Selector
+	{
+		// TODO Missing criteria?
+
+		private byte[] authorityKeyIdentifier;
+		private int basicConstraints = -1;
+		private X509Certificate certificate;
+		private DateTimeObject certificateValid;
+		private ISet extendedKeyUsage;
+		private X509Name issuer;
+		private bool[] keyUsage;
+		private ISet policy;
+		private DateTimeObject privateKeyValid;
+		private BigInteger serialNumber;
+		private X509Name subject;
+		private byte[] subjectKeyIdentifier;
+		private SubjectPublicKeyInfo subjectPublicKey;
+		private DerObjectIdentifier subjectPublicKeyAlgID;
+
+		public X509CertStoreSelector()
+		{
+		}
+
+		public X509CertStoreSelector(
+			X509CertStoreSelector o)
+		{
+			this.authorityKeyIdentifier = o.AuthorityKeyIdentifier;
+			this.basicConstraints = o.BasicConstraints;
+			this.certificate = o.Certificate;
+			this.certificateValid = o.CertificateValid;
+			this.extendedKeyUsage = o.ExtendedKeyUsage;
+			this.issuer = o.Issuer;
+			this.keyUsage = o.KeyUsage;
+			this.policy = o.Policy;
+			this.privateKeyValid = o.PrivateKeyValid;
+			this.serialNumber = o.SerialNumber;
+			this.subject = o.Subject;
+			this.subjectKeyIdentifier = o.SubjectKeyIdentifier;
+			this.subjectPublicKey = o.SubjectPublicKey;
+			this.subjectPublicKeyAlgID = o.SubjectPublicKeyAlgID;
+		}
+
+		public virtual object Clone()
+		{
+			return new X509CertStoreSelector(this);
+		}
+
+		public byte[] AuthorityKeyIdentifier
+		{
+			get { return Arrays.Clone(authorityKeyIdentifier); }
+			set { authorityKeyIdentifier = Arrays.Clone(value); }
+		}
+
+		public int BasicConstraints
+		{
+			get { return basicConstraints; }
+			set
+			{
+				if (value < -2)
+					throw new ArgumentException("value can't be less than -2", "value");
+
+				basicConstraints = value;
+			}
+		}
+
+		public X509Certificate Certificate
+		{
+			get { return certificate; }
+			set { this.certificate = value; }
+		}
+
+		public DateTimeObject CertificateValid
+		{
+			get { return certificateValid; }
+			set { certificateValid = value; }
+		}
+
+		public ISet ExtendedKeyUsage
+		{
+			get { return CopySet(extendedKeyUsage); }
+			set { extendedKeyUsage = CopySet(value); }
+		}
+
+		public X509Name Issuer
+		{
+			get { return issuer; }
+			set { issuer = value; }
+		}
+
+		[Obsolete("Avoid working with X509Name objects in string form")]
+		public string IssuerAsString
+		{
+			get { return issuer != null ? issuer.ToString() : null; }
+		}
+
+		public bool[] KeyUsage
+		{
+			get { return CopyBoolArray(keyUsage); }
+			set { keyUsage = CopyBoolArray(value); }
+		}
+
+		/// <summary>
+		/// An <code>ISet</code> of <code>DerObjectIdentifier</code> objects.
+		/// </summary>
+		public ISet Policy
+		{
+			get { return CopySet(policy); }
+			set { policy = CopySet(value); }
+		}
+
+		public DateTimeObject PrivateKeyValid
+		{
+			get { return privateKeyValid; }
+			set { privateKeyValid = value; }
+		}
+
+		public BigInteger SerialNumber
+		{
+			get { return serialNumber; }
+			set { serialNumber = value; }
+		}
+
+		public X509Name Subject
+		{
+			get { return subject; }
+			set { subject = value; }
+		}
+
+		public string SubjectAsString
+		{
+			get { return subject != null ? subject.ToString() : null; }
+		}
+
+		public byte[] SubjectKeyIdentifier
+		{
+			get { return Arrays.Clone(subjectKeyIdentifier); }
+			set { subjectKeyIdentifier = Arrays.Clone(value); }
+		}
+
+		public SubjectPublicKeyInfo SubjectPublicKey
+		{
+			get { return subjectPublicKey; }
+			set { subjectPublicKey = value; }
+		}
+
+		public DerObjectIdentifier SubjectPublicKeyAlgID
+		{
+			get { return subjectPublicKeyAlgID; }
+			set { subjectPublicKeyAlgID = value; }
+		}
+
+		public virtual bool Match(
+			object obj)
+		{
+			X509Certificate c = obj as X509Certificate;
+
+			if (c == null)
+				return false;
+
+			if (!MatchExtension(authorityKeyIdentifier, c, X509Extensions.AuthorityKeyIdentifier))
+				return false;
+
+			if (basicConstraints != -1)
+			{
+				int bc = c.GetBasicConstraints();
+
+				if (basicConstraints == -2)
+				{
+					if (bc != -1)
+						return false;
+				}
+				else
+				{
+					if (bc < basicConstraints)
+						return false;
+				}
+			}
+
+			if (certificate != null && !certificate.Equals(c))
+				return false;
+
+			if (certificateValid != null && !c.IsValid(certificateValid.Value))
+				return false;
+
+			if (extendedKeyUsage != null)
+			{
+				IList eku = c.GetExtendedKeyUsage();
+
+				// Note: if no extended key usage set, all key purposes are implicitly allowed
+
+				if (eku != null)
+				{
+					foreach (DerObjectIdentifier oid in extendedKeyUsage)
+					{
+						if (!eku.Contains(oid.Id))
+							return false;
+					}
+				}
+			}
+
+			if (issuer != null && !issuer.Equivalent(c.IssuerDN, true))
+				return false;
+
+			if (keyUsage != null)
+			{
+				bool[] ku = c.GetKeyUsage();
+
+				// Note: if no key usage set, all key purposes are implicitly allowed
+
+				if (ku != null)
+				{
+					for (int i = 0; i < 9; ++i)
+					{
+						if (keyUsage[i] && !ku[i])
+							return false;
+					}
+				}
+			}
+
+			if (policy != null)
+			{
+				Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.CertificatePolicies);
+				if (extVal == null)
+					return false;
+
+				Asn1Sequence certPolicies = Asn1Sequence.GetInstance(
+					X509ExtensionUtilities.FromExtensionValue(extVal));
+
+				if (policy.Count < 1 && certPolicies.Count < 1)
+					return false;
+
+				bool found = false;
+				foreach (PolicyInformation pi in certPolicies)
+				{
+					if (policy.Contains(pi.PolicyIdentifier))
+					{
+						found = true;
+						break;
+					}
+				}
+
+				if (!found)
+					return false;
+			}
+
+			if (privateKeyValid != null)
+			{
+				Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.PrivateKeyUsagePeriod);
+				if (extVal == null)
+					return false;
+
+				PrivateKeyUsagePeriod pkup = PrivateKeyUsagePeriod.GetInstance(
+					X509ExtensionUtilities.FromExtensionValue(extVal));
+
+				DateTime dt = privateKeyValid.Value;
+				DateTime notAfter = pkup.NotAfter.ToDateTime();
+				DateTime notBefore = pkup.NotBefore.ToDateTime();
+
+				if (dt.CompareTo(notAfter) > 0 || dt.CompareTo(notBefore) < 0)
+					return false;
+			}
+
+			if (serialNumber != null && !serialNumber.Equals(c.SerialNumber))
+				return false;
+
+			if (subject != null && !subject.Equivalent(c.SubjectDN, true))
+				return false;
+
+			if (!MatchExtension(subjectKeyIdentifier, c, X509Extensions.SubjectKeyIdentifier))
+				return false;
+
+			if (subjectPublicKey != null && !subjectPublicKey.Equals(GetSubjectPublicKey(c)))
+				return false;
+
+			if (subjectPublicKeyAlgID != null
+				&& !subjectPublicKeyAlgID.Equals(GetSubjectPublicKey(c).AlgorithmID))
+				return false;
+
+			return true;
+		}
+
+		internal static bool IssuersMatch(
+			X509Name	a,
+			X509Name	b)
+		{
+			return a == null ? b == null : a.Equivalent(b, true);
+		}
+
+		private static bool[] CopyBoolArray(
+			bool[] b)
+		{
+			return b == null ? null : (bool[]) b.Clone();
+		}
+
+		private static ISet CopySet(
+			ISet s)
+		{
+			return s == null ? null : new HashSet(s);
+		}
+
+		private static SubjectPublicKeyInfo GetSubjectPublicKey(
+			X509Certificate c)
+		{
+			return SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(c.GetPublicKey());
+		}
+
+		private static bool MatchExtension(
+			byte[]				b,
+			X509Certificate		c,
+			DerObjectIdentifier	oid)
+		{
+			if (b == null)
+				return true;
+
+			Asn1OctetString extVal = c.GetExtensionValue(oid);
+
+			if (extVal == null)
+				return false;
+
+			return Arrays.AreEqual(b, extVal.GetOctets());
+		}
+	}
+}
diff --git a/Crypto/src/x509/store/X509CollectionStore.cs b/Crypto/src/x509/store/X509CollectionStore.cs
new file mode 100644
index 000000000..92173140b
--- /dev/null
+++ b/Crypto/src/x509/store/X509CollectionStore.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	/**
+	 * A simple collection backed store.
+	 */
+	internal class X509CollectionStore
+		: IX509Store
+	{
+		private ICollection _local;
+
+		/**
+		 * Basic constructor.
+		 *
+		 * @param collection - initial contents for the store, this is copied.
+		 */
+		internal X509CollectionStore(
+			ICollection collection)
+		{
+			_local = Platform.CreateArrayList(collection);
+		}
+
+		/**
+		 * Return the matches in the collection for the passed in selector.
+		 *
+		 * @param selector the selector to match against.
+		 * @return a possibly empty collection of matching objects.
+		 */
+		public ICollection GetMatches(
+			IX509Selector selector)
+		{
+			if (selector == null)
+			{
+                return Platform.CreateArrayList(_local);
+			}
+
+            IList result = Platform.CreateArrayList();
+			foreach (object obj in _local)
+			{
+				if (selector.Match(obj))
+					result.Add(obj);
+			}
+
+			return result;
+		}
+	}
+}
diff --git a/Crypto/src/x509/store/X509CollectionStoreParameters.cs b/Crypto/src/x509/store/X509CollectionStoreParameters.cs
new file mode 100644
index 000000000..7fd047a47
--- /dev/null
+++ b/Crypto/src/x509/store/X509CollectionStoreParameters.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	/// <remarks>This class contains a collection for collection based <code>X509Store</code>s.</remarks>
+	public class X509CollectionStoreParameters
+		: IX509StoreParameters
+	{
+		private readonly IList collection;
+
+		/// <summary>
+		/// Constructor.
+		/// <p>
+		/// The collection is copied.
+		/// </p>
+		/// </summary>
+		/// <param name="collection">The collection containing X.509 object types.</param>
+		/// <exception cref="ArgumentNullException">If collection is null.</exception>
+		public X509CollectionStoreParameters(
+			ICollection collection)
+		{
+			if (collection == null)
+				throw new ArgumentNullException("collection");
+
+			this.collection = Platform.CreateArrayList(collection);
+		}
+
+		// TODO Do we need to be able to Clone() these, and should it really be shallow?
+//		/**
+//		* Returns a shallow clone. The returned contents are not copied, so adding
+//		* or removing objects will effect this.
+//		*
+//		* @return a shallow clone.
+//		*/
+//		public object Clone()
+//		{
+//			return new X509CollectionStoreParameters(collection);
+//		}
+
+		/// <summary>Returns a copy of the <code>ICollection</code>.</summary>
+		public ICollection GetCollection()
+		{
+			return Platform.CreateArrayList(collection);
+		}
+
+		/// <summary>Returns a formatted string describing the parameters.</summary>
+		public override string ToString()
+		{
+			StringBuilder sb = new StringBuilder();
+			sb.Append("X509CollectionStoreParameters: [\n");
+			sb.Append("  collection: " + collection + "\n");
+			sb.Append("]");
+			return sb.ToString();
+		}
+	}
+}
diff --git a/Crypto/src/x509/store/X509CrlStoreSelector.cs b/Crypto/src/x509/store/X509CrlStoreSelector.cs
new file mode 100644
index 000000000..c4b0062c1
--- /dev/null
+++ b/Crypto/src/x509/store/X509CrlStoreSelector.cs
@@ -0,0 +1,283 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Extension;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	public class X509CrlStoreSelector
+		: IX509Selector
+	{
+		// TODO Missing criteria?
+
+		private X509Certificate certificateChecking;
+		private DateTimeObject dateAndTime;
+		private ICollection issuers;
+		private BigInteger maxCrlNumber;
+		private BigInteger minCrlNumber;
+
+		private IX509AttributeCertificate attrCertChecking;
+		private bool completeCrlEnabled;
+		private bool deltaCrlIndicatorEnabled;
+		private byte[] issuingDistributionPoint;
+		private bool issuingDistributionPointEnabled;
+		private BigInteger maxBaseCrlNumber;
+
+		public X509CrlStoreSelector()
+		{
+		}
+
+		public X509CrlStoreSelector(
+			X509CrlStoreSelector o)
+		{
+			this.certificateChecking = o.CertificateChecking;
+			this.dateAndTime = o.DateAndTime;
+			this.issuers = o.Issuers;
+			this.maxCrlNumber = o.MaxCrlNumber;
+			this.minCrlNumber = o.MinCrlNumber;
+
+			this.deltaCrlIndicatorEnabled = o.DeltaCrlIndicatorEnabled;
+			this.completeCrlEnabled = o.CompleteCrlEnabled;
+			this.maxBaseCrlNumber = o.MaxBaseCrlNumber;
+			this.attrCertChecking = o.AttrCertChecking;
+			this.issuingDistributionPointEnabled = o.IssuingDistributionPointEnabled;
+			this.issuingDistributionPoint = o.IssuingDistributionPoint;
+		}
+
+		public virtual object Clone()
+		{
+			return new X509CrlStoreSelector(this);
+		}
+
+		public X509Certificate CertificateChecking
+		{
+			get { return certificateChecking; }
+			set { certificateChecking = value; }
+		}
+
+		public DateTimeObject DateAndTime
+		{
+			get { return dateAndTime; }
+			set { dateAndTime = value; }
+		}
+
+		/// <summary>
+		/// An <code>ICollection</code> of <code>X509Name</code> objects
+		/// </summary>
+		public ICollection Issuers
+		{
+			get { return Platform.CreateArrayList(issuers); }
+            set { issuers = Platform.CreateArrayList(value); }
+		}
+
+		public BigInteger MaxCrlNumber
+		{
+			get { return maxCrlNumber; }
+			set { maxCrlNumber = value; }
+		}
+
+		public BigInteger MinCrlNumber
+		{
+			get { return minCrlNumber; }
+			set { minCrlNumber = value; }
+		}
+
+		/**
+		 * The attribute certificate being checked. This is not a criterion.
+		 * Rather, it is optional information that may help a {@link X509Store} find
+		 * CRLs that would be relevant when checking revocation for the specified
+		 * attribute certificate. If <code>null</code> is specified, then no such
+		 * optional information is provided.
+		 *
+		 * @param attrCert the <code>IX509AttributeCertificate</code> being checked (or
+		 *             <code>null</code>)
+		 * @see #getAttrCertificateChecking()
+		 */
+		public IX509AttributeCertificate AttrCertChecking
+		{
+			get { return attrCertChecking; }
+			set { this.attrCertChecking = value; }
+		}
+
+		/**
+		 * If <code>true</code> only complete CRLs are returned. Defaults to
+		 * <code>false</code>.
+		 *
+		 * @return <code>true</code> if only complete CRLs are returned.
+		 */
+		public bool CompleteCrlEnabled
+		{
+			get { return completeCrlEnabled; }
+			set { this.completeCrlEnabled = value; }
+		}
+
+		/**
+		 * Returns if this selector must match CRLs with the delta CRL indicator
+		 * extension set. Defaults to <code>false</code>.
+		 *
+		 * @return Returns <code>true</code> if only CRLs with the delta CRL
+		 *         indicator extension are selected.
+		 */
+		public bool DeltaCrlIndicatorEnabled
+		{
+			get { return deltaCrlIndicatorEnabled; }
+			set { this.deltaCrlIndicatorEnabled = value; }
+		}
+
+		/**
+		 * The issuing distribution point.
+		 * <p>
+		 * The issuing distribution point extension is a CRL extension which
+		 * identifies the scope and the distribution point of a CRL. The scope
+		 * contains among others information about revocation reasons contained in
+		 * the CRL. Delta CRLs and complete CRLs must have matching issuing
+		 * distribution points.</p>
+		 * <p>
+		 * The byte array is cloned to protect against subsequent modifications.</p>
+		 * <p>
+		 * You must also enable or disable this criteria with
+		 * {@link #setIssuingDistributionPointEnabled(bool)}.</p>
+		 *
+		 * @param issuingDistributionPoint The issuing distribution point to set.
+		 *                                 This is the DER encoded OCTET STRING extension value.
+		 * @see #getIssuingDistributionPoint()
+		 */
+		public byte[] IssuingDistributionPoint
+		{
+			get { return Arrays.Clone(issuingDistributionPoint); }
+			set { this.issuingDistributionPoint = Arrays.Clone(value); }
+		}
+
+		/**
+		 * Whether the issuing distribution point criteria should be applied.
+		 * Defaults to <code>false</code>.
+		 * <p>
+		 * You may also set the issuing distribution point criteria if not a missing
+		 * issuing distribution point should be assumed.</p>
+		 *
+		 * @return Returns if the issuing distribution point check is enabled.
+		 */
+		public bool IssuingDistributionPointEnabled
+		{
+			get { return issuingDistributionPointEnabled; }
+			set { this.issuingDistributionPointEnabled = value; }
+		}
+
+		/**
+		 * The maximum base CRL number. Defaults to <code>null</code>.
+		 *
+		 * @return Returns the maximum base CRL number.
+		 * @see #setMaxBaseCRLNumber(BigInteger)
+		 */
+		public BigInteger MaxBaseCrlNumber
+		{
+			get { return maxBaseCrlNumber; }
+			set { this.maxBaseCrlNumber = value; }
+		}
+
+		public virtual bool Match(
+			object obj)
+		{
+			X509Crl c = obj as X509Crl;
+
+			if (c == null)
+				return false;
+
+			if (dateAndTime != null)
+			{
+				DateTime dt = dateAndTime.Value;
+				DateTime tu = c.ThisUpdate;
+				DateTimeObject nu = c.NextUpdate;
+
+				if (dt.CompareTo(tu) < 0 || nu == null || dt.CompareTo(nu.Value) >= 0)
+					return false;
+			}
+
+			if (issuers != null)
+			{
+				X509Name i = c.IssuerDN;
+
+				bool found = false;
+
+				foreach (X509Name issuer in issuers)
+				{
+					if (issuer.Equivalent(i, true))
+					{
+						found = true;
+						break;
+					}
+				}
+
+				if (!found)
+					return false;
+			}
+
+			if (maxCrlNumber != null || minCrlNumber != null)
+			{
+				Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.CrlNumber);
+				if (extVal == null)
+					return false;
+
+				BigInteger cn = CrlNumber.GetInstance(
+					X509ExtensionUtilities.FromExtensionValue(extVal)).PositiveValue;
+
+				if (maxCrlNumber != null && cn.CompareTo(maxCrlNumber) > 0)
+					return false;
+
+				if (minCrlNumber != null && cn.CompareTo(minCrlNumber) < 0)
+					return false;
+			}
+
+			DerInteger dci = null;
+			try
+			{
+				Asn1OctetString bytes = c.GetExtensionValue(X509Extensions.DeltaCrlIndicator);
+				if (bytes != null)
+				{
+					dci = DerInteger.GetInstance(X509ExtensionUtilities.FromExtensionValue(bytes));
+				}
+			}
+			catch (Exception)
+			{
+				return false;
+			}
+
+			if (dci == null)
+			{
+				if (DeltaCrlIndicatorEnabled)
+					return false;
+			}
+			else
+			{
+				if (CompleteCrlEnabled)
+					return false;
+
+				if (maxBaseCrlNumber != null && dci.PositiveValue.CompareTo(maxBaseCrlNumber) > 0)
+					return false;
+			}
+
+			if (issuingDistributionPointEnabled)
+			{
+				Asn1OctetString idp = c.GetExtensionValue(X509Extensions.IssuingDistributionPoint);
+				if (issuingDistributionPoint == null)
+				{
+					if (idp != null)
+						return false;
+				}
+				else
+				{
+					if (!Arrays.AreEqual(idp.GetOctets(), issuingDistributionPoint))
+						return false;
+				}
+			}
+
+			return true;
+		}
+	}
+}
diff --git a/Crypto/src/x509/store/X509StoreException.cs b/Crypto/src/x509/store/X509StoreException.cs
new file mode 100644
index 000000000..f6a5e235f
--- /dev/null
+++ b/Crypto/src/x509/store/X509StoreException.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	public class X509StoreException
+		: Exception
+	{
+		public X509StoreException()
+		{
+		}
+
+		public X509StoreException(
+			string message)
+			: base(message)
+		{
+		}
+
+		public X509StoreException(
+			string		message,
+			Exception	e)
+			: base(message, e)
+		{
+		}
+	}
+}
diff --git a/Crypto/src/x509/store/X509StoreFactory.cs b/Crypto/src/x509/store/X509StoreFactory.cs
new file mode 100644
index 000000000..322639991
--- /dev/null
+++ b/Crypto/src/x509/store/X509StoreFactory.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections;
+using System.Globalization;
+
+namespace Org.BouncyCastle.X509.Store
+{
+	public sealed class X509StoreFactory
+	{
+		private X509StoreFactory()
+		{
+		}
+
+		public static IX509Store Create(
+			string					type,
+			IX509StoreParameters	parameters)
+		{
+			if (type == null)
+				throw new ArgumentNullException("type");
+
+			string[] parts = type.ToUpperInvariant().Split('/');
+
+			if (parts.Length < 2)
+				throw new ArgumentException("type");
+
+			if (parts[1] != "COLLECTION")
+				throw new NoSuchStoreException("X.509 store type '" + type + "' not available.");
+
+			X509CollectionStoreParameters p = (X509CollectionStoreParameters) parameters;
+			ICollection coll = p.GetCollection();
+
+			switch (parts[0])
+			{
+				case "ATTRIBUTECERTIFICATE":
+					checkCorrectType(coll, typeof(IX509AttributeCertificate));
+					break;
+				case "CERTIFICATE":
+					checkCorrectType(coll, typeof(X509Certificate));
+					break;
+				case "CERTIFICATEPAIR":
+					checkCorrectType(coll, typeof(X509CertificatePair));
+					break;
+				case "CRL":
+					checkCorrectType(coll, typeof(X509Crl));
+					break;
+				default:
+					throw new NoSuchStoreException("X.509 store type '" + type + "' not available.");
+			}
+
+			return new X509CollectionStore(coll);
+		}
+
+		private static void checkCorrectType(ICollection coll, Type t)
+		{
+			foreach (object o in coll)
+			{
+				if (!t.IsInstanceOfType(o))
+					throw new InvalidCastException("Can't cast object to type: " + t.FullName);
+			}
+		}
+	}
+}