summary refs log tree commit diff
path: root/crypto/src/pkix
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/pkix')
-rw-r--r--crypto/src/pkix/CertStatus.cs35
-rw-r--r--crypto/src/pkix/PkixAttrCertChecker.cs57
-rw-r--r--crypto/src/pkix/PkixAttrCertPathBuilder.cs215
-rw-r--r--crypto/src/pkix/PkixAttrCertPathValidator.cs76
-rw-r--r--crypto/src/pkix/PkixBuilderParameters.cs140
-rw-r--r--crypto/src/pkix/PkixCertPath.cs460
-rw-r--r--crypto/src/pkix/PkixCertPathBuilder.cs205
-rw-r--r--crypto/src/pkix/PkixCertPathBuilderException.cs22
-rw-r--r--crypto/src/pkix/PkixCertPathBuilderResult.cs45
-rw-r--r--crypto/src/pkix/PkixCertPathChecker.cs101
-rw-r--r--crypto/src/pkix/PkixCertPathValidator.cs420
-rw-r--r--crypto/src/pkix/PkixCertPathValidatorException.cs221
-rw-r--r--crypto/src/pkix/PkixCertPathValidatorResult.cs69
-rw-r--r--crypto/src/pkix/PkixCertPathValidatorUtilities.cs1194
-rw-r--r--crypto/src/pkix/PkixCrlUtilities.cs114
-rw-r--r--crypto/src/pkix/PkixNameConstraintValidator.cs1937
-rw-r--r--crypto/src/pkix/PkixNameConstraintValidatorException.cs16
-rw-r--r--crypto/src/pkix/PkixParameters.cs893
-rw-r--r--crypto/src/pkix/PkixPolicyNode.cs158
-rw-r--r--crypto/src/pkix/ReasonsMask.cs96
-rw-r--r--crypto/src/pkix/Rfc3280CertPathUtilities.cs2448
-rw-r--r--crypto/src/pkix/Rfc3281CertPathUtilities.cs608
-rw-r--r--crypto/src/pkix/TrustAnchor.cs259
23 files changed, 9789 insertions, 0 deletions
diff --git a/crypto/src/pkix/CertStatus.cs b/crypto/src/pkix/CertStatus.cs
new file mode 100644
index 000000000..4f40b7bc6
--- /dev/null
+++ b/crypto/src/pkix/CertStatus.cs
@@ -0,0 +1,35 @@
+using System;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Pkix
+{
+    public class CertStatus
+    {
+        public const int Unrevoked = 11;
+
+        public const int Undetermined = 12;
+
+        private int status = Unrevoked;
+
+        DateTimeObject revocationDate = null;
+
+        /// <summary>
+        /// Returns the revocationDate.
+        /// </summary>
+         public DateTimeObject RevocationDate
+        {
+            get { return revocationDate; }
+            set { this.revocationDate = value; }
+        }
+
+		/// <summary>
+        /// Returns the certStatus.
+        /// </summary>
+        public int Status
+        {
+            get { return status; }
+            set { this.status = value; }
+        }
+    }
+}
diff --git a/crypto/src/pkix/PkixAttrCertChecker.cs b/crypto/src/pkix/PkixAttrCertChecker.cs
new file mode 100644
index 000000000..a6eab8480
--- /dev/null
+++ b/crypto/src/pkix/PkixAttrCertChecker.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Pkix
+{
+	public abstract class PkixAttrCertChecker
+	{
+		/**
+		 * Returns an immutable <code>Set</code> of X.509 attribute certificate
+		 * extensions that this <code>PkixAttrCertChecker</code> supports or
+		 * <code>null</code> if no extensions are supported.
+		 * <p>
+		 * Each element of the set is a <code>String</code> representing the
+		 * Object Identifier (OID) of the X.509 extension that is supported.
+		 * </p>
+		 * <p>
+		 * All X.509 attribute certificate extensions that a
+		 * <code>PkixAttrCertChecker</code> might possibly be able to process
+		 * should be included in the set.
+		 * </p>
+		 * 
+		 * @return an immutable <code>Set</code> of X.509 extension OIDs (in
+		 *         <code>String</code> format) supported by this
+		 *         <code>PkixAttrCertChecker</code>, or <code>null</code> if no
+		 *         extensions are supported
+		 */
+		public abstract ISet GetSupportedExtensions();
+
+		/**
+		* Performs checks on the specified attribute certificate. Every handled
+		* extension is rmeoved from the <code>unresolvedCritExts</code>
+		* collection.
+		* 
+		* @param attrCert The attribute certificate to be checked.
+		* @param certPath The certificate path which belongs to the attribute
+		*            certificate issuer public key certificate.
+		* @param holderCertPath The certificate path which belongs to the holder
+		*            certificate.
+		* @param unresolvedCritExts a <code>Collection</code> of OID strings
+		*            representing the current set of unresolved critical extensions
+		* @throws CertPathValidatorException if the specified attribute certificate
+		*             does not pass the check.
+		*/
+		public abstract void Check(IX509AttributeCertificate attrCert, PkixCertPath certPath,
+			PkixCertPath holderCertPath, ICollection unresolvedCritExts);
+
+		/**
+		* Returns a clone of this object.
+		* 
+		* @return a copy of this <code>PkixAttrCertChecker</code>
+		*/
+		public abstract PkixAttrCertChecker Clone();
+	}
+}
diff --git a/crypto/src/pkix/PkixAttrCertPathBuilder.cs b/crypto/src/pkix/PkixAttrCertPathBuilder.cs
new file mode 100644
index 000000000..646cc5db5
--- /dev/null
+++ b/crypto/src/pkix/PkixAttrCertPathBuilder.cs
@@ -0,0 +1,215 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Pkix
+{
+	public class PkixAttrCertPathBuilder
+	{
+		/**
+		* Build and validate a CertPath using the given parameter.
+		*
+		* @param params PKIXBuilderParameters object containing all information to
+		*            build the CertPath
+		*/
+		public virtual PkixCertPathBuilderResult Build(
+			PkixBuilderParameters pkixParams)
+		{
+			// search target certificates
+
+			IX509Selector certSelect = pkixParams.GetTargetConstraints();
+			if (!(certSelect is X509AttrCertStoreSelector))
+			{
+				throw new PkixCertPathBuilderException(
+					"TargetConstraints must be an instance of "
+					+ typeof(X509AttrCertStoreSelector).FullName
+					+ " for "
+					+ typeof(PkixAttrCertPathBuilder).FullName + " class.");
+			}
+
+			ICollection targets;
+			try
+			{
+				targets = PkixCertPathValidatorUtilities.FindCertificates(
+					(X509AttrCertStoreSelector)certSelect, pkixParams.GetStores());
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathBuilderException("Error finding target attribute certificate.", e);
+			}
+
+			if (targets.Count == 0)
+			{
+				throw new PkixCertPathBuilderException(
+					"No attribute certificate found matching targetContraints.");
+			}
+
+			PkixCertPathBuilderResult result = null;
+
+			// check all potential target certificates
+			foreach (IX509AttributeCertificate cert in targets)
+			{
+				X509CertStoreSelector selector = new X509CertStoreSelector();
+				X509Name[] principals = cert.Issuer.GetPrincipals();
+				ISet issuers = new HashSet();
+				for (int i = 0; i < principals.Length; i++)
+				{
+					try
+					{
+						selector.Subject = principals[i];
+
+						issuers.AddAll(PkixCertPathValidatorUtilities.FindCertificates(selector, pkixParams.GetStores()));
+					}
+					catch (Exception e)
+					{
+						throw new PkixCertPathBuilderException(
+							"Public key certificate for attribute certificate cannot be searched.",
+							e);
+					}
+				}
+
+				if (issuers.IsEmpty)
+					throw new PkixCertPathBuilderException("Public key certificate for attribute certificate cannot be found.");
+
+                IList certPathList = Platform.CreateArrayList();
+
+				foreach (X509Certificate issuer in issuers)
+				{
+					result = Build(cert, issuer, pkixParams, certPathList);
+
+					if (result != null)
+						break;
+				}
+
+				if (result != null)
+					break;
+			}
+
+			if (result == null && certPathException != null)
+			{
+				throw new PkixCertPathBuilderException(
+					"Possible certificate chain could not be validated.",
+					certPathException);
+			}
+
+			if (result == null && certPathException == null)
+			{
+				throw new PkixCertPathBuilderException(
+					"Unable to find certificate chain.");
+			}
+
+			return result;
+		}
+
+		private Exception certPathException;
+
+		private PkixCertPathBuilderResult Build(
+			IX509AttributeCertificate	attrCert,
+			X509Certificate				tbvCert,
+			PkixBuilderParameters		pkixParams,
+			IList						tbvPath)
+		{
+			// If tbvCert is readily present in tbvPath, it indicates having run
+			// into a cycle in the
+			// PKI graph.
+			if (tbvPath.Contains(tbvCert))
+				return null;
+
+			// step out, the certificate is not allowed to appear in a certification
+			// chain
+			if (pkixParams.GetExcludedCerts().Contains(tbvCert))
+				return null;
+
+			// test if certificate path exceeds maximum length
+			if (pkixParams.MaxPathLength != -1)
+			{
+				if (tbvPath.Count - 1 > pkixParams.MaxPathLength)
+					return null;
+			}
+
+			tbvPath.Add(tbvCert);
+
+			PkixCertPathBuilderResult builderResult = null;
+
+//			X509CertificateParser certParser = new X509CertificateParser();
+			PkixAttrCertPathValidator validator = new PkixAttrCertPathValidator();
+
+			try
+			{
+				// check whether the issuer of <tbvCert> is a TrustAnchor
+				if (PkixCertPathValidatorUtilities.FindTrustAnchor(tbvCert, pkixParams.GetTrustAnchors()) != null)
+				{
+					PkixCertPath certPath = new PkixCertPath(tbvPath);
+					PkixCertPathValidatorResult result;
+
+					try
+					{
+						result = validator.Validate(certPath, pkixParams);
+					}
+					catch (Exception e)
+					{
+						throw new Exception("Certification path could not be validated.", e);
+					}
+
+					return new PkixCertPathBuilderResult(certPath, result.TrustAnchor,
+						result.PolicyTree, result.SubjectPublicKey);
+				}
+				else
+				{
+					// add additional X.509 stores from locations in certificate
+					try
+					{
+						PkixCertPathValidatorUtilities.AddAdditionalStoresFromAltNames(tbvCert, pkixParams);
+					}
+					catch (CertificateParsingException e)
+					{
+						throw new Exception("No additional X.509 stores can be added from certificate locations.", e);
+					}
+
+					// try to get the issuer certificate from one of the stores
+					ISet issuers = new HashSet();
+					try
+					{
+						issuers.AddAll(PkixCertPathValidatorUtilities.FindIssuerCerts(tbvCert, pkixParams));
+					}
+					catch (Exception e)
+					{
+						throw new Exception("Cannot find issuer certificate for certificate in certification path.", e);
+					}
+
+					if (issuers.IsEmpty)
+						throw new Exception("No issuer certificate for certificate in certification path found.");
+
+					foreach (X509Certificate issuer in issuers)
+					{
+						// if untrusted self signed certificate continue
+						if (PkixCertPathValidatorUtilities.IsSelfIssued(issuer))
+							continue;
+
+						builderResult = Build(attrCert, issuer, pkixParams, tbvPath);
+
+						if (builderResult != null)
+							break;
+					}
+				}
+			}
+			catch (Exception e)
+			{
+				certPathException = new Exception("No valid certification path could be build.", e);
+			}
+
+			if (builderResult == null)
+			{
+				tbvPath.Remove(tbvCert);
+			}
+
+			return builderResult;
+		}
+	}
+}
diff --git a/crypto/src/pkix/PkixAttrCertPathValidator.cs b/crypto/src/pkix/PkixAttrCertPathValidator.cs
new file mode 100644
index 000000000..5f53bcde6
--- /dev/null
+++ b/crypto/src/pkix/PkixAttrCertPathValidator.cs
@@ -0,0 +1,76 @@
+using System;
+
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/**
+	* CertPathValidatorSpi implementation for X.509 Attribute Certificates la RFC 3281.
+	* 
+	* @see org.bouncycastle.x509.ExtendedPkixParameters
+	*/
+	public class PkixAttrCertPathValidator
+	//    extends CertPathValidatorSpi
+	{
+		/**
+		* Validates an attribute certificate with the given certificate path.
+		* 
+		* <p>
+		* <code>params</code> must be an instance of
+		* <code>ExtendedPkixParameters</code>.
+		* </p><p>
+		* The target constraints in the <code>params</code> must be an
+		* <code>X509AttrCertStoreSelector</code> with at least the attribute
+		* certificate criterion set. Obey that also target informations may be
+		* necessary to correctly validate this attribute certificate.
+		* </p><p>
+		* The attribute certificate issuer must be added to the trusted attribute
+		* issuers with {@link ExtendedPkixParameters#setTrustedACIssuers(Set)}.
+		* </p>
+		* @param certPath The certificate path which belongs to the attribute
+		*            certificate issuer public key certificate.
+		* @param params The PKIX parameters.
+		* @return A <code>PKIXCertPathValidatorResult</code> of the result of
+		*         validating the <code>certPath</code>.
+		* @throws InvalidAlgorithmParameterException if <code>params</code> is
+		*             inappropriate for this validator.
+		* @throws CertPathValidatorException if the verification fails.
+		*/
+		public virtual PkixCertPathValidatorResult Validate(
+			PkixCertPath	certPath,
+			PkixParameters	pkixParams)
+		{
+			IX509Selector certSelect = pkixParams.GetTargetConstraints();
+			if (!(certSelect is X509AttrCertStoreSelector))
+			{
+				throw new ArgumentException(
+					"TargetConstraints must be an instance of " + typeof(X509AttrCertStoreSelector).FullName,
+					"pkixParams");
+			}
+			IX509AttributeCertificate attrCert = ((X509AttrCertStoreSelector) certSelect).AttributeCert;
+
+			PkixCertPath holderCertPath = Rfc3281CertPathUtilities.ProcessAttrCert1(attrCert, pkixParams);
+			PkixCertPathValidatorResult result = Rfc3281CertPathUtilities.ProcessAttrCert2(certPath, pkixParams);
+			X509Certificate issuerCert = (X509Certificate)certPath.Certificates[0];
+			Rfc3281CertPathUtilities.ProcessAttrCert3(issuerCert, pkixParams);
+			Rfc3281CertPathUtilities.ProcessAttrCert4(issuerCert, pkixParams);
+			Rfc3281CertPathUtilities.ProcessAttrCert5(attrCert, pkixParams);
+			// 6 already done in X509AttrCertStoreSelector
+			Rfc3281CertPathUtilities.ProcessAttrCert7(attrCert, certPath, holderCertPath, pkixParams);
+			Rfc3281CertPathUtilities.AdditionalChecks(attrCert, pkixParams);
+			DateTime date;
+			try
+			{
+				date = PkixCertPathValidatorUtilities.GetValidCertDateFromValidityModel(pkixParams, null, -1);
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException(
+					"Could not get validity date from attribute certificate.", e);
+			}
+			Rfc3281CertPathUtilities.CheckCrls(attrCert, pkixParams, issuerCert, date, certPath.Certificates);
+			return result;
+		}
+	}
+}
diff --git a/crypto/src/pkix/PkixBuilderParameters.cs b/crypto/src/pkix/PkixBuilderParameters.cs
new file mode 100644
index 000000000..32fc04360
--- /dev/null
+++ b/crypto/src/pkix/PkixBuilderParameters.cs
@@ -0,0 +1,140 @@
+using System;
+using System.Text;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509.Store;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/// <summary>
+    /// Summary description for PkixBuilderParameters.
+	/// </summary>
+	public class PkixBuilderParameters
+		: PkixParameters
+	{
+		private int maxPathLength = 5;
+
+		private ISet excludedCerts = new HashSet();
+
+		/**
+		* Returns an instance of <code>PkixBuilderParameters</code>.
+		* <p>
+		* This method can be used to get a copy from other
+		* <code>PKIXBuilderParameters</code>, <code>PKIXParameters</code>,
+		* and <code>ExtendedPKIXParameters</code> instances.
+		* </p>
+		*
+		* @param pkixParams The PKIX parameters to create a copy of.
+		* @return An <code>PkixBuilderParameters</code> instance.
+		*/
+		public static PkixBuilderParameters GetInstance(
+			PkixParameters pkixParams)
+		{
+			PkixBuilderParameters parameters = new PkixBuilderParameters(
+				pkixParams.GetTrustAnchors(),
+				new X509CertStoreSelector(pkixParams.GetTargetCertConstraints()));
+			parameters.SetParams(pkixParams);
+			return parameters;
+		}
+
+		public PkixBuilderParameters(
+			ISet			trustAnchors,
+			IX509Selector	targetConstraints)
+			: base(trustAnchors)
+		{
+			SetTargetCertConstraints(targetConstraints);
+		}
+
+		public virtual int MaxPathLength
+		{
+			get { return maxPathLength; }
+			set
+			{
+				if (value < -1)
+				{
+					throw new InvalidParameterException(
+						"The maximum path length parameter can not be less than -1.");
+				}
+				this.maxPathLength = value;
+			}
+		}
+
+		/// <summary>
+		/// Excluded certificates are not used for building a certification path.
+		/// </summary>
+		/// <returns>the excluded certificates.</returns>
+		public virtual ISet GetExcludedCerts()
+		{
+			return new HashSet(excludedCerts);
+		}
+
+		/// <summary>
+		/// Sets the excluded certificates which are not used for building a
+		/// certification path. If the <code>ISet</code> is <code>null</code> an
+		/// empty set is assumed.
+		/// </summary>
+		/// <remarks>
+		/// The given set is cloned to protect it against subsequent modifications.
+		/// </remarks>
+		/// <param name="excludedCerts">The excluded certificates to set.</param>
+		public virtual void SetExcludedCerts(
+			ISet excludedCerts)
+		{
+			if (excludedCerts == null)
+			{
+				excludedCerts = new HashSet();
+			}
+			else
+			{
+				this.excludedCerts = new HashSet(excludedCerts);
+			}
+		}
+
+		/**
+		* Can alse handle <code>ExtendedPKIXBuilderParameters</code> and
+		* <code>PKIXBuilderParameters</code>.
+		* 
+		* @param params Parameters to set.
+		* @see org.bouncycastle.x509.ExtendedPKIXParameters#setParams(java.security.cert.PKIXParameters)
+		*/
+		protected override void SetParams(
+			PkixParameters parameters)
+		{
+			base.SetParams(parameters);
+			if (parameters is PkixBuilderParameters)
+			{
+				PkixBuilderParameters _params = (PkixBuilderParameters) parameters;
+				maxPathLength = _params.maxPathLength;
+				excludedCerts = new HashSet(_params.excludedCerts);
+			}
+		}
+
+		/**
+		* Makes a copy of this <code>PKIXParameters</code> object. Changes to the
+		* copy will not affect the original and vice versa.
+		*
+		* @return a copy of this <code>PKIXParameters</code> object
+		*/
+		public override object Clone()
+		{
+			PkixBuilderParameters parameters = new PkixBuilderParameters(
+				GetTrustAnchors(), GetTargetCertConstraints());
+			parameters.SetParams(this);
+			return parameters;
+		}
+
+		public override string ToString()
+		{
+			string nl = Platform.NewLine;
+			StringBuilder s = new StringBuilder();
+			s.Append("PkixBuilderParameters [" + nl);
+			s.Append(base.ToString());
+			s.Append("  Maximum Path Length: ");
+			s.Append(MaxPathLength);
+			s.Append(nl + "]" + nl);
+			return s.ToString();
+		}
+	}
+}
diff --git a/crypto/src/pkix/PkixCertPath.cs b/crypto/src/pkix/PkixCertPath.cs
new file mode 100644
index 000000000..e3d3ea7fe
--- /dev/null
+++ b/crypto/src/pkix/PkixCertPath.cs
@@ -0,0 +1,460 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Cms;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.OpenSsl;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/**
+	 * An immutable sequence of certificates (a certification path).<br />
+	 * <br />
+	 * This is an abstract class that defines the methods common to all CertPaths.
+	 * Subclasses can handle different kinds of certificates (X.509, PGP, etc.).<br />
+	 * <br />
+	 * All CertPath objects have a type, a list of Certificates, and one or more
+	 * supported encodings. Because the CertPath class is immutable, a CertPath
+	 * cannot change in any externally visible way after being constructed. This
+	 * stipulation applies to all public fields and methods of this class and any
+	 * added or overridden by subclasses.<br />
+	 * <br />
+	 * The type is a string that identifies the type of Certificates in the
+	 * certification path. For each certificate cert in a certification path
+	 * certPath, cert.getType().equals(certPath.getType()) must be true.<br />
+	 * <br />
+	 * The list of Certificates is an ordered List of zero or more Certificates.
+	 * This List and all of the Certificates contained in it must be immutable.<br />
+	 * <br />
+	 * Each CertPath object must support one or more encodings so that the object
+	 * can be translated into a byte array for storage or transmission to other
+	 * parties. Preferably, these encodings should be well-documented standards
+	 * (such as PKCS#7). One of the encodings supported by a CertPath is considered
+	 * the default encoding. This encoding is used if no encoding is explicitly
+	 * requested (for the {@link #getEncoded()} method, for instance).<br />
+	 * <br />
+	 * All CertPath objects are also Serializable. CertPath objects are resolved
+	 * into an alternate {@link CertPathRep} object during serialization. This
+	 * allows a CertPath object to be serialized into an equivalent representation
+	 * regardless of its underlying implementation.<br />
+	 * <br />
+	 * CertPath objects can be created with a CertificateFactory or they can be
+	 * returned by other classes, such as a CertPathBuilder.<br />
+	 * <br />
+	 * By convention, X.509 CertPaths (consisting of X509Certificates), are ordered
+	 * starting with the target certificate and ending with a certificate issued by
+	 * the trust anchor. That is, the issuer of one certificate is the subject of
+	 * the following one. The certificate representing the
+	 * {@link TrustAnchor TrustAnchor} should not be included in the certification
+	 * path. Unvalidated X.509 CertPaths may not follow these conventions. PKIX
+	 * CertPathValidators will detect any departure from these conventions that
+	 * cause the certification path to be invalid and throw a
+	 * CertPathValidatorException.<br />
+	 * <br />
+	 * <strong>Concurrent Access</strong><br />
+	 * <br />
+	 * All CertPath objects must be thread-safe. That is, multiple threads may
+	 * concurrently invoke the methods defined in this class on a single CertPath
+	 * object (or more than one) with no ill effects. This is also true for the List
+	 * returned by CertPath.getCertificates.<br />
+	 * <br />
+	 * Requiring CertPath objects to be immutable and thread-safe allows them to be
+	 * passed around to various pieces of code without worrying about coordinating
+	 * access. Providing this thread-safety is generally not difficult, since the
+	 * CertPath and List objects in question are immutable.
+	 *
+	 * @see CertificateFactory
+	 * @see CertPathBuilder
+	 */
+	/// <summary>
+	/// CertPath implementation for X.509 certificates.
+	/// </summary>
+	public class PkixCertPath
+//		: CertPath
+	{
+		internal static readonly IList certPathEncodings;
+
+        static PkixCertPath()
+        {
+            IList encodings = Platform.CreateArrayList();
+            encodings.Add("PkiPath");
+            encodings.Add("PEM");
+            encodings.Add("PKCS7");
+            certPathEncodings = CollectionUtilities.ReadOnly(encodings);
+        }
+
+        private readonly IList certificates;
+
+		/**
+		 * @param certs
+		 */
+		private static IList SortCerts(
+			IList certs)
+		{
+			if (certs.Count < 2)
+				return certs;
+
+			X509Name issuer = ((X509Certificate)certs[0]).IssuerDN;
+			bool okay = true;
+
+			for (int i = 1; i != certs.Count; i++)
+			{
+				X509Certificate cert = (X509Certificate)certs[i];
+
+				if (issuer.Equivalent(cert.SubjectDN, true))
+				{
+					issuer = ((X509Certificate)certs[i]).IssuerDN;
+				}
+				else
+				{
+					okay = false;
+					break;
+				}
+			}
+
+			if (okay)
+				return certs;
+
+			// find end-entity cert
+            IList retList = Platform.CreateArrayList(certs.Count);
+            IList orig = Platform.CreateArrayList(certs);
+
+			for (int i = 0; i < certs.Count; i++)
+			{
+				X509Certificate cert = (X509Certificate)certs[i];
+				bool           found = false;
+
+				X509Name subject = cert.SubjectDN;
+				foreach (X509Certificate c in certs)
+				{
+					if (c.IssuerDN.Equivalent(subject, true))
+					{
+						found = true;
+						break;
+					}
+				}
+
+				if (!found)
+				{
+					retList.Add(cert);
+					certs.RemoveAt(i);
+				}
+			}
+
+			// can only have one end entity cert - something's wrong, give up.
+			if (retList.Count > 1)
+				return orig;
+
+			for (int i = 0; i != retList.Count; i++)
+			{
+				issuer = ((X509Certificate)retList[i]).IssuerDN;
+
+				for (int j = 0; j < certs.Count; j++)
+				{
+					X509Certificate c = (X509Certificate)certs[j];
+					if (issuer.Equivalent(c.SubjectDN, true))
+					{
+						retList.Add(c);
+						certs.RemoveAt(j);
+						break;
+					}
+				}
+			}
+
+			// make sure all certificates are accounted for.
+			if (certs.Count > 0)
+				return orig;
+
+			return retList;
+		}
+
+		/**
+		 * Creates a CertPath of the specified type.
+		 * This constructor is protected because most users should use
+		 * a CertificateFactory to create CertPaths.
+		 * @param type the standard name of the type of Certificatesin this path
+		 **/
+		public PkixCertPath(
+			ICollection certificates)
+//			: base("X.509")
+		{
+			this.certificates = SortCerts(Platform.CreateArrayList(certificates));
+		}
+
+		public PkixCertPath(
+			Stream inStream)
+			: this(inStream, "PkiPath")
+		{
+		}
+
+		/**
+		 * Creates a CertPath of the specified type.
+		 * This constructor is protected because most users should use
+		 * a CertificateFactory to create CertPaths.
+		 *
+		 * @param type the standard name of the type of Certificatesin this path
+		 **/
+		public PkixCertPath(
+			Stream	inStream,
+			string	encoding)
+//			: base("X.509")
+		{
+            string upper = encoding.ToUpper();
+
+            IList certs;
+			try
+			{
+				if (upper.Equals("PkiPath".ToUpper()))
+				{
+					Asn1InputStream derInStream = new Asn1InputStream(inStream);
+					Asn1Object derObject = derInStream.ReadObject();
+					if (!(derObject is Asn1Sequence))
+					{
+						throw new CertificateException(
+							"input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath");
+					}
+
+                    certs = Platform.CreateArrayList();
+
+                    foreach (Asn1Encodable ae in (Asn1Sequence)derObject)
+                    {
+                        byte[] derBytes = ae.GetEncoded(Asn1Encodable.Der);
+                        Stream certInStream = new MemoryStream(derBytes, false);
+
+                        // TODO Is inserting at the front important (list will be sorted later anyway)?
+                        certs.Insert(0, new X509CertificateParser().ReadCertificate(certInStream));
+					}
+				}
+                else if (upper.Equals("PKCS7") || upper.Equals("PEM"))
+				{
+                    certs = Platform.CreateArrayList(new X509CertificateParser().ReadCertificates(inStream));
+				}
+				else
+				{
+					throw new CertificateException("unsupported encoding: " + encoding);
+				}
+			}
+			catch (IOException ex)
+			{
+				throw new CertificateException(
+					"IOException throw while decoding CertPath:\n"
+					+ ex.ToString());
+			}
+
+			this.certificates = SortCerts(certs);
+		}
+
+		/**
+		 * Returns an iteration of the encodings supported by this
+		 * certification path, with the default encoding
+		 * first. Attempts to modify the returned Iterator via its
+		 * remove method result in an UnsupportedOperationException.
+		 *
+		 * @return an Iterator over the names of the supported encodings (as Strings)
+		 **/
+		public virtual IEnumerable Encodings
+		{
+            get { return new EnumerableProxy(certPathEncodings); }
+		}
+
+		/**
+		* Compares this certification path for equality with the specified object.
+		* Two CertPaths are equal if and only if their types are equal and their
+		* certificate Lists (and by implication the Certificates in those Lists)
+		* are equal. A CertPath is never equal to an object that is not a CertPath.<br />
+		* <br />
+		* This algorithm is implemented by this method. If it is overridden, the
+		* behavior specified here must be maintained.
+		*
+		* @param other
+		*            the object to test for equality with this certification path
+		*
+		* @return true if the specified object is equal to this certification path,
+		*         false otherwise
+		*
+		* @see Object#hashCode() Object.hashCode()
+		*/
+		public override bool Equals(
+			object obj)
+		{
+			if (this == obj)
+				return true;
+
+			PkixCertPath other = obj as PkixCertPath;
+			if (other == null)
+				return false;
+
+//			if (!this.Type.Equals(other.Type))
+//				return false;
+
+			//return this.Certificates.Equals(other.Certificates);
+
+			// TODO Extract this to a utility class
+			IList thisCerts = this.Certificates;
+			IList otherCerts = other.Certificates;
+
+			if (thisCerts.Count != otherCerts.Count)
+				return false;
+
+			IEnumerator e1 = thisCerts.GetEnumerator();
+			IEnumerator e2 = thisCerts.GetEnumerator();
+
+			while (e1.MoveNext())
+			{
+				e2.MoveNext();
+
+				if (!Platform.Equals(e1.Current, e2.Current))
+					return false;
+			}
+
+			return true;
+		}
+
+		public override int GetHashCode()
+		{
+			// FIXME?
+			return this.Certificates.GetHashCode();
+		}
+
+		/**
+		 * Returns the encoded form of this certification path, using
+		 * the default encoding.
+		 *
+		 * @return the encoded bytes
+		 * @exception CertificateEncodingException if an encoding error occurs
+		 **/
+		public virtual byte[] GetEncoded()
+		{
+			foreach (object enc in Encodings)
+			{
+				if (enc is string)
+				{
+					return GetEncoded((string)enc);
+				}
+			}
+			return null;
+		}
+
+		/**
+		 * Returns the encoded form of this certification path, using
+		 * the specified encoding.
+		 *
+		 * @param encoding the name of the encoding to use
+		 * @return the encoded bytes
+		 * @exception CertificateEncodingException if an encoding error
+		 * occurs or the encoding requested is not supported
+		 *
+		 */
+		public virtual byte[] GetEncoded(
+			string encoding)
+		{
+			if (Platform.CompareIgnoreCase(encoding, "PkiPath") == 0)
+			{
+				Asn1EncodableVector v = new Asn1EncodableVector();
+
+				for (int i = certificates.Count - 1; i >= 0; i--)
+				{
+					v.Add(ToAsn1Object((X509Certificate) certificates[i]));
+				}
+
+				return ToDerEncoded(new DerSequence(v));
+			}
+            else if (Platform.CompareIgnoreCase(encoding, "PKCS7") == 0)
+			{
+				Asn1.Pkcs.ContentInfo encInfo = new Asn1.Pkcs.ContentInfo(
+					PkcsObjectIdentifiers.Data, null);
+
+				Asn1EncodableVector v = new Asn1EncodableVector();
+				for (int i = 0; i != certificates.Count; i++)
+				{
+					v.Add(ToAsn1Object((X509Certificate)certificates[i]));
+				}
+
+				Asn1.Pkcs.SignedData sd = new Asn1.Pkcs.SignedData(
+					new DerInteger(1),
+					new DerSet(),
+					encInfo,
+					new DerSet(v),
+					null,
+					new DerSet());
+
+				return ToDerEncoded(new Asn1.Pkcs.ContentInfo(PkcsObjectIdentifiers.SignedData, sd));
+			}
+            else if (Platform.CompareIgnoreCase(encoding, "PEM") == 0)
+			{
+				MemoryStream bOut = new MemoryStream();
+				PemWriter pWrt = new PemWriter(new StreamWriter(bOut));
+
+				try
+				{
+					for (int i = 0; i != certificates.Count; i++)
+					{
+						pWrt.WriteObject(certificates[i]);
+					}
+
+					pWrt.Writer.Close();
+				}
+				catch (Exception)
+				{
+					throw new CertificateEncodingException("can't encode certificate for PEM encoded path");
+				}
+
+				return bOut.ToArray();
+			}
+			else
+			{
+				throw new CertificateEncodingException("unsupported encoding: " + encoding);
+			}
+		}
+
+		/// <summary>
+		/// Returns the list of certificates in this certification
+		/// path.
+		/// </summary>
+		public virtual IList Certificates
+		{
+            get { return CollectionUtilities.ReadOnly(certificates); }
+		}
+
+		/**
+		 * Return a DERObject containing the encoded certificate.
+		 *
+		 * @param cert the X509Certificate object to be encoded
+		 *
+		 * @return the DERObject
+		 **/
+		private Asn1Object ToAsn1Object(
+			X509Certificate cert)
+		{
+			try
+			{
+				return Asn1Object.FromByteArray(cert.GetEncoded());
+			}
+			catch (Exception e)
+			{
+				throw new CertificateEncodingException("Exception while encoding certificate", e);
+			}
+		}
+
+		private byte[] ToDerEncoded(Asn1Encodable obj)
+		{
+			try
+			{
+				return obj.GetEncoded(Asn1Encodable.Der);
+			}
+			catch (IOException e)
+			{
+				throw new CertificateEncodingException("Exception thrown", e);
+			}
+		}
+	}
+}
diff --git a/crypto/src/pkix/PkixCertPathBuilder.cs b/crypto/src/pkix/PkixCertPathBuilder.cs
new file mode 100644
index 000000000..7082fe409
--- /dev/null
+++ b/crypto/src/pkix/PkixCertPathBuilder.cs
@@ -0,0 +1,205 @@
+using System;
+using System.Collections;
+using System.Text;
+
+using Org.BouncyCastle.Asn1.IsisMtt;
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Asn1.X500;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/**
+	* Implements the PKIX CertPathBuilding algorithm for BouncyCastle.
+	*
+	* @see CertPathBuilderSpi
+	*/
+	public class PkixCertPathBuilder
+		//		: CertPathBuilderSpi
+	{
+		/**
+		 * Build and validate a CertPath using the given parameter.
+		 *
+		 * @param params PKIXBuilderParameters object containing all information to
+		 *            build the CertPath
+		 */
+		public virtual PkixCertPathBuilderResult Build(
+			PkixBuilderParameters pkixParams)
+		{
+			// search target certificates
+
+			IX509Selector certSelect = pkixParams.GetTargetCertConstraints();
+			if (!(certSelect is X509CertStoreSelector))
+			{
+				throw new PkixCertPathBuilderException(
+					"TargetConstraints must be an instance of "
+					+ typeof(X509CertStoreSelector).FullName + " for "
+					+ this.GetType() + " class.");
+			}
+
+			ISet targets = new HashSet();
+			try
+			{
+				targets.AddAll(PkixCertPathValidatorUtilities.FindCertificates((X509CertStoreSelector)certSelect, pkixParams.GetStores()));
+				// TODO Should this include an entry for pkixParams.GetAdditionalStores() too?
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathBuilderException(
+					"Error finding target certificate.", e);
+			}
+
+			if (targets.IsEmpty)
+				throw new PkixCertPathBuilderException("No certificate found matching targetContraints.");
+
+			PkixCertPathBuilderResult result = null;
+			IList certPathList = Platform.CreateArrayList();
+
+			// check all potential target certificates
+			foreach (X509Certificate cert in targets)
+			{
+				result = Build(cert, pkixParams, certPathList);
+
+				if (result != null)
+					break;
+			}
+
+			if (result == null && certPathException != null)
+			{
+				throw new PkixCertPathBuilderException(certPathException.Message, certPathException.InnerException);
+			}
+
+			if (result == null && certPathException == null)
+			{
+				throw new PkixCertPathBuilderException("Unable to find certificate chain.");
+			}
+
+			return result;
+		}
+
+		private Exception certPathException;
+
+		protected virtual PkixCertPathBuilderResult Build(
+			X509Certificate			tbvCert,
+			PkixBuilderParameters	pkixParams,
+			IList					tbvPath)
+		{
+			// If tbvCert is readily present in tbvPath, it indicates having run
+			// into a cycle in the PKI graph.
+			if (tbvPath.Contains(tbvCert))
+				return null;
+
+			// step out, the certificate is not allowed to appear in a certification
+			// chain.
+			if (pkixParams.GetExcludedCerts().Contains(tbvCert))
+				return null;
+
+			// test if certificate path exceeds maximum length
+			if (pkixParams.MaxPathLength != -1)
+			{
+				if (tbvPath.Count - 1 > pkixParams.MaxPathLength)
+					return null;
+			}
+
+			tbvPath.Add(tbvCert);
+
+//			X509CertificateParser certParser = new X509CertificateParser();
+			PkixCertPathBuilderResult builderResult = null;
+			PkixCertPathValidator validator = new PkixCertPathValidator();
+
+			try
+			{
+				// check whether the issuer of <tbvCert> is a TrustAnchor
+				if (PkixCertPathValidatorUtilities.FindTrustAnchor(tbvCert, pkixParams.GetTrustAnchors()) != null)
+				{
+					// exception message from possibly later tried certification
+					// chains
+					PkixCertPath certPath = null;
+					try
+					{
+						certPath = new PkixCertPath(tbvPath);
+					}
+					catch (Exception e)
+					{
+						throw new Exception(
+							"Certification path could not be constructed from certificate list.",
+							e);
+					}
+
+					PkixCertPathValidatorResult result = null;
+					try
+					{
+						result = (PkixCertPathValidatorResult)validator.Validate(
+							certPath, pkixParams);
+					}
+					catch (Exception e)
+					{
+						throw new Exception(
+							"Certification path could not be validated.", e);
+					}
+
+					return new PkixCertPathBuilderResult(certPath, result.TrustAnchor,
+						result.PolicyTree, result.SubjectPublicKey);
+				}
+				else
+				{
+					// add additional X.509 stores from locations in certificate
+					try
+					{
+						PkixCertPathValidatorUtilities.AddAdditionalStoresFromAltNames(
+							tbvCert, pkixParams);
+					}
+					catch (CertificateParsingException e)
+					{
+						throw new Exception(
+							"No additiontal X.509 stores can be added from certificate locations.",
+							e);
+					}
+
+					// try to get the issuer certificate from one of the stores
+					HashSet issuers = new HashSet();
+					try
+					{
+						issuers.AddAll(PkixCertPathValidatorUtilities.FindIssuerCerts(tbvCert, pkixParams));
+					}
+					catch (Exception e)
+					{
+						throw new Exception(
+							"Cannot find issuer certificate for certificate in certification path.",
+							e);
+					}
+
+					if (issuers.IsEmpty)
+						throw new Exception("No issuer certificate for certificate in certification path found.");
+
+					foreach (X509Certificate issuer in issuers)
+					{
+						builderResult = Build(issuer, pkixParams, tbvPath);
+
+						if (builderResult != null)
+							break;
+					}
+				}
+			}
+			catch (Exception e)
+			{
+				certPathException = e;
+			}
+
+			if (builderResult == null)
+			{
+				tbvPath.Remove(tbvCert);
+			}
+
+			return builderResult;
+		}
+	}
+}
diff --git a/crypto/src/pkix/PkixCertPathBuilderException.cs b/crypto/src/pkix/PkixCertPathBuilderException.cs
new file mode 100644
index 000000000..5a4944dd8
--- /dev/null
+++ b/crypto/src/pkix/PkixCertPathBuilderException.cs
@@ -0,0 +1,22 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/// <summary>
+	/// Summary description for PkixCertPathBuilderException.
+	/// </summary>
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+    [Serializable]
+#endif
+    public class PkixCertPathBuilderException : GeneralSecurityException
+	{
+		public PkixCertPathBuilderException() : base() { }
+		
+		public PkixCertPathBuilderException(string message) : base(message)	{ }  
+
+		public PkixCertPathBuilderException(string message, Exception exception) : base(message, exception) { }
+		
+	}
+}
diff --git a/crypto/src/pkix/PkixCertPathBuilderResult.cs b/crypto/src/pkix/PkixCertPathBuilderResult.cs
new file mode 100644
index 000000000..f8003032f
--- /dev/null
+++ b/crypto/src/pkix/PkixCertPathBuilderResult.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Text;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pkix;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/// <summary>
+	/// Summary description for PkixCertPathBuilderResult.
+	/// </summary>
+	public class PkixCertPathBuilderResult
+		: PkixCertPathValidatorResult//, ICertPathBuilderResult
+	{
+		private PkixCertPath certPath;
+		
+		public PkixCertPathBuilderResult(
+			PkixCertPath			certPath,
+			TrustAnchor				trustAnchor,
+			PkixPolicyNode			policyTree,
+			AsymmetricKeyParameter	subjectPublicKey)
+			: base(trustAnchor, policyTree, subjectPublicKey)
+		{			
+			if (certPath == null)
+				throw new ArgumentNullException("certPath");
+
+			this.certPath = certPath;
+		}
+
+		public PkixCertPath CertPath
+		{
+            get { return certPath; }
+		}
+
+		public override string ToString()
+		{
+			StringBuilder s = new StringBuilder();
+			s.Append("SimplePKIXCertPathBuilderResult: [\n");
+			s.Append("  Certification Path: ").Append(CertPath).Append('\n');
+			s.Append("  Trust Anchor: ").Append(this.TrustAnchor.TrustedCert.IssuerDN.ToString()).Append('\n');
+			s.Append("  Subject Public Key: ").Append(this.SubjectPublicKey).Append("\n]");
+			return s.ToString();
+		}
+	}
+}
diff --git a/crypto/src/pkix/PkixCertPathChecker.cs b/crypto/src/pkix/PkixCertPathChecker.cs
new file mode 100644
index 000000000..f22738d89
--- /dev/null
+++ b/crypto/src/pkix/PkixCertPathChecker.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Pkix
+{
+    public abstract class PkixCertPathChecker
+    {
+        protected PkixCertPathChecker()
+        {
+        }
+
+        /**
+         * Initializes the internal state of this <code>PKIXCertPathChecker</code>.
+         * <p>
+         * The <code>forward</code> flag specifies the order that certificates
+         * will be passed to the {@link #check check} method (forward or reverse). A
+         * <code>PKIXCertPathChecker</code> <b>must</b> support reverse checking
+         * and <b>may</b> support forward checking.
+		 * </p>
+         * 
+         * @param forward
+         *            the order that certificates are presented to the
+         *            <code>check</code> method. If <code>true</code>,
+         *            certificates are presented from target to most-trusted CA
+         *            (forward); if <code>false</code>, from most-trusted CA to
+         *            target (reverse).
+         * @exception CertPathValidatorException
+         *                if this <code>PKIXCertPathChecker</code> is unable to
+         *                check certificates in the specified order; it should never
+         *                be thrown if the forward flag is false since reverse
+         *                checking must be supported
+         */
+        public abstract void Init(bool forward);
+        //throws CertPathValidatorException;
+
+        /**
+         * Indicates if forward checking is supported. Forward checking refers to
+         * the ability of the <code>PKIXCertPathChecker</code> to perform its
+         * checks when certificates are presented to the <code>check</code> method
+         * in the forward direction (from target to most-trusted CA).
+         * 
+         * @return <code>true</code> if forward checking is supported,
+         *         <code>false</code> otherwise
+         */
+        public abstract bool IsForwardCheckingSupported();
+
+        /**
+         * Returns an immutable <code>Set</code> of X.509 certificate extensions
+         * that this <code>PKIXCertPathChecker</code> supports (i.e. recognizes,
+         * is able to process), or <code>null</code> if no extensions are
+         * supported.
+         * <p>
+         * Each element of the set is a <code>String</code> representing the
+         * Object Identifier (OID) of the X.509 extension that is supported. The OID
+         * is represented by a set of nonnegative integers separated by periods.
+         * </p><p>
+         * All X.509 certificate extensions that a <code>PKIXCertPathChecker</code>
+         * might possibly be able to process should be included in the set.
+		 * </p>
+         * 
+         * @return an immutable <code>Set</code> of X.509 extension OIDs (in
+         *         <code>String</code> format) supported by this
+         *         <code>PKIXCertPathChecker</code>, or <code>null</code> if no
+         *         extensions are supported
+         */
+        public abstract ISet GetSupportedExtensions();
+
+        /**
+         * Performs the check(s) on the specified certificate using its internal
+         * state and removes any critical extensions that it processes from the
+         * specified collection of OID strings that represent the unresolved
+         * critical extensions. The certificates are presented in the order
+         * specified by the <code>init</code> method.
+         * 
+         * @param cert
+         *            the <code>Certificate</code> to be checked
+         * @param unresolvedCritExts
+         *            a <code>Collection</code> of OID strings representing the
+         *            current set of unresolved critical extensions
+         * @exception CertPathValidatorException
+         *                if the specified certificate does not pass the check
+         */
+        public abstract void Check(X509Certificate cert, ICollection unresolvedCritExts);
+        //throws CertPathValidatorException;
+
+        /**
+         * Returns a clone of this object. Calls the <code>Object.clone()</code>
+         * method. All subclasses which maintain state must support and override
+         * this method, if necessary.
+         * 
+         * @return a copy of this <code>PKIXCertPathChecker</code>
+         */
+        public virtual object Clone()
+        {
+			// TODO Check this
+			return base.MemberwiseClone();
+        }
+    }
+}
diff --git a/crypto/src/pkix/PkixCertPathValidator.cs b/crypto/src/pkix/PkixCertPathValidator.cs
new file mode 100644
index 000000000..7eb838886
--- /dev/null
+++ b/crypto/src/pkix/PkixCertPathValidator.cs
@@ -0,0 +1,420 @@
+using System;
+using System.Collections;
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/**
+	 * The <i>Service Provider Interface</i> (<b>SPI</b>)
+	 * for the {@link CertPathValidator CertPathValidator} class. All
+	 * <code>CertPathValidator</code> implementations must include a class (the
+	 * SPI class) that extends this class (<code>CertPathValidatorSpi</code>)
+	 * and implements all of its methods. In general, instances of this class
+	 * should only be accessed through the <code>CertPathValidator</code> class.
+	 * For details, see the Java Cryptography Architecture.<br />
+	 * <br />
+	 * <b>Concurrent Access</b><br />
+	 * <br />
+	 * Instances of this class need not be protected against concurrent
+	 * access from multiple threads. Threads that need to access a single
+	 * <code>CertPathValidatorSpi</code> instance concurrently should synchronize
+	 * amongst themselves and provide the necessary locking before calling the
+	 * wrapping <code>CertPathValidator</code> object.<br />
+	 * <br />
+	 * However, implementations of <code>CertPathValidatorSpi</code> may still
+	 * encounter concurrency issues, since multiple threads each
+	 * manipulating a different <code>CertPathValidatorSpi</code> instance need not
+	 * synchronize.
+	 */
+	/// <summary>
+    /// CertPathValidatorSpi implementation for X.509 Certificate validation a la RFC
+    /// 3280.
+    /// </summary>
+    public class PkixCertPathValidator
+    {
+        public virtual PkixCertPathValidatorResult Validate(
+			PkixCertPath	certPath,
+			PkixParameters	paramsPkix)
+        {
+			if (paramsPkix.GetTrustAnchors() == null)
+            {
+                throw new ArgumentException(
+					"trustAnchors is null, this is not allowed for certification path validation.",
+					"parameters");
+            }
+
+            //
+            // 6.1.1 - inputs
+            //
+
+            //
+            // (a)
+            //
+            IList certs = certPath.Certificates;
+            int n = certs.Count;
+
+            if (certs.Count == 0)
+                throw new PkixCertPathValidatorException("Certification path is empty.", null, certPath, 0);
+
+			//
+            // (b)
+            //
+            // DateTime validDate = PkixCertPathValidatorUtilities.GetValidDate(paramsPkix);
+
+            //
+            // (c)
+            //
+            ISet userInitialPolicySet = paramsPkix.GetInitialPolicies();
+
+            //
+            // (d)
+            //
+            TrustAnchor trust;
+            try
+            {
+                trust = PkixCertPathValidatorUtilities.FindTrustAnchor(
+					(X509Certificate)certs[certs.Count - 1],
+					paramsPkix.GetTrustAnchors());
+            }
+            catch (Exception e)
+            {
+                throw new PkixCertPathValidatorException(e.Message, e, certPath, certs.Count - 1);
+            }
+
+            if (trust == null)
+                throw new PkixCertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -1);
+
+			//
+            // (e), (f), (g) are part of the paramsPkix object.
+            //
+            IEnumerator certIter;
+            int index = 0;
+            int i;
+            // Certificate for each interation of the validation loop
+            // Signature information for each iteration of the validation loop
+            //
+            // 6.1.2 - setup
+            //
+
+            //
+            // (a)
+            //
+            IList[] policyNodes = new IList[n + 1];
+            for (int j = 0; j < policyNodes.Length; j++)
+            {
+                policyNodes[j] = Platform.CreateArrayList();
+            }
+
+            ISet policySet = new HashSet();
+
+            policySet.Add(Rfc3280CertPathUtilities.ANY_POLICY);
+
+            PkixPolicyNode validPolicyTree = new PkixPolicyNode(Platform.CreateArrayList(), 0, policySet, null, new HashSet(),
+                    Rfc3280CertPathUtilities.ANY_POLICY, false);
+
+            policyNodes[0].Add(validPolicyTree);
+
+            //
+            // (b) and (c)
+            //
+            PkixNameConstraintValidator nameConstraintValidator = new PkixNameConstraintValidator();
+
+            // (d)
+            //
+            int explicitPolicy;
+            ISet acceptablePolicies = new HashSet();
+
+            if (paramsPkix.IsExplicitPolicyRequired)
+            {
+                explicitPolicy = 0;
+            }
+            else
+            {
+                explicitPolicy = n + 1;
+            }
+
+            //
+            // (e)
+            //
+            int inhibitAnyPolicy;
+
+            if (paramsPkix.IsAnyPolicyInhibited)
+            {
+                inhibitAnyPolicy = 0;
+            }
+            else
+            {
+                inhibitAnyPolicy = n + 1;
+            }
+
+            //
+            // (f)
+            //
+            int policyMapping;
+
+            if (paramsPkix.IsPolicyMappingInhibited)
+            {
+                policyMapping = 0;
+            }
+            else
+            {
+                policyMapping = n + 1;
+            }
+
+            //
+            // (g), (h), (i), (j)
+            //
+            AsymmetricKeyParameter workingPublicKey;
+            X509Name workingIssuerName;
+
+            X509Certificate sign = trust.TrustedCert;
+            try
+            {
+                if (sign != null)
+                {
+                    workingIssuerName = sign.SubjectDN;
+                    workingPublicKey = sign.GetPublicKey();
+                }
+                else
+                {
+                    workingIssuerName = new X509Name(trust.CAName);
+                    workingPublicKey = trust.CAPublicKey;
+                }
+            }
+            catch (ArgumentException ex)
+            {
+                throw new PkixCertPathValidatorException("Subject of trust anchor could not be (re)encoded.", ex, certPath,
+                        -1);
+            }
+
+            AlgorithmIdentifier workingAlgId = null;
+            try
+            {
+                workingAlgId = PkixCertPathValidatorUtilities.GetAlgorithmIdentifier(workingPublicKey);
+            }
+            catch (PkixCertPathValidatorException e)
+            {
+                throw new PkixCertPathValidatorException(
+                        "Algorithm identifier of public key of trust anchor could not be read.", e, certPath, -1);
+            }
+
+//			DerObjectIdentifier workingPublicKeyAlgorithm = workingAlgId.ObjectID;
+//			Asn1Encodable workingPublicKeyParameters = workingAlgId.Parameters;
+
+            //
+            // (k)
+            //
+            int maxPathLength = n;
+
+            //
+            // 6.1.3
+            //
+
+			X509CertStoreSelector certConstraints = paramsPkix.GetTargetCertConstraints();
+            if (certConstraints != null && !certConstraints.Match((X509Certificate)certs[0]))
+            {
+                throw new PkixCertPathValidatorException(
+					"Target certificate in certification path does not match targetConstraints.", null, certPath, 0);
+            }
+
+            //
+            // initialize CertPathChecker's
+            //
+            IList pathCheckers = paramsPkix.GetCertPathCheckers();
+            certIter = pathCheckers.GetEnumerator();
+
+            while (certIter.MoveNext())
+            {
+                ((PkixCertPathChecker)certIter.Current).Init(false);
+            }
+
+            X509Certificate cert = null;
+
+            for (index = certs.Count - 1; index >= 0; index--)
+            {
+                // try
+                // {
+                //
+                // i as defined in the algorithm description
+                //
+                i = n - index;
+
+                //
+                // set certificate to be checked in this round
+                // sign and workingPublicKey and workingIssuerName are set
+                // at the end of the for loop and initialized the
+                // first time from the TrustAnchor
+                //
+                cert = (X509Certificate)certs[index];
+
+                //
+                // 6.1.3
+                //
+
+                Rfc3280CertPathUtilities.ProcessCertA(certPath, paramsPkix, index, workingPublicKey,
+					workingIssuerName, sign);
+
+                Rfc3280CertPathUtilities.ProcessCertBC(certPath, index, nameConstraintValidator);
+
+                validPolicyTree = Rfc3280CertPathUtilities.ProcessCertD(certPath, index,
+					acceptablePolicies, validPolicyTree, policyNodes, inhibitAnyPolicy);
+
+                validPolicyTree = Rfc3280CertPathUtilities.ProcessCertE(certPath, index, validPolicyTree);
+
+                Rfc3280CertPathUtilities.ProcessCertF(certPath, index, validPolicyTree, explicitPolicy);
+
+                //
+                // 6.1.4
+                //
+
+                if (i != n)
+                {
+                    if (cert != null && cert.Version == 1)
+                    {
+                        throw new PkixCertPathValidatorException(
+							"Version 1 certificates can't be used as CA ones.", null, certPath, index);
+                    }
+
+                    Rfc3280CertPathUtilities.PrepareNextCertA(certPath, index);
+
+                    validPolicyTree = Rfc3280CertPathUtilities.PrepareCertB(certPath, index, policyNodes,
+						validPolicyTree, policyMapping);
+
+                    Rfc3280CertPathUtilities.PrepareNextCertG(certPath, index, nameConstraintValidator);
+
+                    // (h)
+                    explicitPolicy = Rfc3280CertPathUtilities.PrepareNextCertH1(certPath, index, explicitPolicy);
+                    policyMapping = Rfc3280CertPathUtilities.PrepareNextCertH2(certPath, index, policyMapping);
+                    inhibitAnyPolicy = Rfc3280CertPathUtilities.PrepareNextCertH3(certPath, index, inhibitAnyPolicy);
+
+                    //
+                    // (i)
+                    //
+                    explicitPolicy = Rfc3280CertPathUtilities.PrepareNextCertI1(certPath, index, explicitPolicy);
+                    policyMapping = Rfc3280CertPathUtilities.PrepareNextCertI2(certPath, index, policyMapping);
+
+                    // (j)
+                    inhibitAnyPolicy = Rfc3280CertPathUtilities.PrepareNextCertJ(certPath, index, inhibitAnyPolicy);
+
+                    // (k)
+                    Rfc3280CertPathUtilities.PrepareNextCertK(certPath, index);
+
+                    // (l)
+                    maxPathLength = Rfc3280CertPathUtilities.PrepareNextCertL(certPath, index, maxPathLength);
+
+                    // (m)
+                    maxPathLength = Rfc3280CertPathUtilities.PrepareNextCertM(certPath, index, maxPathLength);
+
+                    // (n)
+                    Rfc3280CertPathUtilities.PrepareNextCertN(certPath, index);
+
+					ISet criticalExtensions1 = cert.GetCriticalExtensionOids();
+
+					if (criticalExtensions1 != null)
+					{
+						criticalExtensions1 = new HashSet(criticalExtensions1);
+
+						// these extensions are handled by the algorithm
+						criticalExtensions1.Remove(X509Extensions.KeyUsage.Id);
+						criticalExtensions1.Remove(X509Extensions.CertificatePolicies.Id);
+						criticalExtensions1.Remove(X509Extensions.PolicyMappings.Id);
+						criticalExtensions1.Remove(X509Extensions.InhibitAnyPolicy.Id);
+						criticalExtensions1.Remove(X509Extensions.IssuingDistributionPoint.Id);
+						criticalExtensions1.Remove(X509Extensions.DeltaCrlIndicator.Id);
+						criticalExtensions1.Remove(X509Extensions.PolicyConstraints.Id);
+						criticalExtensions1.Remove(X509Extensions.BasicConstraints.Id);
+						criticalExtensions1.Remove(X509Extensions.SubjectAlternativeName.Id);
+						criticalExtensions1.Remove(X509Extensions.NameConstraints.Id);
+					}
+					else
+					{
+						criticalExtensions1 = new HashSet();
+					}
+
+					// (o)
+					Rfc3280CertPathUtilities.PrepareNextCertO(certPath, index, criticalExtensions1, pathCheckers);
+
+					// set signing certificate for next round
+                    sign = cert;
+
+                    // (c)
+                    workingIssuerName = sign.SubjectDN;
+
+                    // (d)
+                    try
+                    {
+                        workingPublicKey = PkixCertPathValidatorUtilities.GetNextWorkingKey(certPath.Certificates, index);
+                    }
+                    catch (PkixCertPathValidatorException e)
+                    {
+                        throw new PkixCertPathValidatorException("Next working key could not be retrieved.", e, certPath, index);
+                    }
+
+                    workingAlgId = PkixCertPathValidatorUtilities.GetAlgorithmIdentifier(workingPublicKey);
+                    // (f)
+//                    workingPublicKeyAlgorithm = workingAlgId.ObjectID;
+                    // (e)
+//                    workingPublicKeyParameters = workingAlgId.Parameters;
+                }
+            }
+
+            //
+            // 6.1.5 Wrap-up procedure
+            //
+
+            explicitPolicy = Rfc3280CertPathUtilities.WrapupCertA(explicitPolicy, cert);
+
+            explicitPolicy = Rfc3280CertPathUtilities.WrapupCertB(certPath, index + 1, explicitPolicy);
+
+            //
+            // (c) (d) and (e) are already done
+            //
+
+            //
+            // (f)
+            //
+            ISet criticalExtensions = cert.GetCriticalExtensionOids();
+
+            if (criticalExtensions != null)
+            {
+                criticalExtensions = new HashSet(criticalExtensions);
+
+                // Requires .Id
+                // these extensions are handled by the algorithm
+                criticalExtensions.Remove(X509Extensions.KeyUsage.Id);
+                criticalExtensions.Remove(X509Extensions.CertificatePolicies.Id);
+                criticalExtensions.Remove(X509Extensions.PolicyMappings.Id);
+                criticalExtensions.Remove(X509Extensions.InhibitAnyPolicy.Id);
+                criticalExtensions.Remove(X509Extensions.IssuingDistributionPoint.Id);
+                criticalExtensions.Remove(X509Extensions.DeltaCrlIndicator.Id);
+                criticalExtensions.Remove(X509Extensions.PolicyConstraints.Id);
+                criticalExtensions.Remove(X509Extensions.BasicConstraints.Id);
+                criticalExtensions.Remove(X509Extensions.SubjectAlternativeName.Id);
+                criticalExtensions.Remove(X509Extensions.NameConstraints.Id);
+                criticalExtensions.Remove(X509Extensions.CrlDistributionPoints.Id);
+            }
+            else
+            {
+                criticalExtensions = new HashSet();
+            }
+
+            Rfc3280CertPathUtilities.WrapupCertF(certPath, index + 1, pathCheckers, criticalExtensions);
+
+            PkixPolicyNode intersection = Rfc3280CertPathUtilities.WrapupCertG(certPath, paramsPkix, userInitialPolicySet,
+                    index + 1, policyNodes, validPolicyTree, acceptablePolicies);
+
+            if ((explicitPolicy > 0) || (intersection != null))
+            {
+				return new PkixCertPathValidatorResult(trust, intersection, cert.GetPublicKey());
+			}
+
+			throw new PkixCertPathValidatorException("Path processing failed on policy.", null, certPath, index);
+        }
+    }
+}
diff --git a/crypto/src/pkix/PkixCertPathValidatorException.cs b/crypto/src/pkix/PkixCertPathValidatorException.cs
new file mode 100644
index 000000000..35522c6f8
--- /dev/null
+++ b/crypto/src/pkix/PkixCertPathValidatorException.cs
@@ -0,0 +1,221 @@
+using System;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/**
+	 * An exception indicating one of a variety of problems encountered when 
+	 * validating a certification path. <br />
+	 * <br />
+	 * A <code>CertPathValidatorException</code> provides support for wrapping
+	 * exceptions. The {@link #getCause getCause} method returns the throwable, 
+	 * if any, that caused this exception to be thrown. <br />
+	 * <br />
+	 * A <code>CertPathValidatorException</code> may also include the 
+	 * certification path that was being validated when the exception was thrown 
+	 * and the index of the certificate in the certification path that caused the 
+	 * exception to be thrown. Use the {@link #getCertPath getCertPath} and
+	 * {@link #getIndex getIndex} methods to retrieve this information.<br />
+	 * <br />
+	 * <b>Concurrent Access</b><br />
+	 * <br />
+	 * Unless otherwise specified, the methods defined in this class are not
+	 * thread-safe. Multiple threads that need to access a single
+	 * object concurrently should synchronize amongst themselves and
+	 * provide the necessary locking. Multiple threads each manipulating
+	 * separate objects need not synchronize.
+	 *
+	 * @see CertPathValidator
+	 **/
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+    [Serializable]
+#endif
+    public class PkixCertPathValidatorException
+        : GeneralSecurityException
+	{
+		private Exception cause;
+		private PkixCertPath certPath;
+		private int index = -1;
+
+		public PkixCertPathValidatorException() : base() { }
+
+		/// <summary>
+		/// Creates a <code>PkixCertPathValidatorException</code> with the given detail
+		/// message. A detail message is a <code>String</code> that describes this
+		/// particular exception. 
+		/// </summary>
+		/// <param name="message">the detail message</param>
+		public PkixCertPathValidatorException(string message) : base(message) { }
+
+		/// <summary>
+		/// Creates a <code>PkixCertPathValidatorException</code> with the specified
+		/// detail message and cause.
+		/// </summary>
+		/// <param name="message">the detail message</param>
+		/// <param name="cause">the cause (which is saved for later retrieval by the
+		/// {@link #getCause getCause()} method). (A <code>null</code>
+		/// value is permitted, and indicates that the cause is
+		/// nonexistent or unknown.)</param>
+		public PkixCertPathValidatorException(string message, Exception cause) : base(message)
+		{
+			this.cause = cause;
+		}
+
+		/// <summary>
+		/// Creates a <code>PkixCertPathValidatorException</code> with the specified
+		/// detail message, cause, certification path, and index.
+		/// </summary>
+		/// <param name="message">the detail message (or <code>null</code> if none)</param>
+		/// <param name="cause">the cause (or <code>null</code> if none)</param>
+		/// <param name="certPath">the certification path that was in the process of being
+		/// validated when the error was encountered</param>
+		/// <param name="index">the index of the certificate in the certification path that</param>																																																																																   * 
+		public PkixCertPathValidatorException(
+			string			message,
+			Exception		cause,
+			PkixCertPath	certPath,
+			int				index)
+			: base(message)
+		{
+			if (certPath == null && index != -1)
+			{
+				throw new ArgumentNullException(
+					"certPath = null and index != -1");
+			}
+			if (index < -1
+				|| (certPath != null && index >= certPath.Certificates.Count))
+			{
+				throw new IndexOutOfRangeException(
+					" index < -1 or out of bound of certPath.getCertificates()");
+			}
+
+			this.cause = cause;
+			this.certPath = certPath;
+			this.index = index;
+		}
+
+		//
+		// Prints a stack trace to a <code>PrintWriter</code>, including the
+		// backtrace of the cause, if any.
+		// 
+		// @param pw
+		//            the <code>PrintWriter</code> to use for output
+		//
+		//		public void printStackTrace(PrintWriter pw)
+		//		{
+		//			super.printStackTrace(pw);
+		//			if (getCause() != null)
+		//			{
+		//				getCause().printStackTrace(pw);
+		//			}
+		//	}
+		//}
+
+
+		//	/**
+		//	 * Creates a <code>CertPathValidatorException</code> that wraps the
+		//	 * specified throwable. This allows any exception to be converted into a
+		//	 * <code>CertPathValidatorException</code>, while retaining information
+		//	 * about the wrapped exception, which may be useful for debugging. The
+		//	 * detail message is set to (<code>cause==null ? null : cause.toString()
+		//	 * </code>)
+		//	 * (which typically contains the class and detail message of cause).
+		//	 * 
+		//	 * @param cause
+		//	 *            the cause (which is saved for later retrieval by the
+		//	 *            {@link #getCause getCause()} method). (A <code>null</code>
+		//	 *            value is permitted, and indicates that the cause is
+		//	 *            nonexistent or unknown.)
+		//	 */
+		//	public PkixCertPathValidatorException(Throwable cause)
+		//	{
+		//		this.cause = cause;
+		//	}
+		//
+
+		/// <summary>
+		/// Returns the detail message for this <code>CertPathValidatorException</code>.
+		/// </summary>
+		/// <returns>the detail message, or <code>null</code> if neither the message nor cause were specified</returns>
+		public override string Message
+		{
+			get
+			{
+				string message = base.Message;
+
+				if (message != null)
+				{
+					return message;
+				}
+
+				if (cause != null)
+				{
+					return cause.Message;
+				}
+
+				return null;
+			}
+		}
+
+	/**
+	 * Returns the certification path that was being validated when the
+	 * exception was thrown.
+	 * 
+	 * @return the <code>CertPath</code> that was being validated when the
+	 *         exception was thrown (or <code>null</code> if not specified)
+	 */
+		public PkixCertPath CertPath
+		{
+			get { return certPath; }
+		}
+
+	/**
+	 * Returns the index of the certificate in the certification path that
+	 * caused the exception to be thrown. Note that the list of certificates in
+	 * a <code>CertPath</code> is zero based. If no index has been set, -1 is
+	 * returned.
+	 * 
+	 * @return the index that has been set, or -1 if none has been set
+	 */
+	public int Index
+	{
+        get { return index; }
+	}
+
+//	/**
+//	 * Returns the cause of this <code>CertPathValidatorException</code> or
+//	 * <code>null</code> if the cause is nonexistent or unknown.
+//	 * 
+//	 * @return the cause of this throwable or <code>null</code> if the cause
+//	 *         is nonexistent or unknown.
+//	 */
+//	public Throwable getCause()
+//	{
+//		return cause;
+//	}
+//
+//	/**
+//	 * Returns a string describing this exception, including a description of
+//	 * the internal (wrapped) cause if there is one.
+//	 * 
+//	 * @return a string representation of this
+//	 *         <code>CertPathValidatorException</code>
+//	 */
+//	public String toString()
+//	{
+//		StringBuffer sb = new StringBuffer();
+//		String s = getMessage();
+//		if (s != null)
+//		{
+//			sb.append(s);
+//		}
+//		if (getIndex() >= 0)
+//		{
+//			sb.append("index in certpath: ").append(getIndex()).append('\n');
+//			sb.append(getCertPath());
+//		}
+//		return sb.toString();
+//	}
+
+	}
+}
diff --git a/crypto/src/pkix/PkixCertPathValidatorResult.cs b/crypto/src/pkix/PkixCertPathValidatorResult.cs
new file mode 100644
index 000000000..c7d81c7f5
--- /dev/null
+++ b/crypto/src/pkix/PkixCertPathValidatorResult.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Text;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/// <summary>
+	/// Summary description for PkixCertPathValidatorResult.
+	/// </summary>
+	public class PkixCertPathValidatorResult
+		//: ICertPathValidatorResult
+	{
+		private TrustAnchor trustAnchor;
+		private PkixPolicyNode policyTree;
+		private AsymmetricKeyParameter subjectPublicKey;
+
+		public PkixPolicyNode PolicyTree
+		{
+			get { return this.policyTree; }
+		}
+
+		public TrustAnchor TrustAnchor
+		{
+			get { return this.trustAnchor; }
+		}
+
+		public AsymmetricKeyParameter SubjectPublicKey
+		{
+			get { return this.subjectPublicKey; }
+		}
+
+		public PkixCertPathValidatorResult(
+			TrustAnchor				trustAnchor,
+			PkixPolicyNode			policyTree,
+			AsymmetricKeyParameter	subjectPublicKey)
+		{
+			if (subjectPublicKey == null)
+			{
+				throw new NullReferenceException("subjectPublicKey must be non-null");
+			}
+			if (trustAnchor == null)
+			{
+				throw new NullReferenceException("trustAnchor must be non-null");
+			}
+			
+			this.trustAnchor = trustAnchor;
+			this.policyTree = policyTree;
+			this.subjectPublicKey = subjectPublicKey;
+		}
+
+		public object Clone()
+		{
+			return new PkixCertPathValidatorResult(this.TrustAnchor, this.PolicyTree, this.SubjectPublicKey);
+		}
+
+		public override String ToString() 
+		{
+			StringBuilder sB = new StringBuilder();
+			sB.Append("PKIXCertPathValidatorResult: [ \n");
+			sB.Append("  Trust Anchor: ").Append(this.TrustAnchor).Append('\n');
+			sB.Append("  Policy Tree: ").Append(this.PolicyTree).Append('\n');
+			sB.Append("  Subject Public Key: ").Append(this.SubjectPublicKey).Append("\n]");
+			return sB.ToString();
+		}
+
+	}
+}
diff --git a/crypto/src/pkix/PkixCertPathValidatorUtilities.cs b/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
new file mode 100644
index 000000000..305b2de35
--- /dev/null
+++ b/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
@@ -0,0 +1,1194 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.IsisMtt;
+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.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Extension;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/// <summary>
+	/// Summary description for PkixCertPathValidatorUtilities.
+	/// </summary>
+	public class PkixCertPathValidatorUtilities
+	{
+		private static readonly PkixCrlUtilities CrlUtilities = new PkixCrlUtilities();
+
+		internal static readonly string ANY_POLICY = "2.5.29.32.0";
+
+		internal static readonly string CRL_NUMBER = X509Extensions.CrlNumber.Id;
+
+		/// <summary>
+		/// key usage bits
+		/// </summary>
+		internal static readonly int KEY_CERT_SIGN = 5;
+		internal static readonly int CRL_SIGN = 6;
+
+		internal static readonly string[] crlReasons = new string[]
+		{
+			"unspecified",
+			"keyCompromise",
+			"cACompromise",
+			"affiliationChanged",
+			"superseded",
+			"cessationOfOperation",
+			"certificateHold",
+			"unknown",
+			"removeFromCRL",
+			"privilegeWithdrawn",
+			"aACompromise"
+		};
+
+		/// <summary>
+		/// Search the given Set of TrustAnchor's for one that is the
+		/// issuer of the given X509 certificate.
+		/// </summary>
+		/// <param name="cert">the X509 certificate</param>
+		/// <param name="trustAnchors">a Set of TrustAnchor's</param>
+		/// <returns>the <code>TrustAnchor</code> object if found or
+		/// <code>null</code> if not.
+		/// </returns>
+		/// @exception
+		internal static TrustAnchor FindTrustAnchor(
+			X509Certificate	cert,
+			ISet			trustAnchors)
+		{
+			IEnumerator iter = trustAnchors.GetEnumerator();
+			TrustAnchor trust = null;
+			AsymmetricKeyParameter trustPublicKey = null;
+			Exception invalidKeyEx = null;
+
+			X509CertStoreSelector certSelectX509 = new X509CertStoreSelector();
+
+			try
+			{
+				certSelectX509.Subject = GetIssuerPrincipal(cert);
+			}
+			catch (IOException ex)
+			{
+				throw new Exception("Cannot set subject search criteria for trust anchor.", ex);
+			}
+
+			while (iter.MoveNext() && trust == null)
+			{
+				trust = (TrustAnchor) iter.Current;
+				if (trust.TrustedCert != null)
+				{
+					if (certSelectX509.Match(trust.TrustedCert))
+					{
+						trustPublicKey = trust.TrustedCert.GetPublicKey();
+					}
+					else
+					{
+						trust = null;
+					}
+				}
+				else if (trust.CAName != null && trust.CAPublicKey != null)
+				{
+					try
+					{
+						X509Name certIssuer = GetIssuerPrincipal(cert);
+						X509Name caName = new X509Name(trust.CAName);
+
+						if (certIssuer.Equivalent(caName, true))
+						{
+							trustPublicKey = trust.CAPublicKey;
+						}
+						else
+						{
+							trust = null;
+						}
+					}
+					catch (InvalidParameterException)
+					{
+						trust = null;
+					}
+				}
+				else
+				{
+					trust = null;
+				}
+
+				if (trustPublicKey != null)
+				{
+					try
+					{
+						cert.Verify(trustPublicKey);
+					}
+					catch (Exception ex)
+					{
+						invalidKeyEx = ex;
+						trust = null;
+					}
+				}
+			}
+
+			if (trust == null && invalidKeyEx != null)
+			{
+				throw new Exception("TrustAnchor found but certificate validation failed.", invalidKeyEx);
+			}
+
+			return trust;
+		}
+
+		internal static void AddAdditionalStoresFromAltNames(
+			X509Certificate	cert,
+			PkixParameters	pkixParams)
+		{
+			// if in the IssuerAltName extension an URI
+			// is given, add an additinal X.509 store
+			if (cert.GetIssuerAlternativeNames() != null)
+			{
+				IEnumerator it = cert.GetIssuerAlternativeNames().GetEnumerator();
+				while (it.MoveNext())
+				{
+					// look for URI
+					IList list = (IList)it.Current;
+					//if (list[0].Equals(new Integer(GeneralName.UniformResourceIdentifier)))
+					if (list[0].Equals(GeneralName.UniformResourceIdentifier))
+					{
+						// found
+						string temp = (string)list[1];
+						PkixCertPathValidatorUtilities.AddAdditionalStoreFromLocation(temp, pkixParams);
+					}
+				}
+			}
+		}
+
+		internal static DateTime GetValidDate(PkixParameters paramsPKIX)
+		{
+			DateTimeObject validDate = paramsPKIX.Date;
+
+			if (validDate == null)
+				return DateTime.UtcNow;
+
+			return validDate.Value;
+		}
+
+		/// <summary>
+		/// Returns the issuer of an attribute certificate or certificate.
+		/// </summary>
+		/// <param name="cert">The attribute certificate or certificate.</param>
+		/// <returns>The issuer as <code>X500Principal</code>.</returns>
+		internal static X509Name GetIssuerPrincipal(
+			object cert)
+		{
+			if (cert is X509Certificate)
+			{
+				return ((X509Certificate)cert).IssuerDN;
+			}
+			else
+			{
+				return ((IX509AttributeCertificate)cert).Issuer.GetPrincipals()[0];
+			}
+		}
+
+		internal static bool IsSelfIssued(
+			X509Certificate cert)
+		{
+			return cert.SubjectDN.Equivalent(cert.IssuerDN, true);
+		}
+
+		internal static AlgorithmIdentifier GetAlgorithmIdentifier(
+			AsymmetricKeyParameter key)
+		{
+			try
+			{
+				SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(key);
+
+				return info.AlgorithmID;
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException("Subject public key cannot be decoded.", e);
+			}
+		}
+
+		internal static bool IsAnyPolicy(
+			ISet policySet)
+		{
+			return policySet == null || policySet.Contains(ANY_POLICY) || policySet.Count == 0;
+		}
+
+		internal static void AddAdditionalStoreFromLocation(
+			string			location,
+			PkixParameters	pkixParams)
+		{
+			if (pkixParams.IsAdditionalLocationsEnabled)
+			{
+				try
+				{
+					if (location.StartsWith("ldap://"))
+					{
+						// ldap://directory.d-trust.net/CN=D-TRUST
+						// Qualified CA 2003 1:PN,O=D-Trust GmbH,C=DE
+						// skip "ldap://"
+						location = location.Substring(7);
+						// after first / baseDN starts
+						string url;//, baseDN;
+						int slashPos = location.IndexOf('/');
+						if (slashPos != -1)
+						{
+							url = "ldap://" + location.Substring(0, slashPos);
+//							baseDN = location.Substring(slashPos);
+						}
+						else
+						{
+							url = "ldap://" + location;
+//							baseDN = nsull;
+						}
+
+						throw Platform.CreateNotImplementedException("LDAP cert/CRL stores");
+
+						// use all purpose parameters
+						//X509LDAPCertStoreParameters ldapParams = new X509LDAPCertStoreParameters.Builder(
+						//                                url, baseDN).build();
+						//pkixParams.AddAdditionalStore(X509Store.getInstance(
+						//    "CERTIFICATE/LDAP", ldapParams));
+						//pkixParams.AddAdditionalStore(X509Store.getInstance(
+						//    "CRL/LDAP", ldapParams));
+						//pkixParams.AddAdditionalStore(X509Store.getInstance(
+						//    "ATTRIBUTECERTIFICATE/LDAP", ldapParams));
+						//pkixParams.AddAdditionalStore(X509Store.getInstance(
+						//    "CERTIFICATEPAIR/LDAP", ldapParams));
+					}
+				}
+				catch (Exception)
+				{
+					// cannot happen
+					throw new Exception("Exception adding X.509 stores.");
+				}
+			}
+		}
+
+		private static BigInteger GetSerialNumber(
+			object cert)
+		{
+			if (cert is X509Certificate)
+			{
+				return ((X509Certificate)cert).SerialNumber;
+			}
+			else
+			{
+				return ((X509V2AttributeCertificate)cert).SerialNumber;
+			}
+		}
+
+		//
+		// policy checking
+		//
+
+		internal static ISet GetQualifierSet(Asn1Sequence qualifiers)
+		{
+			ISet pq = new HashSet();
+
+			if (qualifiers == null)
+			{
+				return pq;
+			}
+
+			foreach (Asn1Encodable ae in qualifiers)
+			{
+				try
+				{
+//					pq.Add(PolicyQualifierInfo.GetInstance(Asn1Object.FromByteArray(ae.GetEncoded())));
+					pq.Add(PolicyQualifierInfo.GetInstance(ae.ToAsn1Object()));
+				}
+				catch (IOException ex)
+				{
+					throw new PkixCertPathValidatorException("Policy qualifier info cannot be decoded.", ex);
+				}
+			}
+
+			return pq;
+		}
+
+		internal static PkixPolicyNode RemovePolicyNode(
+			PkixPolicyNode validPolicyTree,
+			IList[] policyNodes,
+			PkixPolicyNode _node)
+		{
+			PkixPolicyNode _parent = (PkixPolicyNode)_node.Parent;
+
+			if (validPolicyTree == null)
+			{
+				return null;
+			}
+
+			if (_parent == null)
+			{
+				for (int j = 0; j < policyNodes.Length; j++)
+				{
+                    policyNodes[j] = Platform.CreateArrayList();
+				}
+
+				return null;
+			}
+			else
+			{
+				_parent.RemoveChild(_node);
+				RemovePolicyNodeRecurse(policyNodes, _node);
+
+				return validPolicyTree;
+			}
+		}
+
+		private static void RemovePolicyNodeRecurse(IList[] policyNodes, PkixPolicyNode _node)
+		{
+			policyNodes[_node.Depth].Remove(_node);
+
+			if (_node.HasChildren)
+			{
+				foreach (PkixPolicyNode _child in _node.Children)
+				{
+					RemovePolicyNodeRecurse(policyNodes, _child);
+				}
+			}
+		}
+
+		internal static void PrepareNextCertB1(
+			int i,
+			IList[] policyNodes,
+			string id_p,
+			IDictionary m_idp,
+			X509Certificate cert)
+		{
+			bool idp_found = false;
+			IEnumerator nodes_i = policyNodes[i].GetEnumerator();
+			while (nodes_i.MoveNext())
+			{
+				PkixPolicyNode node = (PkixPolicyNode)nodes_i.Current;
+				if (node.ValidPolicy.Equals(id_p))
+				{
+					idp_found = true;
+					node.ExpectedPolicies = (ISet)m_idp[id_p];
+					break;
+				}
+			}
+
+			if (!idp_found)
+			{
+				nodes_i = policyNodes[i].GetEnumerator();
+				while (nodes_i.MoveNext())
+				{
+					PkixPolicyNode node = (PkixPolicyNode)nodes_i.Current;
+					if (ANY_POLICY.Equals(node.ValidPolicy))
+					{
+						ISet pq = null;
+						Asn1Sequence policies = null;
+						try
+						{
+							policies = DerSequence.GetInstance(GetExtensionValue(cert, X509Extensions.CertificatePolicies));
+						}
+						catch (Exception e)
+						{
+							throw new Exception("Certificate policies cannot be decoded.", e);
+						}
+
+						IEnumerator enm = policies.GetEnumerator();
+						while (enm.MoveNext())
+						{
+							PolicyInformation pinfo = null;
+
+							try
+							{
+								pinfo = PolicyInformation.GetInstance(enm.Current);
+							}
+							catch (Exception ex)
+							{
+								throw new Exception("Policy information cannot be decoded.", ex);
+							}
+
+							if (ANY_POLICY.Equals(pinfo.PolicyIdentifier.Id))
+							{
+								try
+								{
+									pq = GetQualifierSet(pinfo.PolicyQualifiers);
+								}
+								catch (PkixCertPathValidatorException ex)
+								{
+									throw new PkixCertPathValidatorException(
+										"Policy qualifier info set could not be built.", ex);
+								}
+								break;
+							}
+						}
+						bool ci = false;
+						ISet critExtOids = cert.GetCriticalExtensionOids();
+						if (critExtOids != null)
+						{
+							ci = critExtOids.Contains(X509Extensions.CertificatePolicies.Id);
+						}
+
+						PkixPolicyNode p_node = (PkixPolicyNode)node.Parent;
+						if (ANY_POLICY.Equals(p_node.ValidPolicy))
+						{
+							PkixPolicyNode c_node = new PkixPolicyNode(
+                                Platform.CreateArrayList(), i,
+								(ISet)m_idp[id_p],
+								p_node, pq, id_p, ci);
+							p_node.AddChild(c_node);
+							policyNodes[i].Add(c_node);
+						}
+						break;
+					}
+				}
+			}
+		}
+
+		internal static PkixPolicyNode PrepareNextCertB2(
+			int				i,
+			IList[]			policyNodes,
+			string			id_p,
+			PkixPolicyNode	validPolicyTree)
+		{
+			int pos = 0;
+
+			// Copy to avoid RemoveAt calls interfering with enumeration
+            foreach (PkixPolicyNode node in Platform.CreateArrayList(policyNodes[i]))
+			{
+				if (node.ValidPolicy.Equals(id_p))
+				{
+					PkixPolicyNode p_node = (PkixPolicyNode)node.Parent;
+					p_node.RemoveChild(node);
+
+					// Removal of element at current iterator position not supported in C#
+					//nodes_i.remove();
+					policyNodes[i].RemoveAt(pos);
+
+					for (int k = (i - 1); k >= 0; k--)
+					{
+						IList nodes = policyNodes[k];
+						for (int l = 0; l < nodes.Count; l++)
+						{
+							PkixPolicyNode node2 = (PkixPolicyNode)nodes[l];
+							if (!node2.HasChildren)
+							{
+								validPolicyTree = RemovePolicyNode(validPolicyTree, policyNodes, node2);
+								if (validPolicyTree == null)
+									break;
+							}
+						}
+					}
+				}
+				else
+				{
+					++pos;
+				}
+			}
+			return validPolicyTree;
+		}
+
+		internal static void GetCertStatus(
+			DateTime validDate,
+			X509Crl crl,
+			Object cert,
+			CertStatus certStatus)
+		{
+			X509Crl bcCRL = null;
+
+			try
+			{
+				bcCRL = new X509Crl(CertificateList.GetInstance((Asn1Sequence)Asn1Sequence.FromByteArray(crl.GetEncoded())));
+			}
+			catch (Exception exception)
+			{
+				throw new Exception("Bouncy Castle X509Crl could not be created.", exception);
+			}
+
+			X509CrlEntry crl_entry = (X509CrlEntry)bcCRL.GetRevokedCertificate(GetSerialNumber(cert));
+
+			if (crl_entry == null)
+				return;
+
+			X509Name issuer = GetIssuerPrincipal(cert);
+
+			if (issuer.Equivalent(crl_entry.GetCertificateIssuer(), true)
+				|| issuer.Equivalent(crl.IssuerDN, true))
+			{
+				DerEnumerated reasonCode = null;
+				if (crl_entry.HasExtensions)
+				{
+					try
+					{
+						reasonCode = DerEnumerated.GetInstance(
+							GetExtensionValue(crl_entry, X509Extensions.ReasonCode));
+					}
+					catch (Exception e)
+					{
+						new Exception(
+							"Reason code CRL entry extension could not be decoded.",
+							e);
+					}
+				}
+
+				// for reason keyCompromise, caCompromise, aACompromise or
+				// unspecified
+				if (!(validDate.Ticks < crl_entry.RevocationDate.Ticks)
+					|| reasonCode == null
+					|| reasonCode.Value.TestBit(0)
+					|| reasonCode.Value.TestBit(1)
+					|| reasonCode.Value.TestBit(2)
+					|| reasonCode.Value.TestBit(8))
+				{
+					if (reasonCode != null) // (i) or (j) (1)
+					{
+						certStatus.Status = reasonCode.Value.SignValue;
+					}
+					else // (i) or (j) (2)
+					{
+						certStatus.Status = CrlReason.Unspecified;
+					}
+					certStatus.RevocationDate = new DateTimeObject(crl_entry.RevocationDate);
+				}
+			}
+		}
+
+		/**
+		* Return the next working key inheriting DSA parameters if necessary.
+		* <p>
+		* This methods inherits DSA parameters from the indexed certificate or
+		* previous certificates in the certificate chain to the returned
+		* <code>PublicKey</code>. The list is searched upwards, meaning the end
+		* certificate is at position 0 and previous certificates are following.
+		* </p>
+		* <p>
+		* If the indexed certificate does not contain a DSA key this method simply
+		* returns the public key. If the DSA key already contains DSA parameters
+		* the key is also only returned.
+		* </p>
+		*
+		* @param certs The certification path.
+		* @param index The index of the certificate which contains the public key
+		*            which should be extended with DSA parameters.
+		* @return The public key of the certificate in list position
+		*         <code>index</code> extended with DSA parameters if applicable.
+		* @throws Exception if DSA parameters cannot be inherited.
+		*/
+		internal static AsymmetricKeyParameter GetNextWorkingKey(
+			IList	certs,
+			int		index)
+		{
+			//Only X509Certificate
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			AsymmetricKeyParameter pubKey = cert.GetPublicKey();
+
+			if (!(pubKey is DsaPublicKeyParameters))
+				return pubKey;
+
+			DsaPublicKeyParameters dsaPubKey = (DsaPublicKeyParameters)pubKey;
+
+			if (dsaPubKey.Parameters != null)
+				return dsaPubKey;
+
+			for (int i = index + 1; i < certs.Count; i++)
+			{
+				X509Certificate parentCert = (X509Certificate)certs[i];
+				pubKey = parentCert.GetPublicKey();
+
+				if (!(pubKey is DsaPublicKeyParameters))
+				{
+					throw new PkixCertPathValidatorException(
+						"DSA parameters cannot be inherited from previous certificate.");
+				}
+
+				DsaPublicKeyParameters prevDSAPubKey = (DsaPublicKeyParameters)pubKey;
+
+				if (prevDSAPubKey.Parameters == null)
+					continue;
+
+				DsaParameters dsaParams = prevDSAPubKey.Parameters;
+
+				try
+				{
+					return new DsaPublicKeyParameters(dsaPubKey.Y, dsaParams);
+				}
+				catch (Exception exception)
+				{
+					throw new Exception(exception.Message);
+				}
+			}
+
+			throw new PkixCertPathValidatorException("DSA parameters cannot be inherited from previous certificate.");
+		}
+
+		internal static DateTime GetValidCertDateFromValidityModel(
+			PkixParameters	paramsPkix,
+			PkixCertPath	certPath,
+			int				index)
+		{
+			if (paramsPkix.ValidityModel != PkixParameters.ChainValidityModel)
+			{
+				return GetValidDate(paramsPkix);
+			}
+
+			// if end cert use given signing/encryption/... time
+			if (index <= 0)
+			{
+				return PkixCertPathValidatorUtilities.GetValidDate(paramsPkix);
+				// else use time when previous cert was created
+			}
+
+			if (index - 1 == 0)
+			{
+				DerGeneralizedTime dateOfCertgen = null;
+				try
+				{
+					X509Certificate cert = (X509Certificate)certPath.Certificates[index - 1];
+					Asn1OctetString extVal = cert.GetExtensionValue(
+						IsisMttObjectIdentifiers.IdIsisMttATDateOfCertGen);
+					dateOfCertgen = DerGeneralizedTime.GetInstance(extVal);
+				}
+				catch (ArgumentException)
+				{
+					throw new Exception(
+						"Date of cert gen extension could not be read.");
+				}
+				if (dateOfCertgen != null)
+				{
+					try
+					{
+						return dateOfCertgen.ToDateTime();
+					}
+					catch (ArgumentException e)
+					{
+						throw new Exception(
+							"Date from date of cert gen extension could not be parsed.",
+							e);
+					}
+				}
+			}
+
+			return ((X509Certificate)certPath.Certificates[index - 1]).NotBefore;
+		}
+
+		/// <summary>
+		/// Return a Collection of all certificates or attribute certificates found
+		/// in the X509Store's that are matching the certSelect criteriums.
+		/// </summary>
+		/// <param name="certSelect">a {@link Selector} object that will be used to select
+		/// the certificates</param>
+		/// <param name="certStores">a List containing only X509Store objects. These
+		/// are used to search for certificates.</param>
+		/// <returns>a Collection of all found <see cref="X509Certificate "/> or
+		/// org.bouncycastle.x509.X509AttributeCertificate objects.
+		/// May be empty but never <code>null</code>.</returns>
+		/// <exception cref="Exception"></exception>
+		internal static ICollection FindCertificates(
+			X509CertStoreSelector	certSelect,
+			IList					certStores)
+		{
+			ISet certs = new HashSet();
+
+			foreach (IX509Store certStore in certStores)
+			{
+				try
+				{
+//					certs.AddAll(certStore.GetMatches(certSelect));
+					foreach (X509Certificate c in certStore.GetMatches(certSelect))
+					{
+						certs.Add(c);
+					}
+				}
+				catch (Exception e)
+				{
+					throw new Exception("Problem while picking certificates from X.509 store.", e);
+				}
+			}
+
+			return certs;
+		}
+
+		/**
+		* Add the CRL issuers from the cRLIssuer field of the distribution point or
+		* from the certificate if not given to the issuer criterion of the
+		* <code>selector</code>.
+		* <p>
+		* The <code>issuerPrincipals</code> are a collection with a single
+		* <code>X500Principal</code> for <code>X509Certificate</code>s. For
+		* {@link X509AttributeCertificate}s the issuer may contain more than one
+		* <code>X500Principal</code>.
+		* </p>
+		*
+		* @param dp The distribution point.
+		* @param issuerPrincipals The issuers of the certificate or attribute
+		*            certificate which contains the distribution point.
+		* @param selector The CRL selector.
+		* @param pkixParams The PKIX parameters containing the cert stores.
+		* @throws Exception if an exception occurs while processing.
+		* @throws ClassCastException if <code>issuerPrincipals</code> does not
+		* contain only <code>X500Principal</code>s.
+		*/
+		internal static void GetCrlIssuersFromDistributionPoint(
+			DistributionPoint		dp,
+			ICollection				issuerPrincipals,
+			X509CrlStoreSelector	selector,
+			PkixParameters			pkixParams)
+		{
+            IList issuers = Platform.CreateArrayList();
+			// indirect CRL
+			if (dp.CrlIssuer != null)
+			{
+				GeneralName[] genNames = dp.CrlIssuer.GetNames();
+				// look for a DN
+				for (int j = 0; j < genNames.Length; j++)
+				{
+					if (genNames[j].TagNo == GeneralName.DirectoryName)
+					{
+						try
+						{
+							issuers.Add(X509Name.GetInstance(genNames[j].Name.ToAsn1Object()));
+						}
+						catch (IOException e)
+						{
+							throw new Exception(
+								"CRL issuer information from distribution point cannot be decoded.",
+								e);
+						}
+					}
+				}
+			}
+			else
+			{
+				/*
+				 * certificate issuer is CRL issuer, distributionPoint field MUST be
+				 * present.
+				 */
+				if (dp.DistributionPointName == null)
+				{
+					throw new Exception(
+						"CRL issuer is omitted from distribution point but no distributionPoint field present.");
+				}
+
+				// add and check issuer principals
+				for (IEnumerator it = issuerPrincipals.GetEnumerator(); it.MoveNext(); )
+				{
+					issuers.Add((X509Name)it.Current);
+				}
+			}
+			// TODO: is not found although this should correctly add the rel name. selector of Sun is buggy here or PKI test case is invalid
+			// distributionPoint
+			//        if (dp.getDistributionPoint() != null)
+			//        {
+			//            // look for nameRelativeToCRLIssuer
+			//            if (dp.getDistributionPoint().getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER)
+			//            {
+			//                // append fragment to issuer, only one
+			//                // issuer can be there, if this is given
+			//                if (issuers.size() != 1)
+			//                {
+			//                    throw new AnnotatedException(
+			//                        "nameRelativeToCRLIssuer field is given but more than one CRL issuer is given.");
+			//                }
+			//                DEREncodable relName = dp.getDistributionPoint().getName();
+			//                Iterator it = issuers.iterator();
+			//                List issuersTemp = new ArrayList(issuers.size());
+			//                while (it.hasNext())
+			//                {
+			//                    Enumeration e = null;
+			//                    try
+			//                    {
+			//                        e = ASN1Sequence.getInstance(
+			//                            new ASN1InputStream(((X500Principal) it.next())
+			//                                .getEncoded()).readObject()).getObjects();
+			//                    }
+			//                    catch (IOException ex)
+			//                    {
+			//                        throw new AnnotatedException(
+			//                            "Cannot decode CRL issuer information.", ex);
+			//                    }
+			//                    ASN1EncodableVector v = new ASN1EncodableVector();
+			//                    while (e.hasMoreElements())
+			//                    {
+			//                        v.add((DEREncodable) e.nextElement());
+			//                    }
+			//                    v.add(relName);
+			//                    issuersTemp.add(new X500Principal(new DERSequence(v)
+			//                        .getDEREncoded()));
+			//                }
+			//                issuers.clear();
+			//                issuers.addAll(issuersTemp);
+			//            }
+			//        }
+
+			selector.Issuers = issuers;
+		}
+
+		/**
+		 * Fetches complete CRLs according to RFC 3280.
+		 *
+		 * @param dp The distribution point for which the complete CRL
+		 * @param cert The <code>X509Certificate</code> or
+		 *            {@link org.bouncycastle.x509.X509AttributeCertificate} for
+		 *            which the CRL should be searched.
+		 * @param currentDate The date for which the delta CRLs must be valid.
+		 * @param paramsPKIX The extended PKIX parameters.
+		 * @return A <code>Set</code> of <code>X509CRL</code>s with complete
+		 *         CRLs.
+		 * @throws Exception if an exception occurs while picking the CRLs
+		 *             or no CRLs are found.
+		 */
+		internal static ISet GetCompleteCrls(
+			DistributionPoint	dp,
+			object				cert,
+			DateTime			currentDate,
+			PkixParameters		paramsPKIX)
+		{
+			X509CrlStoreSelector crlselect = new X509CrlStoreSelector();
+			try
+			{
+				ISet issuers = new HashSet();
+				if (cert is X509V2AttributeCertificate)
+				{
+					issuers.Add(((X509V2AttributeCertificate)cert)
+						.Issuer.GetPrincipals()[0]);
+				}
+				else
+				{
+					issuers.Add(GetIssuerPrincipal(cert));
+				}
+				PkixCertPathValidatorUtilities.GetCrlIssuersFromDistributionPoint(dp, issuers, crlselect, paramsPKIX);
+			}
+			catch (Exception e)
+			{
+				new Exception("Could not get issuer information from distribution point.", e);
+			}
+
+			if (cert is X509Certificate)
+			{
+				crlselect.CertificateChecking = (X509Certificate)cert;
+			}
+			else if (cert is X509V2AttributeCertificate)
+			{
+				crlselect.AttrCertChecking = (IX509AttributeCertificate)cert;
+			}
+
+			crlselect.CompleteCrlEnabled = true;
+			ISet crls = CrlUtilities.FindCrls(crlselect, paramsPKIX, currentDate);
+
+			if (crls.IsEmpty)
+			{
+				if (cert is IX509AttributeCertificate)
+				{
+					IX509AttributeCertificate aCert = (IX509AttributeCertificate)cert;
+
+					throw new Exception("No CRLs found for issuer \"" + aCert.Issuer.GetPrincipals()[0] + "\"");
+				}
+				else
+				{
+					X509Certificate xCert = (X509Certificate)cert;
+
+					throw new Exception("No CRLs found for issuer \"" + xCert.IssuerDN + "\"");
+				}
+			}
+
+			return crls;
+		}
+
+		/**
+		 * Fetches delta CRLs according to RFC 3280 section 5.2.4.
+		 *
+		 * @param currentDate The date for which the delta CRLs must be valid.
+		 * @param paramsPKIX The extended PKIX parameters.
+		 * @param completeCRL The complete CRL the delta CRL is for.
+		 * @return A <code>Set</code> of <code>X509CRL</code>s with delta CRLs.
+		 * @throws Exception if an exception occurs while picking the delta
+		 *             CRLs.
+		 */
+		internal static ISet GetDeltaCrls(
+			DateTime		currentDate,
+			PkixParameters	paramsPKIX,
+			X509Crl			completeCRL)
+		{
+			X509CrlStoreSelector deltaSelect = new X509CrlStoreSelector();
+
+			// 5.2.4 (a)
+			try
+			{
+                IList deltaSelectIssuer = Platform.CreateArrayList();
+				deltaSelectIssuer.Add(completeCRL.IssuerDN);
+				deltaSelect.Issuers = deltaSelectIssuer;
+			}
+			catch (IOException e)
+			{
+				new Exception("Cannot extract issuer from CRL.", e);
+			}
+
+			BigInteger completeCRLNumber = null;
+			try
+			{
+				Asn1Object asn1Object = GetExtensionValue(completeCRL, X509Extensions.CrlNumber);
+				if (asn1Object != null)
+				{
+					completeCRLNumber = CrlNumber.GetInstance(asn1Object).PositiveValue;
+				}
+			}
+			catch (Exception e)
+			{
+				throw new Exception(
+					"CRL number extension could not be extracted from CRL.", e);
+			}
+
+			// 5.2.4 (b)
+			byte[] idp = null;
+
+			try
+			{
+				Asn1Object obj = GetExtensionValue(completeCRL, X509Extensions.IssuingDistributionPoint);
+				if (obj != null)
+				{
+					idp = obj.GetDerEncoded();
+				}
+			}
+			catch (Exception e)
+			{
+				throw new Exception(
+					"Issuing distribution point extension value could not be read.",
+					e);
+			}
+
+			// 5.2.4 (d)
+
+			deltaSelect.MinCrlNumber = (completeCRLNumber == null)
+				?	null
+				:	completeCRLNumber.Add(BigInteger.One);
+
+			deltaSelect.IssuingDistributionPoint = idp;
+			deltaSelect.IssuingDistributionPointEnabled = true;
+
+			// 5.2.4 (c)
+			deltaSelect.MaxBaseCrlNumber = completeCRLNumber;
+
+			// find delta CRLs
+			ISet temp = CrlUtilities.FindCrls(deltaSelect, paramsPKIX, currentDate);
+
+			ISet result = new HashSet();
+
+			foreach (X509Crl crl in temp)
+			{
+				if (isDeltaCrl(crl))
+				{
+					result.Add(crl);
+				}
+			}
+
+			return result;
+		}
+
+		private static bool isDeltaCrl(
+			X509Crl crl)
+		{
+			ISet critical = crl.GetCriticalExtensionOids();
+
+			return critical.Contains(X509Extensions.DeltaCrlIndicator.Id);
+		}
+
+		internal static ICollection FindCertificates(
+			X509AttrCertStoreSelector	certSelect,
+			IList						certStores)
+		{
+			ISet certs = new HashSet();
+
+			foreach (IX509Store certStore in certStores)
+			{
+				try
+				{
+//					certs.AddAll(certStore.GetMatches(certSelect));
+					foreach (X509V2AttributeCertificate ac in certStore.GetMatches(certSelect))
+					{
+						certs.Add(ac);
+					}
+				}
+				catch (Exception e)
+				{
+					throw new Exception(
+						"Problem while picking certificates from X.509 store.", e);
+				}
+			}
+
+			return certs;
+		}
+
+		internal static void AddAdditionalStoresFromCrlDistributionPoint(
+			CrlDistPoint	crldp,
+			PkixParameters	pkixParams)
+		{
+			if (crldp != null)
+			{
+				DistributionPoint[] dps = null;
+				try
+				{
+					dps = crldp.GetDistributionPoints();
+				}
+				catch (Exception e)
+				{
+					throw new Exception(
+						"Distribution points could not be read.", e);
+				}
+				for (int i = 0; i < dps.Length; i++)
+				{
+					DistributionPointName dpn = dps[i].DistributionPointName;
+					// look for URIs in fullName
+					if (dpn != null)
+					{
+						if (dpn.PointType == DistributionPointName.FullName)
+						{
+							GeneralName[] genNames = GeneralNames.GetInstance(
+								dpn.Name).GetNames();
+							// look for an URI
+							for (int j = 0; j < genNames.Length; j++)
+							{
+								if (genNames[j].TagNo == GeneralName.UniformResourceIdentifier)
+								{
+									string location = DerIA5String.GetInstance(
+										genNames[j].Name).GetString();
+									PkixCertPathValidatorUtilities.AddAdditionalStoreFromLocation(
+										location, pkixParams);
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+
+		internal static bool ProcessCertD1i(
+			int					index,
+			IList[]				policyNodes,
+			DerObjectIdentifier	pOid,
+			ISet				pq)
+		{
+			IList policyNodeVec = policyNodes[index - 1];
+
+			for (int j = 0; j < policyNodeVec.Count; j++)
+			{
+				PkixPolicyNode node = (PkixPolicyNode)policyNodeVec[j];
+				ISet expectedPolicies = node.ExpectedPolicies;
+
+				if (expectedPolicies.Contains(pOid.Id))
+				{
+					ISet childExpectedPolicies = new HashSet();
+					childExpectedPolicies.Add(pOid.Id);
+
+                    PkixPolicyNode child = new PkixPolicyNode(Platform.CreateArrayList(),
+						index,
+						childExpectedPolicies,
+						node,
+						pq,
+						pOid.Id,
+						false);
+					node.AddChild(child);
+					policyNodes[index].Add(child);
+
+					return true;
+				}
+			}
+
+			return false;
+		}
+
+		internal static void ProcessCertD1ii(
+			int					index,
+			IList[]				policyNodes,
+			DerObjectIdentifier _poid,
+			ISet				_pq)
+		{
+			IList policyNodeVec = policyNodes[index - 1];
+
+			for (int j = 0; j < policyNodeVec.Count; j++)
+			{
+				PkixPolicyNode _node = (PkixPolicyNode)policyNodeVec[j];
+
+				if (ANY_POLICY.Equals(_node.ValidPolicy))
+				{
+					ISet _childExpectedPolicies = new HashSet();
+					_childExpectedPolicies.Add(_poid.Id);
+
+                    PkixPolicyNode _child = new PkixPolicyNode(Platform.CreateArrayList(),
+						index,
+						_childExpectedPolicies,
+						_node,
+						_pq,
+						_poid.Id,
+						false);
+					_node.AddChild(_child);
+					policyNodes[index].Add(_child);
+					return;
+				}
+			}
+		}
+
+		/**
+		* Find the issuer certificates of a given certificate.
+		*
+		* @param cert
+		*            The certificate for which an issuer should be found.
+		* @param pkixParams
+		* @return A <code>Collection</code> object containing the issuer
+		*         <code>X509Certificate</code>s. Never <code>null</code>.
+		*
+		* @exception Exception
+		*                if an error occurs.
+		*/
+		internal static ICollection FindIssuerCerts(
+			X509Certificate			cert,
+			PkixBuilderParameters	pkixParams)
+		{
+			X509CertStoreSelector certSelect = new X509CertStoreSelector();
+			ISet certs = new HashSet();
+			try
+			{
+				certSelect.Subject = cert.IssuerDN;
+			}
+			catch (IOException ex)
+			{
+				throw new Exception(
+					"Subject criteria for certificate selector to find issuer certificate could not be set.", ex);
+			}
+
+			try
+			{
+                certs.AddAll(PkixCertPathValidatorUtilities.FindCertificates(certSelect, pkixParams.GetStores()));
+                certs.AddAll(PkixCertPathValidatorUtilities.FindCertificates(certSelect, pkixParams.GetAdditionalStores()));
+			}
+			catch (Exception e)
+			{
+				throw new Exception("Issuer certificate cannot be searched.", e);
+			}
+
+			return certs;
+		}
+
+		/// <summary>
+		/// Extract the value of the given extension, if it exists.
+		/// </summary>
+		/// <param name="ext">The extension object.</param>
+		/// <param name="oid">The object identifier to obtain.</param>
+		/// <returns>Asn1Object</returns>
+		/// <exception cref="Exception">if the extension cannot be read.</exception>
+		internal static Asn1Object GetExtensionValue(
+			IX509Extension		ext,
+			DerObjectIdentifier	oid)
+		{
+			Asn1OctetString bytes = ext.GetExtensionValue(oid);
+
+			if (bytes == null)
+				return null;
+
+			return X509ExtensionUtilities.FromExtensionValue(bytes);
+		}
+	}
+}
diff --git a/crypto/src/pkix/PkixCrlUtilities.cs b/crypto/src/pkix/PkixCrlUtilities.cs
new file mode 100644
index 000000000..c386b8a05
--- /dev/null
+++ b/crypto/src/pkix/PkixCrlUtilities.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Pkix
+{
+	public class PkixCrlUtilities
+	{
+		public virtual ISet FindCrls(X509CrlStoreSelector crlselect, PkixParameters paramsPkix, DateTime currentDate)
+		{
+			ISet initialSet = new HashSet();
+
+			// get complete CRL(s)
+			try
+			{
+				initialSet.AddAll(FindCrls(crlselect, paramsPkix.GetAdditionalStores()));
+				initialSet.AddAll(FindCrls(crlselect, paramsPkix.GetStores()));
+			}
+			catch (Exception e)
+			{
+				throw new Exception("Exception obtaining complete CRLs.", e);
+			}
+
+			ISet finalSet = new HashSet();
+			DateTime validityDate = currentDate;
+
+			if (paramsPkix.Date != null)
+			{
+				validityDate = paramsPkix.Date.Value;
+			}
+
+			// based on RFC 5280 6.3.3
+			foreach (X509Crl crl in initialSet)
+			{
+				if (crl.NextUpdate.Value.CompareTo(validityDate) > 0)
+				{
+					X509Certificate cert = crlselect.CertificateChecking;
+
+					if (cert != null)
+					{
+						if (crl.ThisUpdate.CompareTo(cert.NotAfter) < 0)
+						{
+							finalSet.Add(crl);
+						}
+					}
+					else
+					{
+						finalSet.Add(crl);
+					}
+				}
+			}
+
+			return finalSet;
+		}
+
+		public virtual ISet FindCrls(X509CrlStoreSelector crlselect, PkixParameters paramsPkix)
+		{
+			ISet completeSet = new HashSet();
+
+			// get complete CRL(s)
+			try
+			{
+				completeSet.AddAll(FindCrls(crlselect, paramsPkix.GetStores()));
+			}
+			catch (Exception e)
+			{
+				throw new Exception("Exception obtaining complete CRLs.", e);
+			}
+
+			return completeSet;
+		}
+
+		/// <summary>
+		/// crl checking
+		/// Return a Collection of all CRLs found in the X509Store's that are
+		/// matching the crlSelect criteriums.
+		/// </summary>
+		/// <param name="crlSelect">a {@link X509CRLStoreSelector} object that will be used
+		/// to select the CRLs</param>
+		/// <param name="crlStores">a List containing only {@link org.bouncycastle.x509.X509Store
+		/// X509Store} objects. These are used to search for CRLs</param>
+		/// <returns>a Collection of all found {@link X509CRL X509CRL} objects. May be
+		/// empty but never <code>null</code>.
+		/// </returns>
+		private ICollection FindCrls(X509CrlStoreSelector crlSelect, IList crlStores)
+		{
+			ISet crls = new HashSet();
+
+			Exception lastException = null;
+			bool foundValidStore = false;
+
+			foreach (IX509Store store in crlStores)
+			{
+				try
+				{
+					crls.AddAll(store.GetMatches(crlSelect));
+					foundValidStore = true;
+				}
+				catch (X509StoreException e)
+				{
+					lastException = new Exception("Exception searching in X.509 CRL store.", e);
+				}
+			}
+
+	        if (!foundValidStore && lastException != null)
+	            throw lastException;
+
+			return crls;
+		}
+	}
+}
diff --git a/crypto/src/pkix/PkixNameConstraintValidator.cs b/crypto/src/pkix/PkixNameConstraintValidator.cs
new file mode 100644
index 000000000..535f95174
--- /dev/null
+++ b/crypto/src/pkix/PkixNameConstraintValidator.cs
@@ -0,0 +1,1937 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Pkix
+{
+    public class PkixNameConstraintValidator
+    {
+        private ISet excludedSubtreesDN = new HashSet();
+
+        private ISet excludedSubtreesDNS = new HashSet();
+
+        private ISet excludedSubtreesEmail = new HashSet();
+
+        private ISet excludedSubtreesURI = new HashSet();
+
+        private ISet excludedSubtreesIP = new HashSet();
+
+        private ISet permittedSubtreesDN;
+
+        private ISet permittedSubtreesDNS;
+
+        private ISet permittedSubtreesEmail;
+
+        private ISet permittedSubtreesURI;
+
+        private ISet permittedSubtreesIP;
+
+        public PkixNameConstraintValidator()
+        {
+        }
+
+        private static bool WithinDNSubtree(
+            Asn1Sequence dns,
+            Asn1Sequence subtree)
+        {
+            if (subtree.Count < 1)
+            {
+                return false;
+            }
+
+            if (subtree.Count > dns.Count)
+            {
+                return false;
+            }
+
+            for (int j = subtree.Count - 1; j >= 0; j--)
+            {
+                if (!(subtree[j].Equals(dns[j])))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        public void CheckPermittedDN(Asn1Sequence dns)
+        //throws PkixNameConstraintValidatorException
+        {
+            CheckPermittedDN(permittedSubtreesDN, dns);
+        }
+
+        public void CheckExcludedDN(Asn1Sequence dns)
+        //throws PkixNameConstraintValidatorException
+        {
+            CheckExcludedDN(excludedSubtreesDN, dns);
+        }
+
+        private void CheckPermittedDN(ISet permitted, Asn1Sequence dns)
+        //throws PkixNameConstraintValidatorException
+        {
+            if (permitted == null)
+            {
+                return;
+            }
+
+            if ((permitted.Count == 0) && dns.Count == 0)
+            {
+                return;
+            }
+
+            IEnumerator it = permitted.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                Asn1Sequence subtree = (Asn1Sequence)it.Current;
+
+                if (WithinDNSubtree(dns, subtree))
+                {
+                    return;
+                }
+            }
+
+            throw new PkixNameConstraintValidatorException(
+                "Subject distinguished name is not from a permitted subtree");
+        }
+
+        private void CheckExcludedDN(ISet excluded, Asn1Sequence dns)
+        //throws PkixNameConstraintValidatorException
+        {
+            if (excluded.IsEmpty)
+            {
+                return;
+            }
+
+            IEnumerator it = excluded.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                Asn1Sequence subtree = (Asn1Sequence)it.Current;
+
+                if (WithinDNSubtree(dns, subtree))
+                {
+                    throw new PkixNameConstraintValidatorException(
+                        "Subject distinguished name is from an excluded subtree");
+                }
+            }
+        }
+
+        private ISet IntersectDN(ISet permitted, ISet dns)
+        {
+            ISet intersect = new HashSet();
+            for (IEnumerator it = dns.GetEnumerator(); it.MoveNext(); )
+            {
+                Asn1Sequence dn = Asn1Sequence.GetInstance(((GeneralSubtree)it
+                    .Current).Base.Name.ToAsn1Object());
+                if (permitted == null)
+                {
+                    if (dn != null)
+                    {
+                        intersect.Add(dn);
+                    }
+                }
+                else
+                {
+                    IEnumerator _iter = permitted.GetEnumerator();
+                    while (_iter.MoveNext())
+                    {
+                        Asn1Sequence subtree = (Asn1Sequence)_iter.Current;
+
+                        if (WithinDNSubtree(dn, subtree))
+                        {
+                            intersect.Add(dn);
+                        }
+                        else if (WithinDNSubtree(subtree, dn))
+                        {
+                            intersect.Add(subtree);
+                        }
+                    }
+                }
+            }
+            return intersect;
+        }
+
+        private ISet UnionDN(ISet excluded, Asn1Sequence dn)
+        {
+            if (excluded.IsEmpty)
+            {
+                if (dn == null)
+                {
+                    return excluded;
+                }
+                excluded.Add(dn);
+
+                return excluded;
+            }
+            else
+            {
+                ISet intersect = new HashSet();
+
+                IEnumerator it = excluded.GetEnumerator();
+                while (it.MoveNext())
+                {
+                    Asn1Sequence subtree = (Asn1Sequence)it.Current;
+
+                    if (WithinDNSubtree(dn, subtree))
+                    {
+                        intersect.Add(subtree);
+                    }
+                    else if (WithinDNSubtree(subtree, dn))
+                    {
+                        intersect.Add(dn);
+                    }
+                    else
+                    {
+                        intersect.Add(subtree);
+                        intersect.Add(dn);
+                    }
+                }
+
+                return intersect;
+            }
+        }
+
+        private ISet IntersectEmail(ISet permitted, ISet emails)
+        {
+            ISet intersect = new HashSet();
+            for (IEnumerator it = emails.GetEnumerator(); it.MoveNext(); )
+            {
+                String email = ExtractNameAsString(((GeneralSubtree)it.Current)
+                    .Base);
+
+                if (permitted == null)
+                {
+                    if (email != null)
+                    {
+                        intersect.Add(email);
+                    }
+                }
+                else
+                {
+                    IEnumerator it2 = permitted.GetEnumerator();
+                    while (it2.MoveNext())
+                    {
+                        String _permitted = (String)it2.Current;
+
+                        intersectEmail(email, _permitted, intersect);
+                    }
+                }
+            }
+            return intersect;
+        }
+
+        private ISet UnionEmail(ISet excluded, String email)
+        {
+            if (excluded.IsEmpty)
+            {
+                if (email == null)
+                {
+                    return excluded;
+                }
+                excluded.Add(email);
+                return excluded;
+            }
+            else
+            {
+                ISet union = new HashSet();
+
+                IEnumerator it = excluded.GetEnumerator();
+                while (it.MoveNext())
+                {
+                    String _excluded = (String)it.Current;
+
+                    unionEmail(_excluded, email, union);
+                }
+
+                return union;
+            }
+        }
+
+        /**
+         * Returns the intersection of the permitted IP ranges in
+         * <code>permitted</code> with <code>ip</code>.
+         *
+         * @param permitted A <code>Set</code> of permitted IP addresses with
+         *                  their subnet mask as byte arrays.
+         * @param ips       The IP address with its subnet mask.
+         * @return The <code>Set</code> of permitted IP ranges intersected with
+         *         <code>ip</code>.
+         */
+        private ISet IntersectIP(ISet permitted, ISet ips)
+        {
+            ISet intersect = new HashSet();
+            for (IEnumerator it = ips.GetEnumerator(); it.MoveNext(); )
+            {
+                byte[] ip = Asn1OctetString.GetInstance(
+                    ((GeneralSubtree)it.Current).Base.Name).GetOctets();
+                if (permitted == null)
+                {
+                    if (ip != null)
+                    {
+                        intersect.Add(ip);
+                    }
+                }
+                else
+                {
+                    IEnumerator it2 = permitted.GetEnumerator();
+                    while (it2.MoveNext())
+                    {
+                        byte[] _permitted = (byte[])it2.Current;
+                        intersect.AddAll(IntersectIPRange(_permitted, ip));
+                    }
+                }
+            }
+            return intersect;
+        }
+
+        /**
+         * Returns the union of the excluded IP ranges in <code>excluded</code>
+         * with <code>ip</code>.
+         *
+         * @param excluded A <code>Set</code> of excluded IP addresses with their
+         *                 subnet mask as byte arrays.
+         * @param ip       The IP address with its subnet mask.
+         * @return The <code>Set</code> of excluded IP ranges unified with
+         *         <code>ip</code> as byte arrays.
+         */
+        private ISet UnionIP(ISet excluded, byte[] ip)
+        {
+            if (excluded.IsEmpty)
+            {
+                if (ip == null)
+                {
+                    return excluded;
+                }
+                excluded.Add(ip);
+
+                return excluded;
+            }
+            else
+            {
+                ISet union = new HashSet();
+
+                IEnumerator it = excluded.GetEnumerator();
+                while (it.MoveNext())
+                {
+                    byte[] _excluded = (byte[])it.Current;
+                    union.AddAll(UnionIPRange(_excluded, ip));
+                }
+
+                return union;
+            }
+        }
+
+        /**
+         * Calculates the union if two IP ranges.
+         *
+         * @param ipWithSubmask1 The first IP address with its subnet mask.
+         * @param ipWithSubmask2 The second IP address with its subnet mask.
+         * @return A <code>Set</code> with the union of both addresses.
+         */
+        private ISet UnionIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
+        {
+            ISet set = new HashSet();
+
+            // difficult, adding always all IPs is not wrong
+            if (Org.BouncyCastle.Utilities.Arrays.AreEqual(ipWithSubmask1, ipWithSubmask2))
+            {
+                set.Add(ipWithSubmask1);
+            }
+            else
+            {
+                set.Add(ipWithSubmask1);
+                set.Add(ipWithSubmask2);
+            }
+            return set;
+        }
+
+        /**
+         * Calculates the interesction if two IP ranges.
+         *
+         * @param ipWithSubmask1 The first IP address with its subnet mask.
+         * @param ipWithSubmask2 The second IP address with its subnet mask.
+         * @return A <code>Set</code> with the single IP address with its subnet
+         *         mask as a byte array or an empty <code>Set</code>.
+         */
+        private ISet IntersectIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2)
+    {
+        if (ipWithSubmask1.Length != ipWithSubmask2.Length)
+        {
+            //Collections.EMPTY_SET;
+            return new HashSet();
+        }
+
+        byte[][] temp = ExtractIPsAndSubnetMasks(ipWithSubmask1, ipWithSubmask2);
+        byte[] ip1 = temp[0];
+        byte[] subnetmask1 = temp[1];
+        byte[] ip2 = temp[2];
+        byte[] subnetmask2 = temp[3];
+
+        byte[][] minMax = MinMaxIPs(ip1, subnetmask1, ip2, subnetmask2);
+        byte[] min;
+        byte[] max;
+        max = Min(minMax[1], minMax[3]);
+        min = Max(minMax[0], minMax[2]);
+
+        // minimum IP address must be bigger than max
+        if (CompareTo(min, max) == 1)
+        {
+            //return Collections.EMPTY_SET;
+            return new HashSet();
+        }
+        // OR keeps all significant bits
+        byte[] ip = Or(minMax[0], minMax[2]);
+        byte[] subnetmask = Or(subnetmask1, subnetmask2);
+
+            //return new HashSet( ICollectionsingleton(IpWithSubnetMask(ip, subnetmask));
+        ISet hs = new HashSet();
+        hs.Add(IpWithSubnetMask(ip, subnetmask));
+
+            return hs;
+    }
+
+        /**
+         * Concatenates the IP address with its subnet mask.
+         *
+         * @param ip         The IP address.
+         * @param subnetMask Its subnet mask.
+         * @return The concatenated IP address with its subnet mask.
+         */
+        private byte[] IpWithSubnetMask(byte[] ip, byte[] subnetMask)
+        {
+            int ipLength = ip.Length;
+            byte[] temp = new byte[ipLength * 2];
+            Array.Copy(ip, 0, temp, 0, ipLength);
+            Array.Copy(subnetMask, 0, temp, ipLength, ipLength);
+            return temp;
+        }
+
+        /**
+         * Splits the IP addresses and their subnet mask.
+         *
+         * @param ipWithSubmask1 The first IP address with the subnet mask.
+         * @param ipWithSubmask2 The second IP address with the subnet mask.
+         * @return An array with two elements. Each element contains the IP address
+         *         and the subnet mask in this order.
+         */
+        private byte[][] ExtractIPsAndSubnetMasks(
+            byte[] ipWithSubmask1,
+            byte[] ipWithSubmask2)
+    {
+        int ipLength = ipWithSubmask1.Length / 2;
+        byte[] ip1 = new byte[ipLength];
+        byte[] subnetmask1 = new byte[ipLength];
+        Array.Copy(ipWithSubmask1, 0, ip1, 0, ipLength);
+        Array.Copy(ipWithSubmask1, ipLength, subnetmask1, 0, ipLength);
+
+        byte[] ip2 = new byte[ipLength];
+        byte[] subnetmask2 = new byte[ipLength];
+        Array.Copy(ipWithSubmask2, 0, ip2, 0, ipLength);
+        Array.Copy(ipWithSubmask2, ipLength, subnetmask2, 0, ipLength);
+        return new byte[][]
+            {ip1, subnetmask1, ip2, subnetmask2};
+    }
+
+        /**
+         * Based on the two IP addresses and their subnet masks the IP range is
+         * computed for each IP address - subnet mask pair and returned as the
+         * minimum IP address and the maximum address of the range.
+         *
+         * @param ip1         The first IP address.
+         * @param subnetmask1 The subnet mask of the first IP address.
+         * @param ip2         The second IP address.
+         * @param subnetmask2 The subnet mask of the second IP address.
+         * @return A array with two elements. The first/second element contains the
+         *         min and max IP address of the first/second IP address and its
+         *         subnet mask.
+         */
+        private byte[][] MinMaxIPs(
+            byte[] ip1,
+            byte[] subnetmask1,
+            byte[] ip2,
+            byte[] subnetmask2)
+        {
+            int ipLength = ip1.Length;
+            byte[] min1 = new byte[ipLength];
+            byte[] max1 = new byte[ipLength];
+
+            byte[] min2 = new byte[ipLength];
+            byte[] max2 = new byte[ipLength];
+
+            for (int i = 0; i < ipLength; i++)
+            {
+                min1[i] = (byte)(ip1[i] & subnetmask1[i]);
+                max1[i] = (byte)(ip1[i] & subnetmask1[i] | ~subnetmask1[i]);
+
+                min2[i] = (byte)(ip2[i] & subnetmask2[i]);
+                max2[i] = (byte)(ip2[i] & subnetmask2[i] | ~subnetmask2[i]);
+            }
+
+            return new byte[][] { min1, max1, min2, max2 };
+        }
+
+        private void CheckPermittedEmail(ISet permitted, String email)
+        //throws PkixNameConstraintValidatorException
+        {
+            if (permitted == null)
+            {
+                return;
+            }
+
+            IEnumerator it = permitted.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                String str = ((String)it.Current);
+
+                if (EmailIsConstrained(email, str))
+                {
+                    return;
+                }
+            }
+
+            if (email.Length == 0 && permitted.Count == 0)
+            {
+                return;
+            }
+
+            throw new PkixNameConstraintValidatorException(
+                "Subject email address is not from a permitted subtree.");
+        }
+
+        private void CheckExcludedEmail(ISet excluded, String email)
+        //throws PkixNameConstraintValidatorException
+        {
+            if (excluded.IsEmpty)
+            {
+                return;
+            }
+
+            IEnumerator it = excluded.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                String str = (String)it.Current;
+
+                if (EmailIsConstrained(email, str))
+                {
+                    throw new PkixNameConstraintValidatorException(
+                        "Email address is from an excluded subtree.");
+                }
+            }
+        }
+
+        /**
+         * Checks if the IP <code>ip</code> is included in the permitted ISet
+         * <code>permitted</code>.
+         *
+         * @param permitted A <code>Set</code> of permitted IP addresses with
+         *                  their subnet mask as byte arrays.
+         * @param ip        The IP address.
+         * @throws PkixNameConstraintValidatorException
+         *          if the IP is not permitted.
+         */
+        private void CheckPermittedIP(ISet permitted, byte[] ip)
+        //throws PkixNameConstraintValidatorException
+        {
+            if (permitted == null)
+            {
+                return;
+            }
+
+            IEnumerator it = permitted.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                byte[] ipWithSubnet = (byte[])it.Current;
+
+                if (IsIPConstrained(ip, ipWithSubnet))
+                {
+                    return;
+                }
+            }
+            if (ip.Length == 0 && permitted.Count == 0)
+            {
+                return;
+            }
+            throw new PkixNameConstraintValidatorException(
+                "IP is not from a permitted subtree.");
+        }
+
+        /**
+         * Checks if the IP <code>ip</code> is included in the excluded ISet
+         * <code>excluded</code>.
+         *
+         * @param excluded A <code>Set</code> of excluded IP addresses with their
+         *                 subnet mask as byte arrays.
+         * @param ip       The IP address.
+         * @throws PkixNameConstraintValidatorException
+         *          if the IP is excluded.
+         */
+        private void checkExcludedIP(ISet excluded, byte[] ip)
+        //throws PkixNameConstraintValidatorException
+        {
+            if (excluded.IsEmpty)
+            {
+                return;
+            }
+
+            IEnumerator it = excluded.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                byte[] ipWithSubnet = (byte[])it.Current;
+
+                if (IsIPConstrained(ip, ipWithSubnet))
+                {
+                    throw new PkixNameConstraintValidatorException(
+                        "IP is from an excluded subtree.");
+                }
+            }
+        }
+
+        /**
+         * Checks if the IP address <code>ip</code> is constrained by
+         * <code>constraint</code>.
+         *
+         * @param ip         The IP address.
+         * @param constraint The constraint. This is an IP address concatenated with
+         *                   its subnetmask.
+         * @return <code>true</code> if constrained, <code>false</code>
+         *         otherwise.
+         */
+        private bool IsIPConstrained(byte[] ip, byte[] constraint)
+        {
+            int ipLength = ip.Length;
+
+            if (ipLength != (constraint.Length / 2))
+            {
+                return false;
+            }
+
+            byte[] subnetMask = new byte[ipLength];
+            Array.Copy(constraint, ipLength, subnetMask, 0, ipLength);
+
+            byte[] permittedSubnetAddress = new byte[ipLength];
+
+            byte[] ipSubnetAddress = new byte[ipLength];
+
+            // the resulting IP address by applying the subnet mask
+            for (int i = 0; i < ipLength; i++)
+            {
+                permittedSubnetAddress[i] = (byte)(constraint[i] & subnetMask[i]);
+                ipSubnetAddress[i] = (byte)(ip[i] & subnetMask[i]);
+            }
+
+            return Org.BouncyCastle.Utilities.Arrays.AreEqual(permittedSubnetAddress, ipSubnetAddress);
+        }
+
+        private bool EmailIsConstrained(String email, String constraint)
+        {
+            String sub = email.Substring(email.IndexOf('@') + 1);
+            // a particular mailbox
+            if (constraint.IndexOf('@') != -1)
+            {
+                if (email.ToUpper().Equals(constraint.ToUpper()))
+                {
+                    return true;
+                }
+            }
+            // on particular host
+            else if (!(constraint[0].Equals('.')))
+            {
+                if (sub.ToUpper().Equals(constraint.ToUpper()))
+                {
+                    return true;
+                }
+            }
+            // address in sub domain
+            else if (WithinDomain(sub, constraint))
+            {
+                return true;
+            }
+            return false;
+        }
+
+        private bool WithinDomain(String testDomain, String domain)
+        {
+            String tempDomain = domain;
+            if (tempDomain.StartsWith("."))
+            {
+                tempDomain = tempDomain.Substring(1);
+            }
+            String[] domainParts = tempDomain.Split('.'); // Strings.split(tempDomain, '.');
+            String[] testDomainParts = testDomain.Split('.'); // Strings.split(testDomain, '.');
+
+            // must have at least one subdomain
+            if (testDomainParts.Length <= domainParts.Length)
+            {
+                return false;
+            }
+
+            int d = testDomainParts.Length - domainParts.Length;
+            for (int i = -1; i < domainParts.Length; i++)
+            {
+                if (i == -1)
+                {
+                    if (testDomainParts[i + d].Equals(""))
+                    {
+                        return false;
+                    }
+                }
+                else if (!(Platform.CompareIgnoreCase(testDomainParts[i + d], domainParts[i]) == 0))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private void CheckPermittedDNS(ISet permitted, String dns)
+        //throws PkixNameConstraintValidatorException
+        {
+            if (permitted == null)
+            {
+                return;
+            }
+
+            IEnumerator it = permitted.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                String str = ((String)it.Current);
+
+                // is sub domain
+                if (WithinDomain(dns, str) || dns.ToUpper().Equals(str.ToUpper()))
+                {
+                    return;
+                }
+            }
+            if (dns.Length == 0 && permitted.Count == 0)
+            {
+                return;
+            }
+            throw new PkixNameConstraintValidatorException(
+                "DNS is not from a permitted subtree.");
+        }
+
+        private void checkExcludedDNS(ISet excluded, String dns)
+        //     throws PkixNameConstraintValidatorException
+        {
+            if (excluded.IsEmpty)
+            {
+                return;
+            }
+
+            IEnumerator it = excluded.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                String str = ((String)it.Current);
+
+                // is sub domain or the same
+				if (WithinDomain(dns, str) || (Platform.CompareIgnoreCase(dns, str) == 0))
+                {
+                    throw new PkixNameConstraintValidatorException(
+                        "DNS is from an excluded subtree.");
+                }
+            }
+        }
+
+        /**
+         * The common part of <code>email1</code> and <code>email2</code> is
+         * added to the union <code>union</code>. If <code>email1</code> and
+         * <code>email2</code> have nothing in common they are added both.
+         *
+         * @param email1 Email address constraint 1.
+         * @param email2 Email address constraint 2.
+         * @param union  The union.
+         */
+        private void unionEmail(String email1, String email2, ISet union)
+        {
+            // email1 is a particular address
+            if (email1.IndexOf('@') != -1)
+            {
+                String _sub = email1.Substring(email1.IndexOf('@') + 1);
+                // both are a particular mailbox
+                if (email2.IndexOf('@') != -1)
+                {
+                    if (Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(_sub, email2))
+                    {
+                        union.Add(email2);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a particular host
+                else
+                {
+                    if (Platform.CompareIgnoreCase(_sub, email2) == 0)
+                    {
+                        union.Add(email2);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+            }
+            // email1 specifies a domain
+            else if (email1.StartsWith("."))
+            {
+                if (email2.IndexOf('@') != -1)
+                {
+                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    if (WithinDomain(_sub, email1))
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(email1, email2) || Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        union.Add(email2);
+                    }
+                    else if (WithinDomain(email2, email1))
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                else
+                {
+                    if (WithinDomain(email2, email1))
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+            }
+            // email specifies a host
+            else
+            {
+                if (email2.IndexOf('@') != -1)
+                {
+                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    if (Platform.CompareIgnoreCase(_sub, email1) == 0)
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(email1, email2))
+                    {
+                        union.Add(email2);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a particular host
+                else
+                {
+                    if (Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+            }
+        }
+
+        private void unionURI(String email1, String email2, ISet union)
+        {
+            // email1 is a particular address
+            if (email1.IndexOf('@') != -1)
+            {
+                String _sub = email1.Substring(email1.IndexOf('@') + 1);
+                // both are a particular mailbox
+                if (email2.IndexOf('@') != -1)
+                {
+                    if (Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(_sub, email2))
+                    {
+                        union.Add(email2);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a particular host
+                else
+                {
+                    if (Platform.CompareIgnoreCase(_sub, email2) == 0)
+                    {
+                        union.Add(email2);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+
+                    }
+                }
+            }
+            // email1 specifies a domain
+            else if (email1.StartsWith("."))
+            {
+                if (email2.IndexOf('@') != -1)
+                {
+                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    if (WithinDomain(_sub, email1))
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(email1, email2) || Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        union.Add(email2);
+                    }
+                    else if (WithinDomain(email2, email1))
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                else
+                {
+                    if (WithinDomain(email2, email1))
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+            }
+            // email specifies a host
+            else
+            {
+                if (email2.IndexOf('@') != -1)
+                {
+                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    if (Platform.CompareIgnoreCase(_sub, email1) == 0)
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(email1, email2))
+                    {
+                        union.Add(email2);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+                // email2 specifies a particular host
+                else
+                {
+                    if (Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        union.Add(email1);
+                    }
+                    else
+                    {
+                        union.Add(email1);
+                        union.Add(email2);
+                    }
+                }
+            }
+        }
+
+        private ISet intersectDNS(ISet permitted, ISet dnss)
+        {
+            ISet intersect = new HashSet();
+            for (IEnumerator it = dnss.GetEnumerator(); it.MoveNext(); )
+            {
+                String dns = ExtractNameAsString(((GeneralSubtree)it.Current)
+                    .Base);
+                if (permitted == null)
+                {
+                    if (dns != null)
+                    {
+                        intersect.Add(dns);
+                    }
+                }
+                else
+                {
+                    IEnumerator _iter = permitted.GetEnumerator();
+                    while (_iter.MoveNext())
+                    {
+                        String _permitted = (String)_iter.Current;
+
+                        if (WithinDomain(_permitted, dns))
+                        {
+                            intersect.Add(_permitted);
+                        }
+                        else if (WithinDomain(dns, _permitted))
+                        {
+                            intersect.Add(dns);
+                        }
+                    }
+                }
+            }
+
+            return intersect;
+        }
+
+        protected ISet unionDNS(ISet excluded, String dns)
+        {
+            if (excluded.IsEmpty)
+            {
+                if (dns == null)
+                {
+                    return excluded;
+                }
+                excluded.Add(dns);
+
+                return excluded;
+            }
+            else
+            {
+                ISet union = new HashSet();
+
+                IEnumerator _iter = excluded.GetEnumerator();
+                while (_iter.MoveNext())
+                {
+                    String _permitted = (String)_iter.Current;
+
+                    if (WithinDomain(_permitted, dns))
+                    {
+                        union.Add(dns);
+                    }
+                    else if (WithinDomain(dns, _permitted))
+                    {
+                        union.Add(_permitted);
+                    }
+                    else
+                    {
+                        union.Add(_permitted);
+                        union.Add(dns);
+                    }
+                }
+
+                return union;
+            }
+        }
+
+        /**
+         * The most restricting part from <code>email1</code> and
+         * <code>email2</code> is added to the intersection <code>intersect</code>.
+         *
+         * @param email1    Email address constraint 1.
+         * @param email2    Email address constraint 2.
+         * @param intersect The intersection.
+         */
+        private void intersectEmail(String email1, String email2, ISet intersect)
+        {
+            // email1 is a particular address
+            if (email1.IndexOf('@') != -1)
+            {
+                String _sub = email1.Substring(email1.IndexOf('@') + 1);
+                // both are a particular mailbox
+                if (email2.IndexOf('@') != -1)
+                {
+                    if (Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(_sub, email2))
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+                // email2 specifies a particular host
+                else
+                {
+                    if (Platform.CompareIgnoreCase(_sub, email2) == 0)
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+            }
+            // email specifies a domain
+            else if (email1.StartsWith("."))
+            {
+                if (email2.IndexOf('@') != -1)
+                {
+                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    if (WithinDomain(_sub, email1))
+                    {
+                        intersect.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(email1, email2) || (Platform.CompareIgnoreCase(email1, email2) == 0))
+                    {
+                        intersect.Add(email1);
+                    }
+                    else if (WithinDomain(email2, email1))
+                    {
+                        intersect.Add(email2);
+                    }
+                }
+                else
+                {
+                    if (WithinDomain(email2, email1))
+                    {
+                        intersect.Add(email2);
+                    }
+                }
+            }
+            // email1 specifies a host
+            else
+            {
+                if (email2.IndexOf('@') != -1)
+                {
+                    String _sub = email2.Substring(email2.IndexOf('@') + 1);
+                    if (Platform.CompareIgnoreCase(_sub, email1) == 0)
+                    {
+                        intersect.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(email1, email2))
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+                // email2 specifies a particular host
+                else
+                {
+                    if (Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+            }
+        }
+
+        private void checkExcludedURI(ISet excluded, String uri)
+        //       throws PkixNameConstraintValidatorException
+        {
+            if (excluded.IsEmpty)
+            {
+                return;
+            }
+
+            IEnumerator it = excluded.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                String str = ((String)it.Current);
+
+                if (IsUriConstrained(uri, str))
+                {
+                    throw new PkixNameConstraintValidatorException(
+                        "URI is from an excluded subtree.");
+                }
+            }
+        }
+
+        private ISet intersectURI(ISet permitted, ISet uris)
+        {
+            ISet intersect = new HashSet();
+            for (IEnumerator it = uris.GetEnumerator(); it.MoveNext(); )
+            {
+                String uri = ExtractNameAsString(((GeneralSubtree)it.Current)
+                    .Base);
+                if (permitted == null)
+                {
+                    if (uri != null)
+                    {
+                        intersect.Add(uri);
+                    }
+                }
+                else
+                {
+                    IEnumerator _iter = permitted.GetEnumerator();
+                    while (_iter.MoveNext())
+                    {
+                        String _permitted = (String)_iter.Current;
+                        intersectURI(_permitted, uri, intersect);
+                    }
+                }
+            }
+            return intersect;
+        }
+
+        private ISet unionURI(ISet excluded, String uri)
+        {
+            if (excluded.IsEmpty)
+            {
+                if (uri == null)
+                {
+                    return excluded;
+                }
+                excluded.Add(uri);
+
+                return excluded;
+            }
+            else
+            {
+                ISet union = new HashSet();
+
+                IEnumerator _iter = excluded.GetEnumerator();
+                while (_iter.MoveNext())
+                {
+                    String _excluded = (String)_iter.Current;
+
+                    unionURI(_excluded, uri, union);
+                }
+
+                return union;
+            }
+        }
+
+        private void intersectURI(String email1, String email2, ISet intersect)
+        {
+            // email1 is a particular address
+            if (email1.IndexOf('@') != -1)
+            {
+                String _sub = email1.Substring(email1.IndexOf('@') + 1);
+                // both are a particular mailbox
+                if (email2.IndexOf('@') != -1)
+                {
+                    if (Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(_sub, email2))
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+                // email2 specifies a particular host
+                else
+                {
+                    if (Platform.CompareIgnoreCase(_sub, email2) == 0)
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+            }
+            // email specifies a domain
+            else if (email1.StartsWith("."))
+            {
+                if (email2.IndexOf('@') != -1)
+                {
+                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    if (WithinDomain(_sub, email1))
+                    {
+                        intersect.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(email1, email2) || (Platform.CompareIgnoreCase(email1, email2) == 0))
+                    {
+                        intersect.Add(email1);
+                    }
+                    else if (WithinDomain(email2, email1))
+                    {
+                        intersect.Add(email2);
+                    }
+                }
+                else
+                {
+                    if (WithinDomain(email2, email1))
+                    {
+                        intersect.Add(email2);
+                    }
+                }
+            }
+            // email1 specifies a host
+            else
+            {
+                if (email2.IndexOf('@') != -1)
+                {
+                    String _sub = email2.Substring(email2.IndexOf('@') + 1);
+                    if (Platform.CompareIgnoreCase(_sub, email1) == 0)
+                    {
+                        intersect.Add(email2);
+                    }
+                }
+                // email2 specifies a domain
+                else if (email2.StartsWith("."))
+                {
+                    if (WithinDomain(email1, email2))
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+                // email2 specifies a particular host
+                else
+                {
+                    if (Platform.CompareIgnoreCase(email1, email2) == 0)
+                    {
+                        intersect.Add(email1);
+                    }
+                }
+            }
+        }
+
+        private void CheckPermittedURI(ISet permitted, String uri)
+        //        throws PkixNameConstraintValidatorException
+        {
+            if (permitted == null)
+            {
+                return;
+            }
+
+            IEnumerator it = permitted.GetEnumerator();
+
+            while (it.MoveNext())
+            {
+                String str = ((String)it.Current);
+
+                if (IsUriConstrained(uri, str))
+                {
+                    return;
+                }
+            }
+            if (uri.Length == 0 && permitted.Count == 0)
+            {
+                return;
+            }
+            throw new PkixNameConstraintValidatorException(
+                "URI is not from a permitted subtree.");
+        }
+
+        private bool IsUriConstrained(String uri, String constraint)
+        {
+            String host = ExtractHostFromURL(uri);
+            // a host
+            if (!constraint.StartsWith("."))
+            {
+                if (Platform.CompareIgnoreCase(host, constraint) == 0)
+                {
+                    return true;
+                }
+            }
+
+            // in sub domain or domain
+            else if (WithinDomain(host, constraint))
+            {
+                return true;
+            }
+
+            return false;
+        }
+
+        private static String ExtractHostFromURL(String url)
+        {
+            // see RFC 1738
+            // remove ':' after protocol, e.g. http:
+            String sub = url.Substring(url.IndexOf(':') + 1);
+            // extract host from Common Internet Scheme Syntax, e.g. http://
+            if (sub.IndexOf("//") != -1)
+            {
+                sub = sub.Substring(sub.IndexOf("//") + 2);
+            }
+            // first remove port, e.g. http://test.com:21
+            if (sub.LastIndexOf(':') != -1)
+            {
+                sub = sub.Substring(0, sub.LastIndexOf(':'));
+            }
+            // remove user and password, e.g. http://john:password@test.com
+            sub = sub.Substring(sub.IndexOf(':') + 1);
+            sub = sub.Substring(sub.IndexOf('@') + 1);
+            // remove local parts, e.g. http://test.com/bla
+            if (sub.IndexOf('/') != -1)
+            {
+                sub = sub.Substring(0, sub.IndexOf('/'));
+            }
+            return sub;
+        }
+
+        /**
+         * Checks if the given GeneralName is in the permitted ISet.
+         *
+         * @param name The GeneralName
+         * @throws PkixNameConstraintValidatorException
+         *          If the <code>name</code>
+         */
+        public void checkPermitted(GeneralName name)
+        //        throws PkixNameConstraintValidatorException
+        {
+            switch (name.TagNo)
+            {
+                case 1:
+                    CheckPermittedEmail(permittedSubtreesEmail,
+                        ExtractNameAsString(name));
+                    break;
+                case 2:
+                    CheckPermittedDNS(permittedSubtreesDNS, DerIA5String.GetInstance(
+                        name.Name).GetString());
+                    break;
+                case 4:
+                    CheckPermittedDN(Asn1Sequence.GetInstance(name.Name.ToAsn1Object()));
+                    break;
+                case 6:
+                    CheckPermittedURI(permittedSubtreesURI, DerIA5String.GetInstance(
+                        name.Name).GetString());
+                    break;
+                case 7:
+                    byte[] ip = Asn1OctetString.GetInstance(name.Name).GetOctets();
+
+                    CheckPermittedIP(permittedSubtreesIP, ip);
+                    break;
+            }
+        }
+
+        /**
+         * Check if the given GeneralName is contained in the excluded ISet.
+         *
+         * @param name The GeneralName.
+         * @throws PkixNameConstraintValidatorException
+         *          If the <code>name</code> is
+         *          excluded.
+         */
+        public void checkExcluded(GeneralName name)
+        //        throws PkixNameConstraintValidatorException
+        {
+            switch (name.TagNo)
+            {
+                case 1:
+                    CheckExcludedEmail(excludedSubtreesEmail, ExtractNameAsString(name));
+                    break;
+                case 2:
+                    checkExcludedDNS(excludedSubtreesDNS, DerIA5String.GetInstance(
+                        name.Name).GetString());
+                    break;
+                case 4:
+                    CheckExcludedDN(Asn1Sequence.GetInstance(name.Name.ToAsn1Object()));
+                    break;
+                case 6:
+                    checkExcludedURI(excludedSubtreesURI, DerIA5String.GetInstance(
+                        name.Name).GetString());
+                    break;
+                case 7:
+                    byte[] ip = Asn1OctetString.GetInstance(name.Name).GetOctets();
+
+                    checkExcludedIP(excludedSubtreesIP, ip);
+                    break;
+            }
+        }
+
+        /**
+         * Updates the permitted ISet of these name constraints with the intersection
+         * with the given subtree.
+         *
+         * @param permitted The permitted subtrees
+         */
+
+        public void IntersectPermittedSubtree(Asn1Sequence permitted)
+        {
+            IDictionary subtreesMap = Platform.CreateHashtable();
+
+            // group in ISets in a map ordered by tag no.
+            for (IEnumerator e = permitted.GetEnumerator(); e.MoveNext(); )
+            {
+                GeneralSubtree subtree = GeneralSubtree.GetInstance(e.Current);
+
+                int tagNo = subtree.Base.TagNo;
+                if (subtreesMap[tagNo] == null)
+                {
+                    subtreesMap[tagNo] = new HashSet();
+                }
+
+                ((ISet)subtreesMap[tagNo]).Add(subtree);
+            }
+
+            for (IEnumerator it = subtreesMap.GetEnumerator(); it.MoveNext(); )
+            {
+                DictionaryEntry entry = (DictionaryEntry)it.Current;
+
+                // go through all subtree groups
+                switch ((int)entry.Key )
+                {
+                    case 1:
+                        permittedSubtreesEmail = IntersectEmail(permittedSubtreesEmail,
+                            (ISet)entry.Value);
+                        break;
+                    case 2:
+                        permittedSubtreesDNS = intersectDNS(permittedSubtreesDNS,
+                            (ISet)entry.Value);
+                        break;
+                    case 4:
+                        permittedSubtreesDN = IntersectDN(permittedSubtreesDN,
+                            (ISet)entry.Value);
+                        break;
+                    case 6:
+                        permittedSubtreesURI = intersectURI(permittedSubtreesURI,
+                            (ISet)entry.Value);
+                        break;
+                    case 7:
+                        permittedSubtreesIP = IntersectIP(permittedSubtreesIP,
+                            (ISet)entry.Value);
+                        break;
+                }
+            }
+        }
+
+        private String ExtractNameAsString(GeneralName name)
+        {
+            return DerIA5String.GetInstance(name.Name).GetString();
+        }
+
+        public void IntersectEmptyPermittedSubtree(int nameType)
+        {
+            switch (nameType)
+            {
+                case 1:
+                    permittedSubtreesEmail = new HashSet();
+                    break;
+                case 2:
+                    permittedSubtreesDNS = new HashSet();
+                    break;
+                case 4:
+                    permittedSubtreesDN = new HashSet();
+                    break;
+                case 6:
+                    permittedSubtreesURI = new HashSet();
+                    break;
+                case 7:
+                    permittedSubtreesIP = new HashSet();
+                    break;
+            }
+        }
+
+        /**
+         * Adds a subtree to the excluded ISet of these name constraints.
+         *
+         * @param subtree A subtree with an excluded GeneralName.
+         */
+        public void AddExcludedSubtree(GeneralSubtree subtree)
+        {
+            GeneralName subTreeBase = subtree.Base;
+
+            switch (subTreeBase.TagNo)
+            {
+                case 1:
+                    excludedSubtreesEmail = UnionEmail(excludedSubtreesEmail,
+                        ExtractNameAsString(subTreeBase));
+                    break;
+                case 2:
+                    excludedSubtreesDNS = unionDNS(excludedSubtreesDNS,
+                        ExtractNameAsString(subTreeBase));
+                    break;
+                case 4:
+                    excludedSubtreesDN = UnionDN(excludedSubtreesDN,
+                        (Asn1Sequence)subTreeBase.Name.ToAsn1Object());
+                    break;
+                case 6:
+                    excludedSubtreesURI = unionURI(excludedSubtreesURI,
+                        ExtractNameAsString(subTreeBase));
+                    break;
+                case 7:
+                    excludedSubtreesIP = UnionIP(excludedSubtreesIP, Asn1OctetString
+                        .GetInstance(subTreeBase.Name).GetOctets());
+                    break;
+            }
+        }
+
+        /**
+         * Returns the maximum IP address.
+         *
+         * @param ip1 The first IP address.
+         * @param ip2 The second IP address.
+         * @return The maximum IP address.
+         */
+        private static byte[] Max(byte[] ip1, byte[] ip2)
+        {
+            for (int i = 0; i < ip1.Length; i++)
+            {
+                if ((ip1[i] & 0xFFFF) > (ip2[i] & 0xFFFF))
+                {
+                    return ip1;
+                }
+            }
+            return ip2;
+        }
+
+        /**
+         * Returns the minimum IP address.
+         *
+         * @param ip1 The first IP address.
+         * @param ip2 The second IP address.
+         * @return The minimum IP address.
+         */
+        private static byte[] Min(byte[] ip1, byte[] ip2)
+        {
+            for (int i = 0; i < ip1.Length; i++)
+            {
+                if ((ip1[i] & 0xFFFF) < (ip2[i] & 0xFFFF))
+                {
+                    return ip1;
+                }
+            }
+            return ip2;
+        }
+
+        /**
+         * Compares IP address <code>ip1</code> with <code>ip2</code>. If ip1
+         * is equal to ip2 0 is returned. If ip1 is bigger 1 is returned, -1
+         * otherwise.
+         *
+         * @param ip1 The first IP address.
+         * @param ip2 The second IP address.
+         * @return 0 if ip1 is equal to ip2, 1 if ip1 is bigger, -1 otherwise.
+         */
+        private static int CompareTo(byte[] ip1, byte[] ip2)
+        {
+            if (Org.BouncyCastle.Utilities.Arrays.AreEqual(ip1, ip2))
+            {
+                return 0;
+            }
+            if (Org.BouncyCastle.Utilities.Arrays.AreEqual(Max(ip1, ip2), ip1))
+            {
+                return 1;
+            }
+            return -1;
+        }
+
+        /**
+         * Returns the logical OR of the IP addresses <code>ip1</code> and
+         * <code>ip2</code>.
+         *
+         * @param ip1 The first IP address.
+         * @param ip2 The second IP address.
+         * @return The OR of <code>ip1</code> and <code>ip2</code>.
+         */
+        private static byte[] Or(byte[] ip1, byte[] ip2)
+        {
+            byte[] temp = new byte[ip1.Length];
+            for (int i = 0; i < ip1.Length; i++)
+            {
+                temp[i] = (byte)(ip1[i] | ip2[i]);
+            }
+            return temp;
+        }
+
+		[Obsolete("Use GetHashCode instead")]
+		public int HashCode()
+		{
+			return GetHashCode();
+		}
+
+		public override int GetHashCode()
+        {
+            return HashCollection(excludedSubtreesDN)
+                + HashCollection(excludedSubtreesDNS)
+                + HashCollection(excludedSubtreesEmail)
+                + HashCollection(excludedSubtreesIP)
+                + HashCollection(excludedSubtreesURI)
+                + HashCollection(permittedSubtreesDN)
+                + HashCollection(permittedSubtreesDNS)
+                + HashCollection(permittedSubtreesEmail)
+                + HashCollection(permittedSubtreesIP)
+                + HashCollection(permittedSubtreesURI);
+        }
+
+        private int HashCollection(ICollection coll)
+        {
+            if (coll == null)
+            {
+                return 0;
+            }
+            int hash = 0;
+            IEnumerator it1 = coll.GetEnumerator();
+            while (it1.MoveNext())
+            {
+                Object o = it1.Current;
+                if (o is byte[])
+                {
+                    hash += Org.BouncyCastle.Utilities.Arrays.GetHashCode((byte[])o);
+                }
+                else
+                {
+                    hash += o.GetHashCode();
+                }
+            }
+            return hash;
+        }
+
+		public override bool Equals(Object o)
+		{
+			if (!(o is PkixNameConstraintValidator))
+				return false;
+
+			PkixNameConstraintValidator constraintValidator = (PkixNameConstraintValidator)o;
+
+			return CollectionsAreEqual(constraintValidator.excludedSubtreesDN, excludedSubtreesDN)
+				&& CollectionsAreEqual(constraintValidator.excludedSubtreesDNS, excludedSubtreesDNS)
+				&& CollectionsAreEqual(constraintValidator.excludedSubtreesEmail, excludedSubtreesEmail)
+				&& CollectionsAreEqual(constraintValidator.excludedSubtreesIP, excludedSubtreesIP)
+				&& CollectionsAreEqual(constraintValidator.excludedSubtreesURI, excludedSubtreesURI)
+				&& CollectionsAreEqual(constraintValidator.permittedSubtreesDN, permittedSubtreesDN)
+				&& CollectionsAreEqual(constraintValidator.permittedSubtreesDNS, permittedSubtreesDNS)
+				&& CollectionsAreEqual(constraintValidator.permittedSubtreesEmail, permittedSubtreesEmail)
+				&& CollectionsAreEqual(constraintValidator.permittedSubtreesIP, permittedSubtreesIP)
+				&& CollectionsAreEqual(constraintValidator.permittedSubtreesURI, permittedSubtreesURI);
+		}
+
+        private bool CollectionsAreEqual(ICollection coll1, ICollection coll2)
+        {
+            if (coll1 == coll2)
+            {
+                return true;
+            }
+            if (coll1 == null || coll2 == null)
+            {
+                return false;
+            }
+            if (coll1.Count != coll2.Count)
+            {
+                return false;
+            }
+            IEnumerator it1 = coll1.GetEnumerator();
+
+            while (it1.MoveNext())
+            {
+                Object a = it1.Current;
+                IEnumerator it2 = coll2.GetEnumerator();
+                bool found = false;
+                while (it2.MoveNext())
+                {
+                    Object b = it2.Current;
+                    if (SpecialEquals(a, b))
+                    {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private bool SpecialEquals(Object o1, Object o2)
+        {
+            if (o1 == o2)
+            {
+                return true;
+            }
+            if (o1 == null || o2 == null)
+            {
+                return false;
+            }
+            if ((o1 is byte[]) && (o2 is byte[]))
+            {
+                return Org.BouncyCastle.Utilities.Arrays.AreEqual((byte[])o1, (byte[])o2);
+            }
+            else
+            {
+                return o1.Equals(o2);
+            }
+        }
+
+        /**
+         * Stringifies an IPv4 or v6 address with subnet mask.
+         *
+         * @param ip The IP with subnet mask.
+         * @return The stringified IP address.
+         */
+        private String StringifyIP(byte[] ip)
+        {
+            String temp = "";
+            for (int i = 0; i < ip.Length / 2; i++)
+            {
+                //temp += Integer.toString(ip[i] & 0x00FF) + ".";
+                temp += (ip[i] & 0x00FF) + ".";
+            }
+            temp = temp.Substring(0, temp.Length - 1);
+            temp += "/";
+            for (int i = ip.Length / 2; i < ip.Length; i++)
+            {
+                //temp += Integer.toString(ip[i] & 0x00FF) + ".";
+                temp += (ip[i] & 0x00FF) + ".";
+            }
+            temp = temp.Substring(0, temp.Length - 1);
+            return temp;
+        }
+
+        private String StringifyIPCollection(ISet ips)
+        {
+            String temp = "";
+            temp += "[";
+            for (IEnumerator it = ips.GetEnumerator(); it.MoveNext(); )
+            {
+                temp += StringifyIP((byte[])it.Current) + ",";
+            }
+            if (temp.Length > 1)
+            {
+                temp = temp.Substring(0, temp.Length - 1);
+            }
+            temp += "]";
+
+            return temp;
+        }
+
+        public override String ToString()
+        {
+            String temp = "";
+
+            temp += "permitted:\n";
+            if (permittedSubtreesDN != null)
+            {
+                temp += "DN:\n";
+                temp += permittedSubtreesDN.ToString() + "\n";
+            }
+            if (permittedSubtreesDNS != null)
+            {
+                temp += "DNS:\n";
+                temp += permittedSubtreesDNS.ToString() + "\n";
+            }
+            if (permittedSubtreesEmail != null)
+            {
+                temp += "Email:\n";
+                temp += permittedSubtreesEmail.ToString() + "\n";
+            }
+            if (permittedSubtreesURI != null)
+            {
+                temp += "URI:\n";
+                temp += permittedSubtreesURI.ToString() + "\n";
+            }
+            if (permittedSubtreesIP != null)
+            {
+                temp += "IP:\n";
+                temp += StringifyIPCollection(permittedSubtreesIP) + "\n";
+            }
+            temp += "excluded:\n";
+            if (!(excludedSubtreesDN.IsEmpty))
+            {
+                temp += "DN:\n";
+                temp += excludedSubtreesDN.ToString() + "\n";
+            }
+            if (!excludedSubtreesDNS.IsEmpty)
+            {
+                temp += "DNS:\n";
+                temp += excludedSubtreesDNS.ToString() + "\n";
+            }
+            if (!excludedSubtreesEmail.IsEmpty)
+            {
+                temp += "Email:\n";
+                temp += excludedSubtreesEmail.ToString() + "\n";
+            }
+            if (!excludedSubtreesURI.IsEmpty)
+            {
+                temp += "URI:\n";
+                temp += excludedSubtreesURI.ToString() + "\n";
+            }
+            if (!excludedSubtreesIP.IsEmpty)
+            {
+                temp += "IP:\n";
+                temp += StringifyIPCollection(excludedSubtreesIP) + "\n";
+            }
+            return temp;
+        }
+
+    }
+}
diff --git a/crypto/src/pkix/PkixNameConstraintValidatorException.cs b/crypto/src/pkix/PkixNameConstraintValidatorException.cs
new file mode 100644
index 000000000..432d7bd6b
--- /dev/null
+++ b/crypto/src/pkix/PkixNameConstraintValidatorException.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Org.BouncyCastle.Pkix
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+    [Serializable]
+#endif
+    public class PkixNameConstraintValidatorException
+        : Exception
+    {
+        public PkixNameConstraintValidatorException(String msg)
+            : base(msg)
+        {
+        }
+    }
+}
diff --git a/crypto/src/pkix/PkixParameters.cs b/crypto/src/pkix/PkixParameters.cs
new file mode 100644
index 000000000..6df1b646f
--- /dev/null
+++ b/crypto/src/pkix/PkixParameters.cs
@@ -0,0 +1,893 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/// <summary>
+	/// Summary description for PkixParameters.
+	/// </summary>
+	public class PkixParameters
+//		: ICertPathParameters
+	{
+		/**
+		* This is the default PKIX validity model. Actually there are two variants
+		* of this: The PKIX model and the modified PKIX model. The PKIX model
+		* verifies that all involved certificates must have been valid at the
+		* current time. The modified PKIX model verifies that all involved
+		* certificates were valid at the signing time. Both are indirectly choosen
+		* with the {@link PKIXParameters#setDate(java.util.Date)} method, so this
+		* methods sets the Date when <em>all</em> certificates must have been
+		* valid.
+		*/
+		public const int PkixValidityModel = 0;
+
+		/**
+		* This model uses the following validity model. Each certificate must have
+		* been valid at the moment where is was used. That means the end
+		* certificate must have been valid at the time the signature was done. The
+		* CA certificate which signed the end certificate must have been valid,
+		* when the end certificate was signed. The CA (or Root CA) certificate must
+		* have been valid, when the CA certificate was signed and so on. So the
+		* {@link PKIXParameters#setDate(java.util.Date)} method sets the time, when
+		* the <em>end certificate</em> must have been valid. <p/> It is used e.g.
+		* in the German signature law.
+		*/
+		public const int ChainValidityModel = 1;
+
+		private ISet trustAnchors;
+		private DateTimeObject date;
+		private IList certPathCheckers;
+		private bool revocationEnabled = true;
+		private ISet initialPolicies;
+		//private bool checkOnlyEECertificateCrl = false;
+		private bool explicitPolicyRequired = false;
+		private bool anyPolicyInhibited = false;
+		private bool policyMappingInhibited = false;
+		private bool policyQualifiersRejected = true;
+		private IX509Selector certSelector;
+		private IList stores;
+		private IX509Selector selector;
+		private bool additionalLocationsEnabled;
+		private IList additionalStores;
+		private ISet trustedACIssuers;
+		private ISet necessaryACAttributes;
+		private ISet prohibitedACAttributes;
+		private ISet attrCertCheckers;
+		private int validityModel = PkixValidityModel;
+		private bool useDeltas = false;
+
+		/**
+		 * Creates an instance of PKIXParameters with the specified Set of
+		 * most-trusted CAs. Each element of the set is a TrustAnchor.<br />
+		 * <br />
+		 * Note that the Set is copied to protect against subsequent modifications.
+		 *
+		 * @param trustAnchors
+		 *            a Set of TrustAnchors
+		 *
+		 * @exception InvalidAlgorithmParameterException
+		 *                if the specified Set is empty
+		 *                <code>(trustAnchors.isEmpty() == true)</code>
+		 * @exception NullPointerException
+		 *                if the specified Set is <code>null</code>
+		 * @exception ClassCastException
+		 *                if any of the elements in the Set are not of type
+		 *                <code>java.security.cert.TrustAnchor</code>
+		 */
+		public PkixParameters(
+			ISet trustAnchors)
+		{
+			SetTrustAnchors(trustAnchors);
+
+			this.initialPolicies = new HashSet();
+			this.certPathCheckers = Platform.CreateArrayList();
+            this.stores = Platform.CreateArrayList();
+			this.additionalStores = Platform.CreateArrayList();
+			this.trustedACIssuers = new HashSet();
+			this.necessaryACAttributes = new HashSet();
+			this.prohibitedACAttributes = new HashSet();
+			this.attrCertCheckers = new HashSet();
+		}
+
+//		// TODO implement for other keystores (see Java build)?
+//		/**
+//		 * Creates an instance of <code>PKIXParameters</code> that
+//		 * populates the set of most-trusted CAs from the trusted
+//		 * certificate entries contained in the specified <code>KeyStore</code>.
+//		 * Only keystore entries that contain trusted <code>X509Certificates</code>
+//		 * are considered; all other certificate types are ignored.
+//		 *
+//		 * @param keystore a <code>KeyStore</code> from which the set of
+//		 * most-trusted CAs will be populated
+//		 * @throws KeyStoreException if the keystore has not been initialized
+//		 * @throws InvalidAlgorithmParameterException if the keystore does
+//		 * not contain at least one trusted certificate entry
+//		 * @throws NullPointerException if the keystore is <code>null</code>
+//		 */
+//		public PkixParameters(
+//			Pkcs12Store keystore)
+////			throws KeyStoreException, InvalidAlgorithmParameterException
+//		{
+//			if (keystore == null)
+//				throw new ArgumentNullException("keystore");
+//			ISet trustAnchors = new HashSet();
+//			foreach (string alias in keystore.Aliases)
+//			{
+//				if (keystore.IsCertificateEntry(alias))
+//				{
+//					X509CertificateEntry x509Entry = keystore.GetCertificate(alias);
+//					trustAnchors.Add(new TrustAnchor(x509Entry.Certificate, null));
+//				}
+//			}
+//			SetTrustAnchors(trustAnchors);
+//
+//			this.initialPolicies = new HashSet();
+//			this.certPathCheckers = new ArrayList();
+//			this.stores = new ArrayList();
+//			this.additionalStores = new ArrayList();
+//			this.trustedACIssuers = new HashSet();
+//			this.necessaryACAttributes = new HashSet();
+//			this.prohibitedACAttributes = new HashSet();
+//			this.attrCertCheckers = new HashSet();
+//		}
+
+		public virtual bool IsRevocationEnabled
+		{
+			get { return revocationEnabled; }
+			set { revocationEnabled = value; }
+		}
+
+		public virtual bool IsExplicitPolicyRequired
+		{
+			get { return explicitPolicyRequired; }
+			set { this.explicitPolicyRequired = value; }
+		}
+
+		public virtual bool IsAnyPolicyInhibited
+		{
+			get { return anyPolicyInhibited; }
+			set { this.anyPolicyInhibited = value; }
+		}
+
+		public virtual bool IsPolicyMappingInhibited
+		{
+			get { return policyMappingInhibited; }
+			set { this.policyMappingInhibited = value; }
+		}
+
+		public virtual bool IsPolicyQualifiersRejected
+		{
+			get { return policyQualifiersRejected; }
+			set { this.policyQualifiersRejected = value; }
+		}
+
+		//public bool IsCheckOnlyEECertificateCrl
+		//{
+		//	get { return this.checkOnlyEECertificateCrl; }
+		//	set { this.checkOnlyEECertificateCrl = value; }
+		//}
+
+		public virtual DateTimeObject Date
+		{
+			get { return this.date; }
+			set { this.date = value; }
+		}
+
+		// Returns a Set of the most-trusted CAs.
+		public virtual ISet GetTrustAnchors()
+		{
+			return new HashSet(this.trustAnchors);
+		}
+
+		// Sets the set of most-trusted CAs.
+		// Set is copied to protect against subsequent modifications.
+		public virtual void SetTrustAnchors(
+			ISet tas)
+		{
+			if (tas == null)
+				throw new ArgumentNullException("value");
+			if (tas.IsEmpty)
+				throw new ArgumentException("non-empty set required", "value");
+
+			// Explicit copy to enforce type-safety
+			this.trustAnchors = new HashSet();
+			foreach (TrustAnchor ta in tas)
+			{
+				if (ta != null)
+				{
+					trustAnchors.Add(ta);
+				}
+			}
+		}
+
+		/**
+		* Returns the required constraints on the target certificate. The
+		* constraints are returned as an instance of CertSelector. If
+		* <code>null</code>, no constraints are defined.<br />
+		* <br />
+		* Note that the CertSelector returned is cloned to protect against
+		* subsequent modifications.
+		*
+		* @return a CertSelector specifying the constraints on the target
+		*         certificate (or <code>null</code>)
+		*
+		* @see #setTargetCertConstraints(CertSelector)
+		*/
+		public virtual X509CertStoreSelector GetTargetCertConstraints()
+		{
+			if (certSelector == null)
+			{
+				return null;
+			}
+
+			return (X509CertStoreSelector)certSelector.Clone();
+		}
+
+		/**
+		 * Sets the required constraints on the target certificate. The constraints
+		 * are specified as an instance of CertSelector. If null, no constraints are
+		 * defined.<br />
+		 * <br />
+		 * Note that the CertSelector specified is cloned to protect against
+		 * subsequent modifications.
+		 *
+		 * @param selector
+		 *            a CertSelector specifying the constraints on the target
+		 *            certificate (or <code>null</code>)
+		 *
+		 * @see #getTargetCertConstraints()
+		 */
+		public virtual void SetTargetCertConstraints(
+			IX509Selector selector)
+		{
+			if (selector == null)
+			{
+				certSelector = null;
+			}
+			else
+			{
+				certSelector = (IX509Selector)selector.Clone();
+			}
+		}
+
+		/**
+		* Returns an immutable Set of initial policy identifiers (OID strings),
+		* indicating that any one of these policies would be acceptable to the
+		* certificate user for the purposes of certification path processing. The
+		* default return value is an empty <code>Set</code>, which is
+		* interpreted as meaning that any policy would be acceptable.
+		*
+		* @return an immutable <code>Set</code> of initial policy OIDs in String
+		*         format, or an empty <code>Set</code> (implying any policy is
+		*         acceptable). Never returns <code>null</code>.
+		*
+		* @see #setInitialPolicies(java.util.Set)
+		*/
+		public virtual ISet GetInitialPolicies()
+		{
+			ISet returnSet = initialPolicies;
+
+			// TODO Can it really be null?
+			if (initialPolicies == null)
+			{
+				returnSet = new HashSet();
+			}
+
+			return new HashSet(returnSet);
+		}
+
+		/**
+		* Sets the <code>Set</code> of initial policy identifiers (OID strings),
+		* indicating that any one of these policies would be acceptable to the
+		* certificate user for the purposes of certification path processing. By
+		* default, any policy is acceptable (i.e. all policies), so a user that
+		* wants to allow any policy as acceptable does not need to call this
+		* method, or can call it with an empty <code>Set</code> (or
+		* <code>null</code>).<br />
+		* <br />
+		* Note that the Set is copied to protect against subsequent modifications.<br />
+		* <br />
+		*
+		* @param initialPolicies
+		*            a Set of initial policy OIDs in String format (or
+		*            <code>null</code>)
+		*
+		* @exception ClassCastException
+		*                if any of the elements in the set are not of type String
+		*
+		* @see #getInitialPolicies()
+		*/
+		public virtual void SetInitialPolicies(
+			ISet initialPolicies)
+		{
+			this.initialPolicies = new HashSet();
+			if (initialPolicies != null)
+			{
+				foreach (string obj in initialPolicies)
+				{
+					if (obj != null)
+					{
+						this.initialPolicies.Add(obj);
+					}
+				}
+			}
+		}
+
+		/**
+		* Sets a <code>List</code> of additional certification path checkers. If
+		* the specified List contains an object that is not a PKIXCertPathChecker,
+		* it is ignored.<br />
+		* <br />
+		* Each <code>PKIXCertPathChecker</code> specified implements additional
+		* checks on a certificate. Typically, these are checks to process and
+		* verify private extensions contained in certificates. Each
+		* <code>PKIXCertPathChecker</code> should be instantiated with any
+		* initialization parameters needed to execute the check.<br />
+		* <br />
+		* This method allows sophisticated applications to extend a PKIX
+		* <code>CertPathValidator</code> or <code>CertPathBuilder</code>. Each
+		* of the specified PKIXCertPathCheckers will be called, in turn, by a PKIX
+		* <code>CertPathValidator</code> or <code>CertPathBuilder</code> for
+		* each certificate processed or validated.<br />
+		* <br />
+		* Regardless of whether these additional PKIXCertPathCheckers are set, a
+		* PKIX <code>CertPathValidator</code> or <code>CertPathBuilder</code>
+		* must perform all of the required PKIX checks on each certificate. The one
+		* exception to this rule is if the RevocationEnabled flag is set to false
+		* (see the {@link #setRevocationEnabled(boolean) setRevocationEnabled}
+		* method).<br />
+		* <br />
+		* Note that the List supplied here is copied and each PKIXCertPathChecker
+		* in the list is cloned to protect against subsequent modifications.
+		*
+		* @param checkers
+		*            a List of PKIXCertPathCheckers. May be null, in which case no
+		*            additional checkers will be used.
+		* @exception ClassCastException
+		*                if any of the elements in the list are not of type
+		*                <code>java.security.cert.PKIXCertPathChecker</code>
+		* @see #getCertPathCheckers()
+		*/
+		public virtual void SetCertPathCheckers(IList checkers)
+		{
+            certPathCheckers = Platform.CreateArrayList();
+			if (checkers != null)
+			{
+				foreach (PkixCertPathChecker obj in checkers)
+				{
+					certPathCheckers.Add(obj.Clone());
+				}
+			}
+		}
+
+		/**
+		 * Returns the List of certification path checkers. Each PKIXCertPathChecker
+		 * in the returned IList is cloned to protect against subsequent modifications.
+		 *
+		 * @return an immutable List of PKIXCertPathCheckers (may be empty, but not
+		 *         <code>null</code>)
+		 *
+		 * @see #setCertPathCheckers(java.util.List)
+		 */
+		public virtual IList GetCertPathCheckers()
+		{
+			IList checkers = Platform.CreateArrayList();
+			foreach (PkixCertPathChecker obj in certPathCheckers)
+			{
+				checkers.Add(obj.Clone());
+			}
+			return checkers;
+		}
+
+		/**
+		 * Adds a <code>PKIXCertPathChecker</code> to the list of certification
+		 * path checkers. See the {@link #setCertPathCheckers setCertPathCheckers}
+		 * method for more details.
+		 * <p>
+		 * Note that the <code>PKIXCertPathChecker</code> is cloned to protect
+		 * against subsequent modifications.</p>
+		 *
+		 * @param checker a <code>PKIXCertPathChecker</code> to add to the list of
+		 * checks. If <code>null</code>, the checker is ignored (not added to list).
+		 */
+		public virtual void AddCertPathChecker(
+			PkixCertPathChecker checker)
+		{
+			if (checker != null)
+			{
+				certPathCheckers.Add(checker.Clone());
+			}
+		}
+
+		public virtual object Clone()
+		{
+			// FIXME Check this whole method against the Java implementation!
+
+			PkixParameters parameters = new PkixParameters(GetTrustAnchors());
+			parameters.SetParams(this);
+			return parameters;
+
+
+//			PkixParameters obj = new PkixParameters(new HashSet());
+////			(PkixParameters) this.MemberwiseClone();
+//			obj.x509Stores = new ArrayList(x509Stores);
+//			obj.certPathCheckers = new ArrayList(certPathCheckers);
+//
+//			//Iterator iter = certPathCheckers.iterator();
+//			//obj.certPathCheckers = new ArrayList();
+//			//while (iter.hasNext())
+//			//{
+//			//	obj.certPathCheckers.add(((PKIXCertPathChecker)iter.next())
+//			//		.clone());
+//			//}
+//			//if (initialPolicies != null)
+//			//{
+//			//	obj.initialPolicies = new HashSet(initialPolicies);
+//			//}
+////			if (trustAnchors != null)
+////			{
+////				obj.trustAnchors = new HashSet(trustAnchors);
+////			}
+////			if (certSelector != null)
+////			{
+////				obj.certSelector = (X509CertStoreSelector) certSelector.Clone();
+////			}
+//			return obj;
+		}
+
+		/**
+		* Method to support <code>Clone()</code> under J2ME.
+		* <code>super.Clone()</code> does not exist and fields are not copied.
+		*
+		* @param params Parameters to set. If this are
+		*            <code>ExtendedPkixParameters</code> they are copied to.
+		*/
+		protected virtual void SetParams(
+			PkixParameters parameters)
+		{
+			Date = parameters.Date;
+			SetCertPathCheckers(parameters.GetCertPathCheckers());
+			IsAnyPolicyInhibited = parameters.IsAnyPolicyInhibited;
+			IsExplicitPolicyRequired = parameters.IsExplicitPolicyRequired;
+			IsPolicyMappingInhibited = parameters.IsPolicyMappingInhibited;
+			IsRevocationEnabled = parameters.IsRevocationEnabled;
+			SetInitialPolicies(parameters.GetInitialPolicies());
+			IsPolicyQualifiersRejected = parameters.IsPolicyQualifiersRejected;
+			SetTargetCertConstraints(parameters.GetTargetCertConstraints());
+			SetTrustAnchors(parameters.GetTrustAnchors());
+
+			validityModel = parameters.validityModel;
+			useDeltas = parameters.useDeltas;
+			additionalLocationsEnabled = parameters.additionalLocationsEnabled;
+			selector = parameters.selector == null ? null
+				: (IX509Selector) parameters.selector.Clone();
+			stores = Platform.CreateArrayList(parameters.stores);
+            additionalStores = Platform.CreateArrayList(parameters.additionalStores);
+			trustedACIssuers = new HashSet(parameters.trustedACIssuers);
+			prohibitedACAttributes = new HashSet(parameters.prohibitedACAttributes);
+			necessaryACAttributes = new HashSet(parameters.necessaryACAttributes);
+			attrCertCheckers = new HashSet(parameters.attrCertCheckers);
+		}
+
+		/**
+		 * Whether delta CRLs should be used for checking the revocation status.
+		 * Defaults to <code>false</code>.
+		 */
+		public virtual bool IsUseDeltasEnabled
+		{
+			get { return useDeltas; }
+			set { useDeltas = value; }
+		}
+
+		/**
+		* The validity model.
+		* @see #CHAIN_VALIDITY_MODEL
+		* @see #PKIX_VALIDITY_MODEL
+		*/
+		public virtual int ValidityModel
+		{
+			get { return validityModel; }
+			set { validityModel = value; }
+		}
+
+		/**
+		* Sets the Bouncy Castle Stores for finding CRLs, certificates, attribute
+		* certificates or cross certificates.
+		* <p>
+		* The <code>IList</code> is cloned.
+		* </p>
+		*
+		* @param stores A list of stores to use.
+		* @see #getStores
+		* @throws ClassCastException if an element of <code>stores</code> is not
+		*             a {@link Store}.
+		*/
+		public virtual void SetStores(
+			IList stores)
+		{
+			if (stores == null)
+			{
+                this.stores = Platform.CreateArrayList();
+			}
+			else
+			{
+				foreach (object obj in stores)
+				{
+					if (!(obj is IX509Store))
+					{
+						throw new InvalidCastException(
+							"All elements of list must be of type " + typeof(IX509Store).FullName);
+					}
+				}
+                this.stores = Platform.CreateArrayList(stores);
+			}
+		}
+
+		/**
+		* Adds a Bouncy Castle {@link Store} to find CRLs, certificates, attribute
+		* certificates or cross certificates.
+		* <p>
+		* This method should be used to add local stores, like collection based
+		* X.509 stores, if available. Local stores should be considered first,
+		* before trying to use additional (remote) locations, because they do not
+		* need possible additional network traffic.
+		* </p><p>
+		* If <code>store</code> is <code>null</code> it is ignored.
+		* </p>
+		*
+		* @param store The store to add.
+		* @see #getStores
+		*/
+		public virtual void AddStore(
+			IX509Store store)
+		{
+			if (store != null)
+			{
+				stores.Add(store);
+			}
+		}
+
+		/**
+		* Adds an additional Bouncy Castle {@link Store} to find CRLs, certificates,
+		* attribute certificates or cross certificates.
+		* <p>
+		* You should not use this method. This method is used for adding additional
+		* X.509 stores, which are used to add (remote) locations, e.g. LDAP, found
+		* during X.509 object processing, e.g. in certificates or CRLs. This method
+		* is used in PKIX certification path processing.
+		* </p><p>
+		* If <code>store</code> is <code>null</code> it is ignored.
+		* </p>
+		*
+		* @param store The store to add.
+		* @see #getStores()
+		*/
+		public virtual void AddAdditionalStore(
+			IX509Store store)
+		{
+			if (store != null)
+			{
+				additionalStores.Add(store);
+			}
+		}
+
+		/**
+		* Returns an <code>IList</code> of additional Bouncy Castle
+		* <code>Store</code>s used for finding CRLs, certificates, attribute
+		* certificates or cross certificates.
+		*
+		* @return an immutable <code>IList</code> of additional Bouncy Castle
+		*         <code>Store</code>s. Never <code>null</code>.
+		*
+		* @see #addAddionalStore(Store)
+		*/
+		public virtual IList GetAdditionalStores()
+		{
+            return Platform.CreateArrayList(additionalStores);
+		}
+
+		/**
+		* Returns an <code>IList</code> of Bouncy Castle
+		* <code>Store</code>s used for finding CRLs, certificates, attribute
+		* certificates or cross certificates.
+		*
+		* @return an immutable <code>IList</code> of Bouncy Castle
+		*         <code>Store</code>s. Never <code>null</code>.
+		*
+		* @see #setStores(IList)
+		*/
+		public virtual IList GetStores()
+		{
+            return Platform.CreateArrayList(stores);
+		}
+
+		/**
+		* Returns if additional {@link X509Store}s for locations like LDAP found
+		* in certificates or CRLs should be used.
+		*
+		* @return Returns <code>true</code> if additional stores are used.
+		*/
+		public virtual bool IsAdditionalLocationsEnabled
+		{
+			get { return additionalLocationsEnabled; }
+		}
+
+		/**
+		* Sets if additional {@link X509Store}s for locations like LDAP found in
+		* certificates or CRLs should be used.
+		*
+		* @param enabled <code>true</code> if additional stores are used.
+		*/
+		public virtual void SetAdditionalLocationsEnabled(
+			bool enabled)
+		{
+			additionalLocationsEnabled = enabled;
+		}
+
+		/**
+		* Returns the required constraints on the target certificate or attribute
+		* certificate. The constraints are returned as an instance of
+		* <code>IX509Selector</code>. If <code>null</code>, no constraints are
+		* defined.
+		*
+		* <p>
+		* The target certificate in a PKIX path may be a certificate or an
+		* attribute certificate.
+		* </p><p>
+		* Note that the <code>IX509Selector</code> returned is cloned to protect
+		* against subsequent modifications.
+		* </p>
+		* @return a <code>IX509Selector</code> specifying the constraints on the
+		*         target certificate or attribute certificate (or <code>null</code>)
+		* @see #setTargetConstraints
+		* @see X509CertStoreSelector
+		* @see X509AttributeCertStoreSelector
+		*/
+		public virtual IX509Selector GetTargetConstraints()
+		{
+			if (selector != null)
+			{
+				return (IX509Selector) selector.Clone();
+			}
+			else
+			{
+				return null;
+			}
+		}
+
+		/**
+		* Sets the required constraints on the target certificate or attribute
+		* certificate. The constraints are specified as an instance of
+		* <code>IX509Selector</code>. If <code>null</code>, no constraints are
+		* defined.
+		* <p>
+		* The target certificate in a PKIX path may be a certificate or an
+		* attribute certificate.
+		* </p><p>
+		* Note that the <code>IX509Selector</code> specified is cloned to protect
+		* against subsequent modifications.
+		* </p>
+		*
+		* @param selector a <code>IX509Selector</code> specifying the constraints on
+		*            the target certificate or attribute certificate (or
+		*            <code>null</code>)
+		* @see #getTargetConstraints
+		* @see X509CertStoreSelector
+		* @see X509AttributeCertStoreSelector
+		*/
+		public virtual void SetTargetConstraints(IX509Selector selector)
+		{
+			if (selector != null)
+			{
+				this.selector = (IX509Selector) selector.Clone();
+			}
+			else
+			{
+				this.selector = null;
+			}
+		}
+
+		/**
+		* Returns the trusted attribute certificate issuers. If attribute
+		* certificates is verified the trusted AC issuers must be set.
+		* <p>
+		* The returned <code>ISet</code> consists of <code>TrustAnchor</code>s.
+		* </p><p>
+		* The returned <code>ISet</code> is immutable. Never <code>null</code>
+		* </p>
+		*
+		* @return Returns an immutable set of the trusted AC issuers.
+		*/
+		public virtual ISet GetTrustedACIssuers()
+		{
+			return new HashSet(trustedACIssuers);
+		}
+
+		/**
+		* Sets the trusted attribute certificate issuers. If attribute certificates
+		* is verified the trusted AC issuers must be set.
+		* <p>
+		* The <code>trustedACIssuers</code> must be a <code>ISet</code> of
+		* <code>TrustAnchor</code>
+		* </p><p>
+		* The given set is cloned.
+		* </p>
+		*
+		* @param trustedACIssuers The trusted AC issuers to set. Is never
+		*            <code>null</code>.
+		* @throws ClassCastException if an element of <code>stores</code> is not
+		*             a <code>TrustAnchor</code>.
+		*/
+		public virtual void SetTrustedACIssuers(
+			ISet trustedACIssuers)
+		{
+			if (trustedACIssuers == null)
+			{
+				this.trustedACIssuers = new HashSet();
+			}
+			else
+			{
+				foreach (object obj in trustedACIssuers)
+				{
+					if (!(obj is TrustAnchor))
+					{
+						throw new InvalidCastException("All elements of set must be "
+							+ "of type " + typeof(TrustAnchor).Name + ".");
+					}
+				}
+				this.trustedACIssuers = new HashSet(trustedACIssuers);
+			}
+		}
+
+		/**
+		* Returns the neccessary attributes which must be contained in an attribute
+		* certificate.
+		* <p>
+		* The returned <code>ISet</code> is immutable and contains
+		* <code>String</code>s with the OIDs.
+		* </p>
+		*
+		* @return Returns the necessary AC attributes.
+		*/
+		public virtual ISet GetNecessaryACAttributes()
+		{
+			return new HashSet(necessaryACAttributes);
+		}
+
+		/**
+		* Sets the neccessary which must be contained in an attribute certificate.
+		* <p>
+		* The <code>ISet</code> must contain <code>String</code>s with the
+		* OIDs.
+		* </p><p>
+		* The set is cloned.
+		* </p>
+		*
+		* @param necessaryACAttributes The necessary AC attributes to set.
+		* @throws ClassCastException if an element of
+		*             <code>necessaryACAttributes</code> is not a
+		*             <code>String</code>.
+		*/
+		public virtual void SetNecessaryACAttributes(
+			ISet necessaryACAttributes)
+		{
+			if (necessaryACAttributes == null)
+			{
+				this.necessaryACAttributes = new HashSet();
+			}
+			else
+			{
+				foreach (object obj in necessaryACAttributes)
+				{
+					if (!(obj is string))
+					{
+						throw new InvalidCastException("All elements of set must be "
+							+ "of type string.");
+					}
+				}
+				this.necessaryACAttributes = new HashSet(necessaryACAttributes);
+			}
+		}
+
+		/**
+		* Returns the attribute certificates which are not allowed.
+		* <p>
+		* The returned <code>ISet</code> is immutable and contains
+		* <code>String</code>s with the OIDs.
+		* </p>
+		*
+		* @return Returns the prohibited AC attributes. Is never <code>null</code>.
+		*/
+		public virtual ISet GetProhibitedACAttributes()
+		{
+			return new HashSet(prohibitedACAttributes);
+		}
+
+		/**
+		* Sets the attribute certificates which are not allowed.
+		* <p>
+		* The <code>ISet</code> must contain <code>String</code>s with the
+		* OIDs.
+		* </p><p>
+		* The set is cloned.
+		* </p>
+		*
+		* @param prohibitedACAttributes The prohibited AC attributes to set.
+		* @throws ClassCastException if an element of
+		*             <code>prohibitedACAttributes</code> is not a
+		*             <code>String</code>.
+		*/
+		public virtual void SetProhibitedACAttributes(
+			ISet prohibitedACAttributes)
+		{
+			if (prohibitedACAttributes == null)
+			{
+				this.prohibitedACAttributes = new HashSet();
+			}
+			else
+			{
+				foreach (object obj in prohibitedACAttributes)
+				{
+					if (!(obj is String))
+					{
+						throw new InvalidCastException("All elements of set must be "
+							+ "of type string.");
+					}
+				}
+				this.prohibitedACAttributes = new HashSet(prohibitedACAttributes);
+			}
+		}
+
+		/**
+		* Returns the attribute certificate checker. The returned set contains
+		* {@link PKIXAttrCertChecker}s and is immutable.
+		*
+		* @return Returns the attribute certificate checker. Is never
+		*         <code>null</code>.
+		*/
+		public virtual ISet GetAttrCertCheckers()
+		{
+			return new HashSet(attrCertCheckers);
+		}
+
+		/**
+		* Sets the attribute certificate checkers.
+		* <p>
+		* All elements in the <code>ISet</code> must a {@link PKIXAttrCertChecker}.
+		* </p>
+		* <p>
+		* The given set is cloned.
+		* </p>
+		*
+		* @param attrCertCheckers The attribute certificate checkers to set. Is
+		*            never <code>null</code>.
+		* @throws ClassCastException if an element of <code>attrCertCheckers</code>
+		*             is not a <code>PKIXAttrCertChecker</code>.
+		*/
+		public virtual void SetAttrCertCheckers(
+			ISet attrCertCheckers)
+		{
+			if (attrCertCheckers == null)
+			{
+				this.attrCertCheckers = new HashSet();
+			}
+			else
+			{
+				foreach (object obj in attrCertCheckers)
+				{
+					if (!(obj is PkixAttrCertChecker))
+					{
+						throw new InvalidCastException("All elements of set must be "
+							+ "of type " + typeof(PkixAttrCertChecker).FullName + ".");
+					}
+				}
+				this.attrCertCheckers = new HashSet(attrCertCheckers);
+			}
+		}
+	}
+}
diff --git a/crypto/src/pkix/PkixPolicyNode.cs b/crypto/src/pkix/PkixPolicyNode.cs
new file mode 100644
index 000000000..fc5b82f6f
--- /dev/null
+++ b/crypto/src/pkix/PkixPolicyNode.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Collections;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/// <summary>
+	/// Summary description for PkixPolicyNode.
+	/// </summary>
+	public class PkixPolicyNode
+//		: IPolicyNode
+	{
+		protected IList				mChildren;
+		protected int				mDepth;
+		protected ISet				mExpectedPolicies;
+		protected PkixPolicyNode	mParent;
+		protected ISet				mPolicyQualifiers;
+		protected string			mValidPolicy;
+		protected bool				mCritical;
+
+		public virtual int Depth
+		{
+			get { return this.mDepth; }
+		}
+
+		public virtual IEnumerable Children
+		{
+			get { return new EnumerableProxy(mChildren); }
+		}
+
+		public virtual bool IsCritical
+		{
+			get { return this.mCritical; }
+			set { this.mCritical = value; }
+		}
+
+		public virtual ISet PolicyQualifiers
+		{
+			get { return new HashSet(this.mPolicyQualifiers); }
+		}
+
+		public virtual string ValidPolicy
+		{
+			get { return this.mValidPolicy; }
+		}
+
+		public virtual bool HasChildren
+		{
+			get { return mChildren.Count != 0; }
+		}
+
+		public virtual ISet ExpectedPolicies
+		{
+			get { return new HashSet(this.mExpectedPolicies); }
+			set { this.mExpectedPolicies = new HashSet(value); }
+		}
+
+		public virtual PkixPolicyNode Parent
+		{
+			get { return this.mParent; }
+			set { this.mParent = value; }
+		}
+
+		/// Constructors
+		public PkixPolicyNode(
+			IList			children,
+			int				depth,
+			ISet			expectedPolicies,
+			PkixPolicyNode	parent,
+			ISet			policyQualifiers,
+			string			validPolicy,
+			bool			critical)
+		{
+            if (children == null)
+            {
+                this.mChildren = Platform.CreateArrayList();
+            }
+            else
+            {
+                this.mChildren = Platform.CreateArrayList(children);
+            }
+
+            this.mDepth = depth;
+			this.mExpectedPolicies = expectedPolicies;
+			this.mParent = parent;
+			this.mPolicyQualifiers = policyQualifiers;
+			this.mValidPolicy = validPolicy;
+			this.mCritical = critical;
+		}
+
+		public virtual void AddChild(
+			PkixPolicyNode child)
+		{
+			child.Parent = this;
+			mChildren.Add(child);
+		}
+
+		public virtual void RemoveChild(
+			PkixPolicyNode child)
+		{
+			mChildren.Remove(child);
+		}
+
+		public override string ToString()
+		{
+			return ToString("");
+		}
+
+		public virtual string ToString(
+			string indent)
+		{
+			StringBuilder buf = new StringBuilder();
+			buf.Append(indent);
+			buf.Append(mValidPolicy);
+			buf.Append(" {");
+			buf.Append(Platform.NewLine);
+
+			foreach (PkixPolicyNode child in mChildren)
+			{
+				buf.Append(child.ToString(indent + "    "));
+			}
+
+			buf.Append(indent);
+			buf.Append("}");
+			buf.Append(Platform.NewLine);
+			return buf.ToString();
+		}
+
+		public virtual object Clone()
+		{
+			return Copy();
+		}
+
+		public virtual PkixPolicyNode Copy()
+		{
+			PkixPolicyNode node = new PkixPolicyNode(
+                Platform.CreateArrayList(),
+				mDepth,
+				new HashSet(mExpectedPolicies),
+				null,
+				new HashSet(mPolicyQualifiers),
+				mValidPolicy,
+				mCritical);
+
+			foreach (PkixPolicyNode child in mChildren)
+			{
+				PkixPolicyNode copy = child.Copy();
+				copy.Parent = node;
+				node.AddChild(copy);
+			}
+
+			return node;
+		}
+	}
+}
diff --git a/crypto/src/pkix/ReasonsMask.cs b/crypto/src/pkix/ReasonsMask.cs
new file mode 100644
index 000000000..e389bfe11
--- /dev/null
+++ b/crypto/src/pkix/ReasonsMask.cs
@@ -0,0 +1,96 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/// <summary>
+	/// This class helps to handle CRL revocation reasons mask. Each CRL handles a
+	/// certain set of revocation reasons.
+	/// </summary>
+	internal class ReasonsMask
+	{
+		private int _reasons;
+
+		/// <summary>
+		/// Constructs are reason mask with the reasons.
+		/// </summary>
+		/// <param name="reasons">The reasons.</param>
+		internal ReasonsMask(
+			int reasons)
+		{
+			_reasons = reasons;
+		}
+
+		/// <summary>
+		/// A reason mask with no reason.
+		/// </summary>
+		internal ReasonsMask()
+			: this(0)
+		{
+		}
+
+		/// <summary>
+		/// A mask with all revocation reasons.
+		/// </summary>
+		internal static readonly ReasonsMask AllReasons = new ReasonsMask(
+				ReasonFlags.AACompromise | ReasonFlags.AffiliationChanged | ReasonFlags.CACompromise
+			|	ReasonFlags.CertificateHold | ReasonFlags.CessationOfOperation
+			|	ReasonFlags.KeyCompromise | ReasonFlags.PrivilegeWithdrawn | ReasonFlags.Unused
+			|	ReasonFlags.Superseded);
+
+		/**
+		 * Adds all reasons from the reasons mask to this mask.
+		 *
+		 * @param mask The reasons mask to add.
+		 */
+		internal void AddReasons(
+			ReasonsMask mask)
+		{
+			_reasons = _reasons | mask.Reasons.IntValue;
+		}
+
+		/// <summary>
+		/// Returns <code>true</code> if this reasons mask contains all possible
+		/// reasons.
+		/// </summary>
+		/// <returns>true if this reasons mask contains all possible reasons.
+		/// </returns>
+		internal bool IsAllReasons
+		{
+			get { return _reasons == AllReasons._reasons; }
+		}
+
+		/// <summary>
+		/// Intersects this mask with the given reasons mask.
+		/// </summary>
+		/// <param name="mask">mask The mask to intersect with.</param>
+		/// <returns>The intersection of this and teh given mask.</returns>
+		internal ReasonsMask Intersect(
+			ReasonsMask mask)
+		{
+			ReasonsMask _mask = new ReasonsMask();
+			_mask.AddReasons(new ReasonsMask(_reasons & mask.Reasons.IntValue));
+			return _mask;
+		}
+
+		/// <summary>
+		/// Returns <c>true</c> if the passed reasons mask has new reasons.
+		/// </summary>
+		/// <param name="mask">The reasons mask which should be tested for new reasons.</param>
+		/// <returns><c>true</c> if the passed reasons mask has new reasons.</returns>
+		internal bool HasNewReasons(
+			ReasonsMask mask)
+		{
+			return ((_reasons | mask.Reasons.IntValue ^ _reasons) != 0);
+		}
+
+		/// <summary>
+		/// Returns the reasons in this mask.
+		/// </summary>
+		public ReasonFlags Reasons
+		{
+			get { return new ReasonFlags(_reasons); }
+		}
+	}
+}
diff --git a/crypto/src/pkix/Rfc3280CertPathUtilities.cs b/crypto/src/pkix/Rfc3280CertPathUtilities.cs
new file mode 100644
index 000000000..bae657d90
--- /dev/null
+++ b/crypto/src/pkix/Rfc3280CertPathUtilities.cs
@@ -0,0 +1,2448 @@
+using System;
+using System.Collections;
+using System.Globalization;
+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;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Pkix
+{
+	public class Rfc3280CertPathUtilities
+	{
+		private static readonly PkixCrlUtilities CrlUtilities = new PkixCrlUtilities();
+
+		internal static readonly string ANY_POLICY = "2.5.29.32.0";
+
+		// key usage bits
+		internal static readonly int KEY_CERT_SIGN = 5;
+		internal static readonly int CRL_SIGN = 6;
+
+		/**
+		* If the complete CRL includes an issuing distribution point (IDP) CRL
+		* extension check the following:
+		* <p>
+		* (i) If the distribution point name is present in the IDP CRL extension
+		* and the distribution field is present in the DP, then verify that one of
+		* the names in the IDP matches one of the names in the DP. If the
+		* distribution point name is present in the IDP CRL extension and the
+		* distribution field is omitted from the DP, then verify that one of the
+		* names in the IDP matches one of the names in the cRLIssuer field of the
+		* DP.
+		* </p>
+		* <p>
+		* (ii) If the onlyContainsUserCerts boolean is asserted in the IDP CRL
+		* extension, verify that the certificate does not include the basic
+		* constraints extension with the cA boolean asserted.
+		* </p>
+		* <p>
+		* (iii) If the onlyContainsCACerts boolean is asserted in the IDP CRL
+		* extension, verify that the certificate includes the basic constraints
+		* extension with the cA boolean asserted.
+		* </p>
+		* <p>
+		* (iv) Verify that the onlyContainsAttributeCerts boolean is not asserted.
+		* </p>
+		*
+		* @param dp   The distribution point.
+		* @param cert The certificate.
+		* @param crl  The CRL.
+		* @throws AnnotatedException if one of the conditions is not met or an error occurs.
+		*/
+		internal static void ProcessCrlB2(
+			DistributionPoint	dp,
+			object				cert,
+			X509Crl				crl)
+		{
+			IssuingDistributionPoint idp = null;
+			try
+			{
+				idp = IssuingDistributionPoint.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(crl, X509Extensions.IssuingDistributionPoint));
+			}
+			catch (Exception e)
+			{
+				throw new Exception("0 Issuing distribution point extension could not be decoded.", e);
+			}
+			// (b) (2) (i)
+			// distribution point name is present
+			if (idp != null)
+			{
+				if (idp.DistributionPoint != null)
+				{
+					// make list of names
+					DistributionPointName dpName = IssuingDistributionPoint.GetInstance(idp).DistributionPoint;
+					IList names = Platform.CreateArrayList();
+
+					if (dpName.PointType == DistributionPointName.FullName)
+					{
+						GeneralName[] genNames = GeneralNames.GetInstance(dpName.Name).GetNames();
+						for (int j = 0; j < genNames.Length; j++)
+						{
+							names.Add(genNames[j]);
+						}
+					}
+					if (dpName.PointType == DistributionPointName.NameRelativeToCrlIssuer)
+					{
+						Asn1EncodableVector vec = new Asn1EncodableVector();
+						try
+						{
+							IEnumerator e = Asn1Sequence.GetInstance(
+								Asn1Sequence.FromByteArray(crl.IssuerDN.GetEncoded())).GetEnumerator();
+							while (e.MoveNext())
+							{
+								vec.Add((Asn1Encodable)e.Current);
+							}
+						}
+						catch (IOException e)
+						{
+							throw new Exception("Could not read CRL issuer.", e);
+						}
+						vec.Add(dpName.Name);
+						names.Add(new GeneralName(X509Name.GetInstance(new DerSequence(vec))));
+					}
+					bool matches = false;
+					// verify that one of the names in the IDP matches one
+					// of the names in the DP.
+					if (dp.DistributionPointName != null)
+					{
+						dpName = dp.DistributionPointName;
+						GeneralName[] genNames = null;
+						if (dpName.PointType == DistributionPointName.FullName)
+						{
+							genNames = GeneralNames.GetInstance(dpName.Name).GetNames();
+						}
+						if (dpName.PointType == DistributionPointName.NameRelativeToCrlIssuer)
+						{
+							if (dp.CrlIssuer != null)
+							{
+								genNames = dp.CrlIssuer.GetNames();
+							}
+							else
+							{
+								genNames = new GeneralName[1];
+								try
+								{
+									genNames[0] = new GeneralName(
+										PkixCertPathValidatorUtilities.GetIssuerPrincipal(cert));
+								}
+								catch (IOException e)
+								{
+									throw new Exception("Could not read certificate issuer.", e);
+								}
+							}
+							for (int j = 0; j < genNames.Length; j++)
+							{
+								IEnumerator e = Asn1Sequence.GetInstance(genNames[j].Name.ToAsn1Object()).GetEnumerator();
+								Asn1EncodableVector vec = new Asn1EncodableVector();
+								while (e.MoveNext())
+								{
+									vec.Add((Asn1Encodable)e.Current);
+								}
+								vec.Add(dpName.Name);
+								genNames[j] = new GeneralName(X509Name.GetInstance(new DerSequence(vec)));
+							}
+						}
+						if (genNames != null)
+						{
+							for (int j = 0; j < genNames.Length; j++)
+							{
+								if (names.Contains(genNames[j]))
+								{
+									matches = true;
+									break;
+								}
+							}
+						}
+						if (!matches)
+						{
+							throw new Exception(
+								"No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
+						}
+					}
+						// verify that one of the names in
+						// the IDP matches one of the names in the cRLIssuer field of
+						// the DP
+					else
+					{
+						if (dp.CrlIssuer == null)
+						{
+							throw new Exception("Either the cRLIssuer or the distributionPoint field must "
+								+ "be contained in DistributionPoint.");
+						}
+						GeneralName[] genNames = dp.CrlIssuer.GetNames();
+						for (int j = 0; j < genNames.Length; j++)
+						{
+							if (names.Contains(genNames[j]))
+							{
+								matches = true;
+								break;
+							}
+						}
+						if (!matches)
+						{
+							throw new Exception(
+								"No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point.");
+						}
+					}
+				}
+				BasicConstraints bc = null;
+				try
+				{
+					bc = BasicConstraints.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(
+						(IX509Extension)cert, X509Extensions.BasicConstraints));
+				}
+				catch (Exception e)
+				{
+					throw new Exception("Basic constraints extension could not be decoded.", e);
+				}
+
+				//if (cert is X509Certificate)
+				{
+					// (b) (2) (ii)
+					if (idp.OnlyContainsUserCerts && ((bc != null) && bc.IsCA()))
+					{
+						throw new Exception("CA Cert CRL only contains user certificates.");
+					}
+
+					// (b) (2) (iii)
+					if (idp.OnlyContainsCACerts && (bc == null || !bc.IsCA()))
+					{
+						throw new Exception("End CRL only contains CA certificates.");
+					}
+				}
+
+				// (b) (2) (iv)
+				if (idp.OnlyContainsAttributeCerts)
+				{
+					throw new Exception("onlyContainsAttributeCerts boolean is asserted.");
+				}
+			}
+		}
+
+		internal static void ProcessCertBC(
+			PkixCertPath				certPath,
+			int							index,
+			PkixNameConstraintValidator	nameConstraintValidator)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+			int n = certs.Count;
+			// i as defined in the algorithm description
+			int i = n - index;
+			//
+			// (b), (c) permitted and excluded subtree checking.
+			//
+			if (!(PkixCertPathValidatorUtilities.IsSelfIssued(cert) && (i < n)))
+			{
+				X509Name principal = cert.SubjectDN;
+				Asn1InputStream aIn = new Asn1InputStream(principal.GetEncoded());
+				Asn1Sequence dns;
+
+				try
+				{
+					dns = DerSequence.GetInstance(aIn.ReadObject());
+				}
+				catch (Exception e)
+				{
+					throw new PkixCertPathValidatorException(
+						"Exception extracting subject name when checking subtrees.", e, certPath, index);
+				}
+
+				try
+				{
+					nameConstraintValidator.CheckPermittedDN(dns);
+					nameConstraintValidator.CheckExcludedDN(dns);
+				}
+				catch (PkixNameConstraintValidatorException e)
+				{
+					throw new PkixCertPathValidatorException(
+						"Subtree check for certificate subject failed.", e, certPath, index);
+				}
+
+				GeneralNames altName = null;
+				try
+				{
+					altName = GeneralNames.GetInstance(
+						PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.SubjectAlternativeName));
+				}
+				catch (Exception e)
+				{
+					throw new PkixCertPathValidatorException(
+						"Subject alternative name extension could not be decoded.", e, certPath, index);
+				}
+
+				IList emails = X509Name.GetInstance(dns).GetValueList(X509Name.EmailAddress);
+				foreach (string email in emails)
+				{
+					GeneralName emailAsGeneralName = new GeneralName(GeneralName.Rfc822Name, email);
+					try
+					{
+						nameConstraintValidator.checkPermitted(emailAsGeneralName);
+						nameConstraintValidator.checkExcluded(emailAsGeneralName);
+					}
+					catch (PkixNameConstraintValidatorException ex)
+					{
+						throw new PkixCertPathValidatorException(
+							"Subtree check for certificate subject alternative email failed.", ex, certPath, index);
+					}
+				}
+				if (altName != null)
+				{
+					GeneralName[] genNames = null;
+					try
+					{
+						genNames = altName.GetNames();
+					}
+					catch (Exception e)
+					{
+						throw new PkixCertPathValidatorException(
+							"Subject alternative name contents could not be decoded.", e, certPath, index);
+					}
+					foreach (GeneralName genName in genNames)
+					{
+						try
+						{
+							nameConstraintValidator.checkPermitted(genName);
+							nameConstraintValidator.checkExcluded(genName);
+						}
+						catch (PkixNameConstraintValidatorException e)
+						{
+							throw new PkixCertPathValidatorException(
+								"Subtree check for certificate subject alternative name failed.", e, certPath, index);
+						}
+					}
+				}
+			}
+		}
+
+		internal static void PrepareNextCertA(
+			PkixCertPath	certPath,
+			int				index)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+			//
+			//
+			// (a) check the policy mappings
+			//
+			Asn1Sequence pm = null;
+			try
+			{
+				pm = Asn1Sequence.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyMappings));
+			}
+			catch (Exception ex)
+			{
+				throw new PkixCertPathValidatorException(
+					"Policy mappings extension could not be decoded.", ex, certPath, index);
+			}
+			if (pm != null)
+			{
+				Asn1Sequence mappings = pm;
+
+				for (int j = 0; j < mappings.Count; j++)
+				{
+					DerObjectIdentifier issuerDomainPolicy = null;
+					DerObjectIdentifier subjectDomainPolicy = null;
+					try
+					{
+						Asn1Sequence mapping = DerSequence.GetInstance(mappings[j]);
+
+						issuerDomainPolicy = DerObjectIdentifier.GetInstance(mapping[0]);
+						subjectDomainPolicy = DerObjectIdentifier.GetInstance(mapping[1]);
+					}
+					catch (Exception e)
+					{
+						throw new PkixCertPathValidatorException(
+							"Policy mappings extension contents could not be decoded.", e, certPath, index);
+					}
+
+					if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(issuerDomainPolicy.Id))
+						throw new PkixCertPathValidatorException(
+							"IssuerDomainPolicy is anyPolicy", null, certPath, index);
+
+					if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(subjectDomainPolicy.Id))
+						throw new PkixCertPathValidatorException(
+							"SubjectDomainPolicy is anyPolicy,", null, certPath, index);
+				}
+			}
+		}
+
+		internal static PkixPolicyNode ProcessCertD(
+			PkixCertPath	certPath,
+			int				index,
+			ISet			acceptablePolicies,
+			PkixPolicyNode	validPolicyTree,
+			IList[]			policyNodes,
+			int				inhibitAnyPolicy)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+			int n = certs.Count;
+			// i as defined in the algorithm description
+			int i = n - index;
+			//
+			// (d) policy Information checking against initial policy and
+			// policy mapping
+			//
+			Asn1Sequence certPolicies = null;
+			try
+			{
+				certPolicies = DerSequence.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.CertificatePolicies));
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException(
+					"Could not read certificate policies extension from certificate.", e, certPath, index);
+			}
+			if (certPolicies != null && validPolicyTree != null)
+			{
+				//
+				// (d) (1)
+				//
+				ISet pols = new HashSet();
+
+				foreach (Asn1Encodable ae in certPolicies)
+				{
+					PolicyInformation pInfo = PolicyInformation.GetInstance(ae.ToAsn1Object());
+					DerObjectIdentifier pOid = pInfo.PolicyIdentifier;
+
+					pols.Add(pOid.Id);
+
+					if (!Rfc3280CertPathUtilities.ANY_POLICY.Equals(pOid.Id))
+					{
+						ISet pq = null;
+						try
+						{
+							pq = PkixCertPathValidatorUtilities.GetQualifierSet(pInfo.PolicyQualifiers);
+						}
+						catch (PkixCertPathValidatorException ex)
+						{
+							throw new PkixCertPathValidatorException(
+								"Policy qualifier info set could not be build.", ex, certPath, index);
+						}
+
+						bool match = PkixCertPathValidatorUtilities.ProcessCertD1i(i, policyNodes, pOid, pq);
+
+						if (!match)
+						{
+							PkixCertPathValidatorUtilities.ProcessCertD1ii(i, policyNodes, pOid, pq);
+						}
+					}
+				}
+
+				if (acceptablePolicies.IsEmpty || acceptablePolicies.Contains(Rfc3280CertPathUtilities.ANY_POLICY))
+				{
+					acceptablePolicies.Clear();
+					acceptablePolicies.AddAll(pols);
+				}
+				else
+				{
+					ISet t1 = new HashSet();
+
+					foreach (object o in acceptablePolicies)
+					{
+						if (pols.Contains(o))
+						{
+							t1.Add(o);
+						}
+					}
+					acceptablePolicies.Clear();
+					acceptablePolicies.AddAll(t1);
+				}
+
+				//
+				// (d) (2)
+				//
+				if ((inhibitAnyPolicy > 0) || ((i < n) && PkixCertPathValidatorUtilities.IsSelfIssued(cert)))
+				{
+					foreach (Asn1Encodable ae in certPolicies)
+					{
+						PolicyInformation pInfo = PolicyInformation.GetInstance(ae.ToAsn1Object());
+						if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(pInfo.PolicyIdentifier.Id))
+						{
+							ISet _apq = PkixCertPathValidatorUtilities.GetQualifierSet(pInfo.PolicyQualifiers);
+							IList _nodes = policyNodes[i - 1];
+
+							for (int k = 0; k < _nodes.Count; k++)
+							{
+								PkixPolicyNode _node = (PkixPolicyNode)_nodes[k];
+
+								IEnumerator  _policySetIter = _node.ExpectedPolicies.GetEnumerator();
+								while (_policySetIter.MoveNext())
+								{
+									object _tmp = _policySetIter.Current;
+
+									string _policy;
+									if (_tmp is string)
+									{
+										_policy = (string)_tmp;
+									}
+									else if (_tmp is DerObjectIdentifier)
+									{
+										_policy = ((DerObjectIdentifier)_tmp).Id;
+									}
+									else
+									{
+										continue;
+									}
+
+									bool _found = false;
+
+									foreach (PkixPolicyNode _child in _node.Children)
+									{
+										if (_policy.Equals(_child.ValidPolicy))
+										{
+											_found = true;
+										}
+									}
+
+									if (!_found)
+									{
+										ISet _newChildExpectedPolicies = new HashSet();
+										_newChildExpectedPolicies.Add(_policy);
+
+										PkixPolicyNode _newChild = new PkixPolicyNode(Platform.CreateArrayList(), i,
+											_newChildExpectedPolicies, _node, _apq, _policy, false);
+										_node.AddChild(_newChild);
+										policyNodes[i].Add(_newChild);
+									}
+								}
+							}
+							break;
+						}
+					}
+				}
+
+				PkixPolicyNode _validPolicyTree = validPolicyTree;
+				//
+				// (d) (3)
+				//
+				for (int j = (i - 1); j >= 0; j--)
+				{
+					IList nodes = policyNodes[j];
+
+					for (int k = 0; k < nodes.Count; k++)
+					{
+						PkixPolicyNode node = (PkixPolicyNode)nodes[k];
+						if (!node.HasChildren)
+						{
+							_validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode(_validPolicyTree, policyNodes,
+								node);
+							if (_validPolicyTree == null)
+							{
+								break;
+							}
+						}
+					}
+				}
+
+				//
+				// d (4)
+				//
+				ISet criticalExtensionOids = cert.GetCriticalExtensionOids();
+
+				if (criticalExtensionOids != null)
+				{
+					bool critical = criticalExtensionOids.Contains(X509Extensions.CertificatePolicies.Id);
+
+					IList nodes = policyNodes[i];
+					for (int j = 0; j < nodes.Count; j++)
+					{
+						PkixPolicyNode node = (PkixPolicyNode)nodes[j];
+						node.IsCritical = critical;
+					}
+				}
+				return _validPolicyTree;
+			}
+			return null;
+		}
+
+		/**
+		* If the DP includes cRLIssuer, then verify that the issuer field in the
+		* complete CRL matches cRLIssuer in the DP and that the complete CRL
+		* contains an
+		*      g distribution point extension with the indirectCRL
+		* boolean asserted. Otherwise, verify that the CRL issuer matches the
+		* certificate issuer.
+		*
+		* @param dp   The distribution point.
+		* @param cert The certificate ot attribute certificate.
+		* @param crl  The CRL for <code>cert</code>.
+		* @throws AnnotatedException if one of the above conditions does not apply or an error
+		*                            occurs.
+		*/
+		internal static void ProcessCrlB1(
+			DistributionPoint	dp,
+			object				cert,
+			X509Crl				crl)
+		{
+			Asn1Object idp = PkixCertPathValidatorUtilities.GetExtensionValue(
+				crl, X509Extensions.IssuingDistributionPoint);
+
+			bool isIndirect = false;
+			if (idp != null)
+			{
+				if (IssuingDistributionPoint.GetInstance(idp).IsIndirectCrl)
+				{
+					isIndirect = true;
+				}
+			}
+			byte[] issuerBytes = crl.IssuerDN.GetEncoded();
+
+			bool matchIssuer = false;
+			if (dp.CrlIssuer != null)
+			{
+				GeneralName[] genNames = dp.CrlIssuer.GetNames();
+				for (int j = 0; j < genNames.Length; j++)
+				{
+					if (genNames[j].TagNo == GeneralName.DirectoryName)
+					{
+						try
+						{
+							if (Org.BouncyCastle.Utilities.Arrays.AreEqual(genNames[j].Name.ToAsn1Object().GetEncoded(), issuerBytes))
+							{
+								matchIssuer = true;
+							}
+						}
+						catch (IOException e)
+						{
+							throw new Exception(
+								"CRL issuer information from distribution point cannot be decoded.", e);
+						}
+					}
+				}
+				if (matchIssuer && !isIndirect)
+				{
+					throw new Exception("Distribution point contains cRLIssuer field but CRL is not indirect.");
+				}
+				if (!matchIssuer)
+				{
+					throw new Exception("CRL issuer of CRL does not match CRL issuer of distribution point.");
+				}
+			}
+			else
+			{
+				if (crl.IssuerDN.Equivalent(PkixCertPathValidatorUtilities.GetIssuerPrincipal(cert), true))
+				{
+					matchIssuer = true;
+				}
+			}
+			if (!matchIssuer)
+			{
+				throw new Exception("Cannot find matching CRL issuer for certificate.");
+			}
+		}
+
+		internal static ReasonsMask ProcessCrlD(
+			X509Crl				crl,
+			DistributionPoint	dp)
+			//throws AnnotatedException
+		{
+			IssuingDistributionPoint idp = null;
+			try
+			{
+				idp = IssuingDistributionPoint.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(crl, X509Extensions.IssuingDistributionPoint));
+			}
+			catch (Exception e)
+			{
+				throw new Exception("issuing distribution point extension could not be decoded.", e);
+			}
+
+			// (d) (1)
+			if (idp != null && idp.OnlySomeReasons != null && dp.Reasons != null)
+			{
+				return new ReasonsMask(dp.Reasons.IntValue).Intersect(new ReasonsMask(idp.OnlySomeReasons
+					.IntValue));
+			}
+			// (d) (4)
+			if ((idp == null || idp.OnlySomeReasons == null) && dp.Reasons == null)
+			{
+				return ReasonsMask.AllReasons;
+			}
+
+			// (d) (2) and (d)(3)
+
+			ReasonsMask dpReasons = null;
+
+			if (dp.Reasons == null)
+			{
+				dpReasons = ReasonsMask.AllReasons;
+			}
+			else
+			{
+				dpReasons = new ReasonsMask(dp.Reasons.IntValue);
+			}
+
+			ReasonsMask idpReasons = null;
+
+			if (idp == null)
+			{
+				idpReasons = ReasonsMask.AllReasons;
+			}
+			else
+			{
+				idpReasons = new ReasonsMask(idp.OnlySomeReasons.IntValue);
+			}
+
+			return dpReasons.Intersect(idpReasons);
+		}
+
+		/**
+		* Obtain and validate the certification path for the complete CRL issuer.
+		* If a key usage extension is present in the CRL issuer's certificate,
+		* verify that the cRLSign bit is set.
+		*
+		* @param crl                CRL which contains revocation information for the certificate
+		*                           <code>cert</code>.
+		* @param cert               The attribute certificate or certificate to check if it is
+		*                           revoked.
+		* @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
+		* @param defaultCRLSignKey  The public key of the issuer certificate
+		*                           <code>defaultCRLSignCert</code>.
+		* @param paramsPKIX         paramsPKIX PKIX parameters.
+		* @param certPathCerts      The certificates on the certification path.
+		* @return A <code>Set</code> with all keys of possible CRL issuer
+		*         certificates.
+		* @throws AnnotatedException if the CRL is not valid or the status cannot be checked or
+		*                            some error occurs.
+		*/
+		internal static ISet ProcessCrlF(
+			X509Crl					crl,
+			object					cert,
+			X509Certificate			defaultCRLSignCert,
+			AsymmetricKeyParameter	defaultCRLSignKey,
+			PkixParameters			paramsPKIX,
+			IList					certPathCerts)
+		{
+			// (f)
+
+			// get issuer from CRL
+			X509CertStoreSelector selector = new X509CertStoreSelector();
+			try
+			{
+				selector.Subject = crl.IssuerDN;
+			}
+			catch (IOException e)
+			{
+				throw new Exception(
+					"Subject criteria for certificate selector to find issuer certificate for CRL could not be set.", e);
+			}
+
+			// get CRL signing certs
+			IList coll = Platform.CreateArrayList();
+
+			try
+			{
+                CollectionUtilities.AddRange(coll, PkixCertPathValidatorUtilities.FindCertificates(selector, paramsPKIX.GetStores()));
+                CollectionUtilities.AddRange(coll, PkixCertPathValidatorUtilities.FindCertificates(selector, paramsPKIX.GetAdditionalStores()));
+			}
+			catch (Exception e)
+			{
+				throw new Exception("Issuer certificate for CRL cannot be searched.", e);
+			}
+
+			coll.Add(defaultCRLSignCert);
+
+			IEnumerator cert_it = coll.GetEnumerator();
+
+            IList validCerts = Platform.CreateArrayList();
+            IList validKeys = Platform.CreateArrayList();
+
+			while (cert_it.MoveNext())
+			{
+				X509Certificate signingCert = (X509Certificate)cert_it.Current;
+
+				/*
+				 * CA of the certificate, for which this CRL is checked, has also
+				 * signed CRL, so skip the path validation, because is already done
+				 */
+				if (signingCert.Equals(defaultCRLSignCert))
+				{
+					validCerts.Add(signingCert);
+					validKeys.Add(defaultCRLSignKey);
+					continue;
+				}
+				try
+				{
+//					CertPathBuilder builder = CertPathBuilder.GetInstance("PKIX");
+					PkixCertPathBuilder builder = new PkixCertPathBuilder();
+					selector = new X509CertStoreSelector();
+					selector.Certificate = signingCert;
+
+					PkixParameters temp = (PkixParameters)paramsPKIX.Clone();
+					temp.SetTargetCertConstraints(selector);
+
+					PkixBuilderParameters parameters = (PkixBuilderParameters)
+						PkixBuilderParameters.GetInstance(temp);
+
+					/*
+					 * if signingCert is placed not higher on the cert path a
+					 * dependency loop results. CRL for cert is checked, but
+					 * signingCert is needed for checking the CRL which is dependent
+					 * on checking cert because it is higher in the cert path and so
+					 * signing signingCert transitively. so, revocation is disabled,
+					 * forgery attacks of the CRL are detected in this outer loop
+					 * for all other it must be enabled to prevent forgery attacks
+					 */
+					if (certPathCerts.Contains(signingCert))
+					{
+						parameters.IsRevocationEnabled = false;
+					}
+					else
+					{
+						parameters.IsRevocationEnabled = true;
+					}
+					IList certs = builder.Build(parameters).CertPath.Certificates;
+					validCerts.Add(signingCert);
+					validKeys.Add(PkixCertPathValidatorUtilities.GetNextWorkingKey(certs, 0));
+				}
+				catch (PkixCertPathBuilderException e)
+				{
+					throw new Exception("Internal error.", e);
+				}
+				catch (PkixCertPathValidatorException e)
+				{
+					throw new Exception("Public key of issuer certificate of CRL could not be retrieved.", e);
+				}
+				//catch (Exception e)
+				//{
+				//    throw new Exception(e.Message);
+				//}
+			}
+
+			ISet checkKeys = new HashSet();
+
+			Exception lastException = null;
+			for (int i = 0; i < validCerts.Count; i++)
+			{
+				X509Certificate signCert = (X509Certificate)validCerts[i];
+				bool[] keyusage = signCert.GetKeyUsage();
+
+				if (keyusage != null && (keyusage.Length < 7 || !keyusage[CRL_SIGN]))
+				{
+					lastException = new Exception(
+						"Issuer certificate key usage extension does not permit CRL signing.");
+				}
+				else
+				{
+					checkKeys.Add(validKeys[i]);
+				}
+			}
+
+			if ((checkKeys.Count == 0) && lastException == null)
+			{
+				throw new Exception("Cannot find a valid issuer certificate.");
+			}
+			if ((checkKeys.Count == 0) && lastException != null)
+			{
+				throw lastException;
+			}
+
+			return checkKeys;
+		}
+
+		internal static AsymmetricKeyParameter ProcessCrlG(
+			X509Crl	crl,
+			ISet	keys)
+		{
+			Exception lastException = null;
+			foreach (AsymmetricKeyParameter key in keys)
+			{
+				try
+				{
+					crl.Verify(key);
+					return key;
+				}
+				catch (Exception e)
+				{
+					lastException = e;
+				}
+			}
+			throw new Exception("Cannot verify CRL.", lastException);
+		}
+
+		internal static X509Crl ProcessCrlH(
+			ISet					deltaCrls,
+			AsymmetricKeyParameter	key)
+		{
+			Exception lastException = null;
+			foreach (X509Crl crl in deltaCrls)
+			{
+				try
+				{
+					crl.Verify(key);
+					return crl;
+				}
+				catch (Exception e)
+				{
+					lastException = e;
+				}
+			}
+			if (lastException != null)
+			{
+				throw new Exception("Cannot verify delta CRL.", lastException);
+			}
+			return null;
+		}
+
+		/**
+		* Checks a distribution point for revocation information for the
+		* certificate <code>cert</code>.
+		*
+		* @param dp                 The distribution point to consider.
+		* @param paramsPKIX         PKIX parameters.
+		* @param cert               Certificate to check if it is revoked.
+		* @param validDate          The date when the certificate revocation status should be
+		*                           checked.
+		* @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>.
+		* @param defaultCRLSignKey  The public key of the issuer certificate
+		*                           <code>defaultCRLSignCert</code>.
+		* @param certStatus         The current certificate revocation status.
+		* @param reasonMask         The reasons mask which is already checked.
+		* @param certPathCerts      The certificates of the certification path.
+		* @throws AnnotatedException if the certificate is revoked or the status cannot be checked
+		*                            or some error occurs.
+		*/
+		private static void CheckCrl(
+			DistributionPoint dp,
+			PkixParameters paramsPKIX,
+			X509Certificate cert,
+			DateTime validDate,
+			X509Certificate defaultCRLSignCert,
+			AsymmetricKeyParameter defaultCRLSignKey,
+			CertStatus certStatus,
+			ReasonsMask reasonMask,
+			IList certPathCerts)
+			//throws AnnotatedException
+		{
+			DateTime currentDate = DateTime.UtcNow;
+
+			if (validDate.Ticks > currentDate.Ticks)
+			{
+				throw new Exception("Validation time is in future.");
+			}
+
+			// (a)
+			/*
+			 * We always get timely valid CRLs, so there is no step (a) (1).
+			 * "locally cached" CRLs are assumed to be in getStore(), additional
+			 * CRLs must be enabled in the ExtendedPKIXParameters and are in
+			 * getAdditionalStore()
+			 */
+
+			ISet crls = PkixCertPathValidatorUtilities.GetCompleteCrls(dp, cert, currentDate, paramsPKIX);
+			bool validCrlFound = false;
+			Exception lastException = null;
+
+			IEnumerator crl_iter = crls.GetEnumerator();
+
+			while (crl_iter.MoveNext() && certStatus.Status == CertStatus.Unrevoked && !reasonMask.IsAllReasons)
+			{
+				try
+				{
+					X509Crl crl = (X509Crl)crl_iter.Current;
+
+					// (d)
+					ReasonsMask interimReasonsMask = Rfc3280CertPathUtilities.ProcessCrlD(crl, dp);
+
+					// (e)
+					/*
+					 * The reasons mask is updated at the end, so only valid CRLs
+					 * can update it. If this CRL does not contain new reasons it
+					 * must be ignored.
+					 */
+					if (!interimReasonsMask.HasNewReasons(reasonMask))
+					{
+						continue;
+					}
+
+					// (f)
+					ISet keys = Rfc3280CertPathUtilities.ProcessCrlF(crl, cert, defaultCRLSignCert, defaultCRLSignKey,
+						paramsPKIX, certPathCerts);
+					// (g)
+					AsymmetricKeyParameter key = Rfc3280CertPathUtilities.ProcessCrlG(crl, keys);
+
+					X509Crl deltaCRL = null;
+
+					if (paramsPKIX.IsUseDeltasEnabled)
+					{
+						// get delta CRLs
+						ISet deltaCRLs = PkixCertPathValidatorUtilities.GetDeltaCrls(currentDate, paramsPKIX, crl);
+						// we only want one valid delta CRL
+						// (h)
+						deltaCRL = Rfc3280CertPathUtilities.ProcessCrlH(deltaCRLs, key);
+					}
+
+					/*
+					 * CRL must be be valid at the current time, not the validation
+					 * time. If a certificate is revoked with reason keyCompromise,
+					 * cACompromise, it can be used for forgery, also for the past.
+					 * This reason may not be contained in older CRLs.
+					 */
+
+					/*
+					 * in the chain model signatures stay valid also after the
+					 * certificate has been expired, so they do not have to be in
+					 * the CRL validity time
+					 */
+
+					if (paramsPKIX.ValidityModel != PkixParameters.ChainValidityModel)
+					{
+						/*
+						 * if a certificate has expired, but was revoked, it is not
+						 * more in the CRL, so it would be regarded as valid if the
+						 * first check is not done
+						 */
+						if (cert.NotAfter.Ticks < crl.ThisUpdate.Ticks)
+						{
+							throw new Exception("No valid CRL for current time found.");
+						}
+					}
+
+					Rfc3280CertPathUtilities.ProcessCrlB1(dp, cert, crl);
+
+					// (b) (2)
+					Rfc3280CertPathUtilities.ProcessCrlB2(dp, cert, crl);
+
+					// (c)
+					Rfc3280CertPathUtilities.ProcessCrlC(deltaCRL, crl, paramsPKIX);
+
+					// (i)
+					Rfc3280CertPathUtilities.ProcessCrlI(validDate, deltaCRL, cert, certStatus, paramsPKIX);
+
+					// (j)
+					Rfc3280CertPathUtilities.ProcessCrlJ(validDate, crl, cert, certStatus);
+
+					// (k)
+					if (certStatus.Status == CrlReason.RemoveFromCrl)
+					{
+						certStatus.Status = CertStatus.Unrevoked;
+					}
+
+					// update reasons mask
+					reasonMask.AddReasons(interimReasonsMask);
+
+					ISet criticalExtensions = crl.GetCriticalExtensionOids();
+
+					if (criticalExtensions != null)
+					{
+						criticalExtensions = new HashSet(criticalExtensions);
+						criticalExtensions.Remove(X509Extensions.IssuingDistributionPoint.Id);
+						criticalExtensions.Remove(X509Extensions.DeltaCrlIndicator.Id);
+
+						if (!criticalExtensions.IsEmpty)
+							throw new Exception("CRL contains unsupported critical extensions.");
+					}
+
+					if (deltaCRL != null)
+					{
+						criticalExtensions = deltaCRL.GetCriticalExtensionOids();
+						if (criticalExtensions != null)
+						{
+							criticalExtensions = new HashSet(criticalExtensions);
+							criticalExtensions.Remove(X509Extensions.IssuingDistributionPoint.Id);
+							criticalExtensions.Remove(X509Extensions.DeltaCrlIndicator.Id);
+
+							if (!criticalExtensions.IsEmpty)
+								throw new Exception("Delta CRL contains unsupported critical extension.");
+						}
+					}
+
+					validCrlFound = true;
+				}
+				catch (Exception e)
+				{
+					lastException = e;
+				}
+			}
+			if (!validCrlFound)
+			{
+				throw lastException;
+			}
+		}
+
+		/**
+		 * Checks a certificate if it is revoked.
+		 *
+		 * @param paramsPKIX       PKIX parameters.
+		 * @param cert             Certificate to check if it is revoked.
+		 * @param validDate        The date when the certificate revocation status should be
+		 *                         checked.
+		 * @param sign             The issuer certificate of the certificate <code>cert</code>.
+		 * @param workingPublicKey The public key of the issuer certificate <code>sign</code>.
+		 * @param certPathCerts    The certificates of the certification path.
+		 * @throws AnnotatedException if the certificate is revoked or the status cannot be checked
+		 *                            or some error occurs.
+		 */
+		protected static void CheckCrls(
+			PkixParameters			paramsPKIX,
+			X509Certificate			cert,
+			DateTime				validDate,
+			X509Certificate			sign,
+			AsymmetricKeyParameter	workingPublicKey,
+			IList					certPathCerts)
+		{
+			Exception lastException = null;
+			CrlDistPoint crldp = null;
+
+			try
+			{
+				crldp = CrlDistPoint.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.CrlDistributionPoints));
+			}
+			catch (Exception e)
+			{
+				throw new Exception("CRL distribution point extension could not be read.", e);
+			}
+
+			try
+			{
+				PkixCertPathValidatorUtilities.AddAdditionalStoresFromCrlDistributionPoint(crldp, paramsPKIX);
+			}
+			catch (Exception e)
+			{
+				throw new Exception(
+					"No additional CRL locations could be decoded from CRL distribution point extension.", e);
+			}
+			CertStatus certStatus = new CertStatus();
+			ReasonsMask reasonsMask = new ReasonsMask();
+
+			bool validCrlFound = false;
+
+			// for each distribution point
+			if (crldp != null)
+			{
+				DistributionPoint[] dps = null;
+				try
+				{
+					dps = crldp.GetDistributionPoints();
+				}
+				catch (Exception e)
+				{
+					throw new Exception("Distribution points could not be read.", e);
+				}
+				if (dps != null)
+				{
+					for (int i = 0; i < dps.Length && certStatus.Status == CertStatus.Unrevoked && !reasonsMask.IsAllReasons; i++)
+					{
+						PkixParameters paramsPKIXClone = (PkixParameters)paramsPKIX.Clone();
+						try
+						{
+							CheckCrl(dps[i], paramsPKIXClone, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask, certPathCerts);
+							validCrlFound = true;
+						}
+						catch (Exception e)
+						{
+							lastException = e;
+						}
+					}
+				}
+			}
+
+			/*
+			 * If the revocation status has not been determined, repeat the process
+			 * above with any available CRLs not specified in a distribution point
+			 * but issued by the certificate issuer.
+			 */
+
+			if (certStatus.Status == CertStatus.Unrevoked && !reasonsMask.IsAllReasons)
+			{
+				try
+				{
+					/*
+					 * assume a DP with both the reasons and the cRLIssuer fields
+					 * omitted and a distribution point name of the certificate
+					 * issuer.
+					 */
+					Asn1Object issuer = null;
+					try
+					{
+						issuer = new Asn1InputStream(cert.IssuerDN.GetEncoded()).ReadObject();
+					}
+					catch (Exception e)
+					{
+						throw new Exception("Issuer from certificate for CRL could not be reencoded.", e);
+					}
+					DistributionPoint dp = new DistributionPoint(new DistributionPointName(0, new GeneralNames(
+						new GeneralName(GeneralName.DirectoryName, issuer))), null, null);
+					PkixParameters paramsPKIXClone = (PkixParameters)paramsPKIX.Clone();
+
+					CheckCrl(dp, paramsPKIXClone, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask,
+						certPathCerts);
+
+					validCrlFound = true;
+				}
+				catch (Exception e)
+				{
+					lastException = e;
+				}
+			}
+
+			if (!validCrlFound)
+			{
+				throw lastException;
+			}
+			if (certStatus.Status != CertStatus.Unrevoked)
+			{
+				// TODO This format is forced by the NistCertPath tests
+				string formattedDate = certStatus.RevocationDate.Value.ToString(
+					"G", new CultureInfo("en-us"));
+				string message = "Certificate revocation after " + formattedDate;
+				message += ", reason: " + CrlReasons[certStatus.Status];
+				throw new Exception(message);
+			}
+
+			if (!reasonsMask.IsAllReasons && certStatus.Status == CertStatus.Unrevoked)
+			{
+				certStatus.Status = CertStatus.Undetermined;
+			}
+
+			if (certStatus.Status == CertStatus.Undetermined)
+			{
+				throw new Exception("Certificate status could not be determined.");
+			}
+		}
+
+		internal static PkixPolicyNode PrepareCertB(
+			PkixCertPath	certPath,
+			int				index,
+			IList[]			policyNodes,
+			PkixPolicyNode	validPolicyTree,
+			int				policyMapping)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+			int n = certs.Count;
+			// i as defined in the algorithm description
+			int i = n - index;
+			// (b)
+			//
+			Asn1Sequence pm = null;
+			try
+			{
+				pm = (Asn1Sequence)Asn1Sequence.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyMappings));
+			}
+			catch (Exception ex)
+			{
+				throw new PkixCertPathValidatorException(
+					"Policy mappings extension could not be decoded.", ex, certPath, index);
+			}
+			PkixPolicyNode _validPolicyTree = validPolicyTree;
+			if (pm != null)
+			{
+				Asn1Sequence mappings = (Asn1Sequence)pm;
+				IDictionary m_idp = Platform.CreateHashtable();
+				ISet s_idp = new HashSet();
+
+				for (int j = 0; j < mappings.Count; j++)
+				{
+					Asn1Sequence mapping = (Asn1Sequence) mappings[j];
+					string id_p = ((DerObjectIdentifier) mapping[0]).Id;
+					string sd_p = ((DerObjectIdentifier) mapping[1]).Id;
+					ISet tmp;
+
+					if (!m_idp.Contains(id_p))
+					{
+						tmp = new HashSet();
+						tmp.Add(sd_p);
+						m_idp[id_p] = tmp;
+						s_idp.Add(id_p);
+					}
+					else
+					{
+						tmp = (ISet)m_idp[id_p];
+						tmp.Add(sd_p);
+					}
+				}
+
+				IEnumerator it_idp = s_idp.GetEnumerator();
+				while (it_idp.MoveNext())
+				{
+					string id_p = (string)it_idp.Current;
+
+					//
+					// (1)
+					//
+					if (policyMapping > 0)
+					{
+						bool idp_found = false;
+						IEnumerator nodes_i = policyNodes[i].GetEnumerator();
+
+						while (nodes_i.MoveNext())
+						{
+							PkixPolicyNode node = (PkixPolicyNode)nodes_i.Current;
+							if (node.ValidPolicy.Equals(id_p))
+							{
+								idp_found = true;
+								node.ExpectedPolicies = (ISet)m_idp[id_p];
+								break;
+							}
+						}
+
+						if (!idp_found)
+						{
+							nodes_i = policyNodes[i].GetEnumerator();
+							while (nodes_i.MoveNext())
+							{
+								PkixPolicyNode node = (PkixPolicyNode)nodes_i.Current;
+								if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(node.ValidPolicy))
+								{
+									ISet pq = null;
+									Asn1Sequence policies = null;
+									try
+									{
+										policies = (Asn1Sequence)PkixCertPathValidatorUtilities.GetExtensionValue(cert,
+											X509Extensions.CertificatePolicies);
+									}
+									catch (Exception e)
+									{
+										throw new PkixCertPathValidatorException(
+											"Certificate policies extension could not be decoded.", e, certPath, index);
+									}
+
+									foreach (Asn1Encodable ae in policies)
+									{
+										PolicyInformation pinfo = null;
+										try
+										{
+											pinfo = PolicyInformation.GetInstance(ae.ToAsn1Object());
+										}
+										catch (Exception ex)
+										{
+											throw new PkixCertPathValidatorException(
+												"Policy information could not be decoded.", ex, certPath, index);
+										}
+										if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(pinfo.PolicyIdentifier.Id))
+										{
+											try
+											{
+												pq = PkixCertPathValidatorUtilities
+													.GetQualifierSet(pinfo.PolicyQualifiers);
+											}
+											catch (PkixCertPathValidatorException ex)
+											{
+												throw new PkixCertPathValidatorException(
+													"Policy qualifier info set could not be decoded.", ex, certPath,
+													index);
+											}
+											break;
+										}
+									}
+									bool ci = false;
+									ISet critExtOids = cert.GetCriticalExtensionOids();
+									if (critExtOids != null)
+									{
+										ci = critExtOids.Contains(X509Extensions.CertificatePolicies.Id);
+									}
+
+									PkixPolicyNode p_node = (PkixPolicyNode)node.Parent;
+									if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(p_node.ValidPolicy))
+									{
+										PkixPolicyNode c_node = new PkixPolicyNode(Platform.CreateArrayList(), i,
+											(ISet)m_idp[id_p], p_node, pq, id_p, ci);
+										p_node.AddChild(c_node);
+										policyNodes[i].Add(c_node);
+									}
+									break;
+								}
+							}
+						}
+
+						//
+						// (2)
+						//
+					}
+					else if (policyMapping <= 0)
+					{
+                        foreach (PkixPolicyNode node in Platform.CreateArrayList(policyNodes[i]))
+                        {
+							if (node.ValidPolicy.Equals(id_p))
+							{
+								node.Parent.RemoveChild(node);
+
+                                for (int k = i - 1; k >= 0; k--)
+								{
+                                    foreach (PkixPolicyNode node2 in Platform.CreateArrayList(policyNodes[k]))
+									{
+										if (!node2.HasChildren)
+										{
+											_validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode(
+												_validPolicyTree, policyNodes, node2);
+
+                                            if (_validPolicyTree == null)
+												break;
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+			return _validPolicyTree;
+		}
+
+		internal static ISet[] ProcessCrlA1ii(
+			DateTime		currentDate,
+			PkixParameters	paramsPKIX,
+			X509Certificate	cert,
+			X509Crl			crl)
+		{
+			ISet deltaSet = new HashSet();
+			X509CrlStoreSelector crlselect = new X509CrlStoreSelector();
+			crlselect.CertificateChecking = cert;
+
+			try
+			{
+				IList issuer = Platform.CreateArrayList();
+				issuer.Add(crl.IssuerDN);
+				crlselect.Issuers = issuer;
+			}
+			catch (IOException e)
+			{
+				throw new Exception("Cannot extract issuer from CRL." + e, e);
+			}
+
+			crlselect.CompleteCrlEnabled = true;
+			ISet completeSet = CrlUtilities.FindCrls(crlselect, paramsPKIX, currentDate);
+
+			if (paramsPKIX.IsUseDeltasEnabled)
+			{
+				// get delta CRL(s)
+				try
+				{
+					deltaSet.AddAll(PkixCertPathValidatorUtilities.GetDeltaCrls(currentDate, paramsPKIX, crl));
+				}
+				catch (Exception e)
+				{
+					throw new Exception("Exception obtaining delta CRLs.", e);
+				}
+			}
+
+			return new ISet[]{ completeSet, deltaSet };
+		}
+
+		internal static ISet ProcessCrlA1i(
+			DateTime		currentDate,
+			PkixParameters	paramsPKIX,
+			X509Certificate	cert,
+			X509Crl			crl)
+		{
+			ISet deltaSet = new HashSet();
+			if (paramsPKIX.IsUseDeltasEnabled)
+			{
+				CrlDistPoint freshestCRL = null;
+				try
+				{
+					freshestCRL = CrlDistPoint.GetInstance(
+						PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.FreshestCrl));
+				}
+				catch (Exception e)
+				{
+					throw new Exception("Freshest CRL extension could not be decoded from certificate.", e);
+				}
+
+				if (freshestCRL == null)
+				{
+					try
+					{
+						freshestCRL = CrlDistPoint.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(crl, X509Extensions.FreshestCrl));
+					}
+					catch (Exception e)
+					{
+						throw new Exception("Freshest CRL extension could not be decoded from CRL.", e);
+					}
+				}
+				if (freshestCRL != null)
+				{
+					try
+					{
+						PkixCertPathValidatorUtilities.AddAdditionalStoresFromCrlDistributionPoint(freshestCRL, paramsPKIX);
+					}
+					catch (Exception e)
+					{
+						throw new Exception(
+							"No new delta CRL locations could be added from Freshest CRL extension.", e);
+					}
+					// get delta CRL(s)
+					try
+					{
+						deltaSet.AddAll(PkixCertPathValidatorUtilities.GetDeltaCrls(currentDate, paramsPKIX, crl));
+					}
+					catch (Exception e)
+					{
+						throw new Exception("Exception obtaining delta CRLs.", e);
+					}
+				}
+			}
+			return deltaSet;
+		}
+
+		internal static void ProcessCertF(
+			PkixCertPath	certPath,
+			int				index,
+			PkixPolicyNode	validPolicyTree,
+			int				explicitPolicy)
+		{
+			//
+			// (f)
+			//
+			if (explicitPolicy <= 0 && validPolicyTree == null)
+			{
+				throw new PkixCertPathValidatorException(
+					"No valid policy tree found when one expected.", null, certPath, index);
+			}
+		}
+
+		internal static void ProcessCertA(
+			PkixCertPath			certPath,
+			PkixParameters			paramsPKIX,
+			int						index,
+			AsymmetricKeyParameter	workingPublicKey,
+			X509Name				workingIssuerName,
+			X509Certificate			sign)
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+			//
+			// (a) verify
+			//
+			try
+			{
+				// (a) (1)
+				//
+				cert.Verify(workingPublicKey);
+			}
+			catch (GeneralSecurityException e)
+			{
+				throw new PkixCertPathValidatorException("Could not validate certificate signature.", e, certPath, index);
+			}
+
+			try
+			{
+				// (a) (2)
+				//
+				cert.CheckValidity(PkixCertPathValidatorUtilities
+					.GetValidCertDateFromValidityModel(paramsPKIX, certPath, index));
+			}
+			catch (CertificateExpiredException e)
+			{
+				throw new PkixCertPathValidatorException("Could not validate certificate: " + e.Message, e, certPath, index);
+			}
+			catch (CertificateNotYetValidException e)
+			{
+				throw new PkixCertPathValidatorException("Could not validate certificate: " + e.Message, e, certPath, index);
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException("Could not validate time of certificate.", e, certPath, index);
+			}
+
+			//
+			// (a) (3)
+			//
+			if (paramsPKIX.IsRevocationEnabled)
+			{
+				try
+				{
+					CheckCrls(paramsPKIX, cert, PkixCertPathValidatorUtilities.GetValidCertDateFromValidityModel(paramsPKIX,
+						certPath, index), sign, workingPublicKey, certs);
+				}
+				catch (Exception e)
+				{
+					Exception cause = e.InnerException;
+					if (cause == null)
+					{
+						cause = e;
+					}
+					throw new PkixCertPathValidatorException(e.Message, cause, certPath, index);
+				}
+			}
+
+			//
+			// (a) (4) name chaining
+			//
+			X509Name issuer = PkixCertPathValidatorUtilities.GetIssuerPrincipal(cert);
+			if (!issuer.Equivalent(workingIssuerName, true))
+			{
+				throw new PkixCertPathValidatorException("IssuerName(" + issuer
+					+ ") does not match SubjectName(" + workingIssuerName + ") of signing certificate.", null,
+					certPath, index);
+			}
+		}
+
+		internal static int PrepareNextCertI1(
+			PkixCertPath	certPath,
+			int				index,
+			int				explicitPolicy)
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+			//
+			// (i)
+			//
+			Asn1Sequence pc = null;
+			try
+			{
+				pc = DerSequence.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyConstraints));
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException(
+					"Policy constraints extension cannot be decoded.", e, certPath, index);
+			}
+
+			int tmpInt;
+
+			if (pc != null)
+			{
+				IEnumerator policyConstraints = pc.GetEnumerator();
+
+				while (policyConstraints.MoveNext())
+				{
+					try
+					{
+						Asn1TaggedObject constraint = Asn1TaggedObject.GetInstance(policyConstraints.Current);
+						if (constraint.TagNo == 0)
+						{
+							tmpInt = DerInteger.GetInstance(constraint, false).Value.IntValue;
+							if (tmpInt < explicitPolicy)
+							{
+								return tmpInt;
+							}
+							break;
+						}
+					}
+					catch (ArgumentException e)
+					{
+						throw new PkixCertPathValidatorException(
+							"Policy constraints extension contents cannot be decoded.", e, certPath, index);
+					}
+				}
+			}
+			return explicitPolicy;
+		}
+
+		internal static int PrepareNextCertI2(
+			PkixCertPath	certPath,
+			int				index,
+			int				policyMapping)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (i)
+			//
+			Asn1Sequence pc = null;
+			try
+			{
+				pc = DerSequence.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyConstraints));
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException(
+					"Policy constraints extension cannot be decoded.", e, certPath, index);
+			}
+
+			int tmpInt;
+
+			if (pc != null)
+			{
+				IEnumerator policyConstraints = pc.GetEnumerator();
+
+				while (policyConstraints.MoveNext())
+				{
+					try
+					{
+						Asn1TaggedObject constraint = Asn1TaggedObject.GetInstance(policyConstraints.Current);
+						if (constraint.TagNo == 1)
+						{
+							tmpInt = DerInteger.GetInstance(constraint, false).Value.IntValue;
+							if (tmpInt < policyMapping)
+							{
+								return tmpInt;
+							}
+							break;
+						}
+					}
+					catch (ArgumentException e)
+					{
+						throw new PkixCertPathValidatorException(
+							"Policy constraints extension contents cannot be decoded.", e, certPath, index);
+					}
+				}
+			}
+			return policyMapping;
+		}
+
+		internal static void PrepareNextCertG(
+			PkixCertPath				certPath,
+			int							index,
+			PkixNameConstraintValidator	nameConstraintValidator)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (g) handle the name constraints extension
+			//
+			NameConstraints nc = null;
+			try
+			{
+				Asn1Sequence ncSeq = DerSequence.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.NameConstraints));
+				if (ncSeq != null)
+				{
+					nc = new NameConstraints(ncSeq);
+				}
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException(
+					"Name constraints extension could not be decoded.", e, certPath, index);
+			}
+			if (nc != null)
+			{
+				//
+				// (g) (1) permitted subtrees
+				//
+				Asn1Sequence permitted = nc.PermittedSubtrees;
+				if (permitted != null)
+				{
+					try
+					{
+						nameConstraintValidator.IntersectPermittedSubtree(permitted);
+					}
+					catch (Exception ex)
+					{
+						throw new PkixCertPathValidatorException(
+							"Permitted subtrees cannot be build from name constraints extension.", ex, certPath, index);
+					}
+				}
+
+				//
+				// (g) (2) excluded subtrees
+				//
+				Asn1Sequence excluded = nc.ExcludedSubtrees;
+				if (excluded != null)
+				{
+					IEnumerator e = excluded.GetEnumerator();
+					try
+					{
+						while (e.MoveNext())
+						{
+							GeneralSubtree subtree = GeneralSubtree.GetInstance(e.Current);
+							nameConstraintValidator.AddExcludedSubtree(subtree);
+						}
+					}
+					catch (Exception ex)
+					{
+						throw new PkixCertPathValidatorException(
+							"Excluded subtrees cannot be build from name constraints extension.", ex, certPath, index);
+					}
+				}
+			}
+		}
+
+		internal static int PrepareNextCertJ(
+			PkixCertPath	certPath,
+			int				index,
+			int				inhibitAnyPolicy)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (j)
+			//
+			DerInteger iap = null;
+			try
+			{
+				iap = DerInteger.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.InhibitAnyPolicy));
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException(
+					"Inhibit any-policy extension cannot be decoded.", e, certPath, index);
+			}
+
+			if (iap != null)
+			{
+				int _inhibitAnyPolicy = iap.Value.IntValue;
+
+				if (_inhibitAnyPolicy < inhibitAnyPolicy)
+					return _inhibitAnyPolicy;
+			}
+			return inhibitAnyPolicy;
+		}
+
+		internal static void PrepareNextCertK(
+			PkixCertPath	certPath,
+			int				index)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+			//
+			// (k)
+			//
+			BasicConstraints bc = null;
+			try
+			{
+				bc = BasicConstraints.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.BasicConstraints));
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath,
+					index);
+			}
+			if (bc != null)
+			{
+				if (!(bc.IsCA()))
+					throw new PkixCertPathValidatorException("Not a CA certificate");
+			}
+			else
+			{
+				throw new PkixCertPathValidatorException("Intermediate certificate lacks BasicConstraints");
+			}
+		}
+
+		internal static int PrepareNextCertL(
+			PkixCertPath	certPath,
+			int				index,
+			int				maxPathLength)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+			//
+			// (l)
+			//
+			if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert))
+			{
+				if (maxPathLength <= 0)
+				{
+					throw new PkixCertPathValidatorException("Max path length not greater than zero", null, certPath, index);
+				}
+
+				return maxPathLength - 1;
+			}
+			return maxPathLength;
+		}
+
+		internal static int PrepareNextCertM(
+			PkixCertPath	certPath,
+			int				index,
+			int				maxPathLength)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (m)
+			//
+			BasicConstraints bc = null;
+			try
+			{
+				bc = BasicConstraints.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.BasicConstraints));
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath,
+					index);
+			}
+			if (bc != null)
+			{
+				BigInteger _pathLengthConstraint = bc.PathLenConstraint;
+
+				if (_pathLengthConstraint != null)
+				{
+					int _plc = _pathLengthConstraint.IntValue;
+
+					if (_plc < maxPathLength)
+					{
+						return _plc;
+					}
+				}
+			}
+			return maxPathLength;
+		}
+
+		internal static void PrepareNextCertN(
+			PkixCertPath	certPath,
+			int				index)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (n)
+			//
+			bool[] _usage = cert.GetKeyUsage();
+
+			if ((_usage != null) && !_usage[Rfc3280CertPathUtilities.KEY_CERT_SIGN])
+			{
+				throw new PkixCertPathValidatorException(
+					"Issuer certificate keyusage extension is critical and does not permit key signing.", null,
+					certPath, index);
+			}
+		}
+
+		internal static void PrepareNextCertO(
+			PkixCertPath	certPath,
+			int				index,
+			ISet			criticalExtensions,
+			IList			pathCheckers)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (o)
+			//
+			IEnumerator tmpIter = pathCheckers.GetEnumerator();
+			while (tmpIter.MoveNext())
+			{
+				try
+				{
+					((PkixCertPathChecker)tmpIter.Current).Check(cert, criticalExtensions);
+				}
+				catch (PkixCertPathValidatorException e)
+				{
+					throw new PkixCertPathValidatorException(e.Message, e.InnerException, certPath, index);
+				}
+			}
+			if (!criticalExtensions.IsEmpty)
+			{
+				throw new PkixCertPathValidatorException("Certificate has unsupported critical extension.", null, certPath,
+					index);
+			}
+		}
+
+		internal static int PrepareNextCertH1(
+			PkixCertPath	certPath,
+			int				index,
+			int				explicitPolicy)
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (h)
+			//
+			if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert))
+			{
+				//
+				// (1)
+				//
+				if (explicitPolicy != 0)
+					return explicitPolicy - 1;
+			}
+			return explicitPolicy;
+		}
+
+		internal static int PrepareNextCertH2(
+			PkixCertPath	certPath,
+			int				index,
+			int				policyMapping)
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (h)
+			//
+			if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert))
+			{
+				//
+				// (2)
+				//
+				if (policyMapping != 0)
+					return policyMapping - 1;
+			}
+			return policyMapping;
+		}
+
+
+		internal static int PrepareNextCertH3(
+			PkixCertPath	certPath,
+			int				index,
+			int				inhibitAnyPolicy)
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (h)
+			//
+			if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert))
+			{
+				//
+				// (3)
+				//
+				if (inhibitAnyPolicy != 0)
+					return inhibitAnyPolicy - 1;
+			}
+			return inhibitAnyPolicy;
+		}
+
+		internal static int WrapupCertA(
+			int				explicitPolicy,
+			X509Certificate	cert)
+		{
+			//
+			// (a)
+			//
+			if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert) && (explicitPolicy != 0))
+			{
+				explicitPolicy--;
+			}
+			return explicitPolicy;
+		}
+
+		internal static int WrapupCertB(
+			PkixCertPath	certPath,
+			int				index,
+			int				explicitPolicy)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (b)
+			//
+			int tmpInt;
+			Asn1Sequence pc = null;
+			try
+			{
+				pc = DerSequence.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyConstraints));
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException("Policy constraints could not be decoded.", e, certPath, index);
+			}
+
+			if (pc != null)
+			{
+				IEnumerator policyConstraints = pc.GetEnumerator();
+
+				while (policyConstraints.MoveNext())
+				{
+					Asn1TaggedObject constraint = (Asn1TaggedObject)policyConstraints.Current;
+					switch (constraint.TagNo)
+					{
+						case 0:
+							try
+							{
+								tmpInt = DerInteger.GetInstance(constraint, false).Value.IntValue;
+							}
+							catch (Exception e)
+							{
+								throw new PkixCertPathValidatorException(
+									"Policy constraints requireExplicitPolicy field could not be decoded.", e, certPath,
+									index);
+							}
+							if (tmpInt == 0)
+							{
+								return 0;
+							}
+							break;
+					}
+				}
+			}
+			return explicitPolicy;
+		}
+
+		internal static void WrapupCertF(
+			PkixCertPath	certPath,
+			int				index,
+			IList			pathCheckers,
+			ISet			criticalExtensions)
+			//throws CertPathValidatorException
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+			IEnumerator tmpIter = pathCheckers.GetEnumerator();
+
+			while (tmpIter.MoveNext())
+			{
+				try
+				{
+					((PkixCertPathChecker)tmpIter.Current).Check(cert, criticalExtensions);
+				}
+				catch (PkixCertPathValidatorException e)
+				{
+					throw new PkixCertPathValidatorException("Additional certificate path checker failed.", e, certPath,
+						index);
+				}
+			}
+
+			if (!criticalExtensions.IsEmpty)
+			{
+				throw new PkixCertPathValidatorException("Certificate has unsupported critical extension",
+					null, certPath, index);
+			}
+		}
+
+		internal static PkixPolicyNode WrapupCertG(
+			PkixCertPath	certPath,
+			PkixParameters	paramsPKIX,
+			ISet			userInitialPolicySet,
+			int				index,
+			IList[]			policyNodes,
+			PkixPolicyNode	validPolicyTree,
+			ISet			acceptablePolicies)
+		{
+			int n = certPath.Certificates.Count;
+
+			//
+			// (g)
+			//
+			PkixPolicyNode intersection;
+
+			//
+			// (g) (i)
+			//
+			if (validPolicyTree == null)
+			{
+				if (paramsPKIX.IsExplicitPolicyRequired)
+				{
+					throw new PkixCertPathValidatorException(
+						"Explicit policy requested but none available.", null, certPath, index);
+				}
+				intersection = null;
+			}
+			else if (PkixCertPathValidatorUtilities.IsAnyPolicy(userInitialPolicySet)) // (g)
+				// (ii)
+			{
+				if (paramsPKIX.IsExplicitPolicyRequired)
+				{
+					if (acceptablePolicies.IsEmpty)
+					{
+						throw new PkixCertPathValidatorException(
+							"Explicit policy requested but none available.", null, certPath, index);
+					}
+					else
+					{
+						ISet _validPolicyNodeSet = new HashSet();
+
+						for (int j = 0; j < policyNodes.Length; j++)
+						{
+							IList _nodeDepth = policyNodes[j];
+
+							for (int k = 0; k < _nodeDepth.Count; k++)
+							{
+								PkixPolicyNode _node = (PkixPolicyNode)_nodeDepth[k];
+
+								if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(_node.ValidPolicy))
+								{
+									foreach (object o in _node.Children)
+									{
+										_validPolicyNodeSet.Add(o);
+									}
+								}
+							}
+						}
+
+						foreach (PkixPolicyNode _node in _validPolicyNodeSet)
+						{
+							string _validPolicy = _node.ValidPolicy;
+
+							if (!acceptablePolicies.Contains(_validPolicy))
+							{
+								// TODO?
+								// validPolicyTree =
+								// removePolicyNode(validPolicyTree, policyNodes,
+								// _node);
+							}
+						}
+						if (validPolicyTree != null)
+						{
+							for (int j = (n - 1); j >= 0; j--)
+							{
+								IList nodes = policyNodes[j];
+
+								for (int k = 0; k < nodes.Count; k++)
+								{
+									PkixPolicyNode node = (PkixPolicyNode)nodes[k];
+									if (!node.HasChildren)
+									{
+										validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode(validPolicyTree,
+											policyNodes, node);
+									}
+								}
+							}
+						}
+					}
+				}
+
+				intersection = validPolicyTree;
+			}
+			else
+			{
+				//
+				// (g) (iii)
+				//
+				// This implementation is not exactly same as the one described in
+				// RFC3280.
+				// However, as far as the validation result is concerned, both
+				// produce
+				// adequate result. The only difference is whether AnyPolicy is
+				// remain
+				// in the policy tree or not.
+				//
+				// (g) (iii) 1
+				//
+				ISet _validPolicyNodeSet = new HashSet();
+
+				for (int j = 0; j < policyNodes.Length; j++)
+				{
+					IList _nodeDepth = policyNodes[j];
+
+					for (int k = 0; k < _nodeDepth.Count; k++)
+					{
+						PkixPolicyNode _node = (PkixPolicyNode)_nodeDepth[k];
+
+						if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(_node.ValidPolicy))
+						{
+							foreach (PkixPolicyNode _c_node in _node.Children)
+							{
+								if (!Rfc3280CertPathUtilities.ANY_POLICY.Equals(_c_node.ValidPolicy))
+								{
+									_validPolicyNodeSet.Add(_c_node);
+								}
+							}
+						}
+					}
+				}
+
+				//
+				// (g) (iii) 2
+				//
+				IEnumerator _vpnsIter = _validPolicyNodeSet.GetEnumerator();
+				while (_vpnsIter.MoveNext())
+				{
+					PkixPolicyNode _node = (PkixPolicyNode)_vpnsIter.Current;
+					string _validPolicy = _node.ValidPolicy;
+
+					if (!userInitialPolicySet.Contains(_validPolicy))
+					{
+						validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode(validPolicyTree, policyNodes, _node);
+					}
+				}
+
+				//
+				// (g) (iii) 4
+				//
+				if (validPolicyTree != null)
+				{
+					for (int j = (n - 1); j >= 0; j--)
+					{
+						IList nodes = policyNodes[j];
+
+						for (int k = 0; k < nodes.Count; k++)
+						{
+							PkixPolicyNode node = (PkixPolicyNode)nodes[k];
+							if (!node.HasChildren)
+							{
+								validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode(validPolicyTree, policyNodes,
+									node);
+							}
+						}
+					}
+				}
+
+				intersection = validPolicyTree;
+			}
+			return intersection;
+		}
+
+		/**
+		* If use-deltas is set, verify the issuer and scope of the delta CRL.
+		*
+		* @param deltaCRL    The delta CRL.
+		* @param completeCRL The complete CRL.
+		* @param pkixParams  The PKIX paramaters.
+		* @throws AnnotatedException if an exception occurs.
+		*/
+		internal static void ProcessCrlC(
+			X509Crl			deltaCRL,
+			X509Crl			completeCRL,
+			PkixParameters	pkixParams)
+		{
+			if (deltaCRL == null)
+				return;
+
+			IssuingDistributionPoint completeidp = null;
+			try
+			{
+				completeidp = IssuingDistributionPoint.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(completeCRL, X509Extensions.IssuingDistributionPoint));
+			}
+			catch (Exception e)
+			{
+				throw new Exception("000 Issuing distribution point extension could not be decoded.", e);
+			}
+
+			if (pkixParams.IsUseDeltasEnabled)
+			{
+				// (c) (1)
+				if (!deltaCRL.IssuerDN.Equivalent(completeCRL.IssuerDN, true))
+					throw new Exception("Complete CRL issuer does not match delta CRL issuer.");
+
+				// (c) (2)
+				IssuingDistributionPoint deltaidp = null;
+				try
+				{
+					deltaidp = IssuingDistributionPoint.GetInstance(
+						PkixCertPathValidatorUtilities.GetExtensionValue(deltaCRL, X509Extensions.IssuingDistributionPoint));
+				}
+				catch (Exception e)
+				{
+					throw new Exception(
+						"Issuing distribution point extension from delta CRL could not be decoded.", e);
+				}
+
+				if (!Platform.Equals(completeidp, deltaidp))
+				{
+					throw new Exception(
+						"Issuing distribution point extension from delta CRL and complete CRL does not match.");
+				}
+
+				// (c) (3)
+				Asn1Object completeKeyIdentifier = null;
+				try
+				{
+					completeKeyIdentifier = PkixCertPathValidatorUtilities.GetExtensionValue(
+						completeCRL, X509Extensions.AuthorityKeyIdentifier);
+				}
+				catch (Exception e)
+				{
+					throw new Exception(
+						"Authority key identifier extension could not be extracted from complete CRL.", e);
+				}
+
+				Asn1Object deltaKeyIdentifier = null;
+				try
+				{
+					deltaKeyIdentifier = PkixCertPathValidatorUtilities.GetExtensionValue(
+						deltaCRL, X509Extensions.AuthorityKeyIdentifier);
+				}
+				catch (Exception e)
+				{
+					throw new Exception(
+						"Authority key identifier extension could not be extracted from delta CRL.", e);
+				}
+
+				if (completeKeyIdentifier == null)
+					throw new Exception("CRL authority key identifier is null.");
+
+				if (deltaKeyIdentifier == null)
+					throw new Exception("Delta CRL authority key identifier is null.");
+
+				if (!completeKeyIdentifier.Equals(deltaKeyIdentifier))
+				{
+					throw new Exception(
+						"Delta CRL authority key identifier does not match complete CRL authority key identifier.");
+				}
+			}
+		}
+
+		internal static void ProcessCrlI(
+			DateTime		validDate,
+			X509Crl			deltacrl,
+			object			cert,
+			CertStatus		certStatus,
+			PkixParameters	pkixParams)
+		{
+			if (pkixParams.IsUseDeltasEnabled && deltacrl != null)
+			{
+				PkixCertPathValidatorUtilities.GetCertStatus(validDate, deltacrl, cert, certStatus);
+			}
+		}
+
+		internal static void ProcessCrlJ(
+			DateTime	validDate,
+			X509Crl		completecrl,
+			object		cert,
+			CertStatus	certStatus)
+		{
+			if (certStatus.Status == CertStatus.Unrevoked)
+			{
+				PkixCertPathValidatorUtilities.GetCertStatus(validDate, completecrl, cert, certStatus);
+			}
+		}
+
+		internal static PkixPolicyNode ProcessCertE(
+			PkixCertPath	certPath,
+			int				index,
+			PkixPolicyNode	validPolicyTree)
+		{
+			IList certs = certPath.Certificates;
+			X509Certificate cert = (X509Certificate)certs[index];
+
+			//
+			// (e)
+			//
+			Asn1Sequence certPolicies = null;
+			try
+			{
+				certPolicies = DerSequence.GetInstance(
+					PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.CertificatePolicies));
+			}
+			catch (Exception e)
+			{
+				throw new PkixCertPathValidatorException("Could not read certificate policies extension from certificate.",
+					e, certPath, index);
+			}
+			if (certPolicies == null)
+			{
+				validPolicyTree = null;
+			}
+			return validPolicyTree;
+		}
+
+		internal static readonly string[] CrlReasons = new string[]
+		{
+			"unspecified",
+			"keyCompromise",
+			"cACompromise",
+			"affiliationChanged",
+			"superseded",
+			"cessationOfOperation",
+			"certificateHold",
+			"unknown",
+			"removeFromCRL",
+			"privilegeWithdrawn",
+			"aACompromise"
+		};
+	}
+}
diff --git a/crypto/src/pkix/Rfc3281CertPathUtilities.cs b/crypto/src/pkix/Rfc3281CertPathUtilities.cs
new file mode 100644
index 000000000..bda2aa737
--- /dev/null
+++ b/crypto/src/pkix/Rfc3281CertPathUtilities.cs
@@ -0,0 +1,608 @@
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security.Certificates;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Pkix
+{
+	internal class Rfc3281CertPathUtilities
+	{
+		internal static void ProcessAttrCert7(
+			IX509AttributeCertificate	attrCert,
+			PkixCertPath				certPath,
+			PkixCertPath				holderCertPath,
+			PkixParameters				pkixParams)
+		{
+			// TODO:
+			// AA Controls
+			// Attribute encryption
+			// Proxy
+			ISet critExtOids = attrCert.GetCriticalExtensionOids();
+
+			// 7.1
+			// process extensions
+
+			// target information checked in step 6 / X509AttributeCertStoreSelector
+			if (critExtOids.Contains(X509Extensions.TargetInformation.Id))
+			{
+				try
+				{
+					TargetInformation.GetInstance(PkixCertPathValidatorUtilities
+						.GetExtensionValue(attrCert, X509Extensions.TargetInformation));
+				}
+				catch (Exception e)
+				{
+					throw new PkixCertPathValidatorException(
+						"Target information extension could not be read.", e);
+				}
+			}
+			critExtOids.Remove(X509Extensions.TargetInformation.Id);
+			foreach (PkixAttrCertChecker checker in pkixParams.GetAttrCertCheckers())
+			{
+				checker.Check(attrCert, certPath, holderCertPath, critExtOids);
+			}
+			if (!critExtOids.IsEmpty)
+			{
+				throw new PkixCertPathValidatorException(
+					"Attribute certificate contains unsupported critical extensions: "
+						+ critExtOids);
+			}
+		}
+
+		/**
+		* Checks if an attribute certificate is revoked.
+		* 
+		* @param attrCert Attribute certificate to check if it is revoked.
+		* @param paramsPKIX PKIX parameters.
+		* @param issuerCert The issuer certificate of the attribute certificate
+		*            <code>attrCert</code>.
+		* @param validDate The date when the certificate revocation status should
+		*            be checked.
+		* @param certPathCerts The certificates of the certification path to be
+		*            checked.
+		* 
+		* @throws CertPathValidatorException if the certificate is revoked or the
+		*             status cannot be checked or some error occurs.
+		*/
+		internal static void CheckCrls(
+			IX509AttributeCertificate	attrCert,
+			PkixParameters				paramsPKIX,
+			X509Certificate				issuerCert,
+			DateTime					validDate,
+			IList						certPathCerts)
+		{
+			if (paramsPKIX.IsRevocationEnabled)
+			{
+				// check if revocation is available
+				if (attrCert.GetExtensionValue(X509Extensions.NoRevAvail) == null)
+				{
+					CrlDistPoint crldp = null;
+					try
+					{
+						crldp = CrlDistPoint.GetInstance(
+							PkixCertPathValidatorUtilities.GetExtensionValue(
+								attrCert, X509Extensions.CrlDistributionPoints));
+					}
+					catch (Exception e)
+					{
+						throw new PkixCertPathValidatorException(
+							"CRL distribution point extension could not be read.", e);
+					}
+					try
+					{
+						PkixCertPathValidatorUtilities
+							.AddAdditionalStoresFromCrlDistributionPoint(crldp, paramsPKIX);
+					}
+					catch (Exception e)
+					{
+						throw new PkixCertPathValidatorException(
+							"No additional CRL locations could be decoded from CRL distribution point extension.", e);
+					}
+					CertStatus certStatus = new CertStatus();
+					ReasonsMask reasonsMask = new ReasonsMask();
+
+					Exception lastException = null;
+					bool validCrlFound = false;
+					// for each distribution point
+					if (crldp != null)
+					{
+						DistributionPoint[] dps = null;
+						try
+						{
+							dps = crldp.GetDistributionPoints();
+						}
+						catch (Exception e)
+						{
+							throw new PkixCertPathValidatorException(
+								"Distribution points could not be read.", e);
+						}
+						try
+						{
+							for (int i = 0; i < dps.Length
+								&& certStatus.Status == CertStatus.Unrevoked
+								&& !reasonsMask.IsAllReasons; i++)
+							{
+								PkixParameters paramsPKIXClone = (PkixParameters) paramsPKIX
+									.Clone();
+								CheckCrl(dps[i], attrCert, paramsPKIXClone,
+									validDate, issuerCert, certStatus, reasonsMask,
+									certPathCerts);
+								validCrlFound = true;
+							}
+						}
+						catch (Exception e)
+						{
+							lastException = new Exception(
+								"No valid CRL for distribution point found.", e);
+						}
+					}
+
+					/*
+					* If the revocation status has not been determined, repeat the
+					* process above with any available CRLs not specified in a
+					* distribution point but issued by the certificate issuer.
+					*/
+
+					if (certStatus.Status == CertStatus.Unrevoked
+						&& !reasonsMask.IsAllReasons)
+					{
+						try
+						{
+							/*
+							* assume a DP with both the reasons and the cRLIssuer
+							* fields omitted and a distribution point name of the
+							* certificate issuer.
+							*/
+							Asn1Object issuer = null;
+							try
+							{
+								issuer = new Asn1InputStream(
+									attrCert.Issuer.GetPrincipals()[0].GetEncoded()).ReadObject();
+							}
+							catch (Exception e)
+							{
+								throw new Exception(
+									"Issuer from certificate for CRL could not be reencoded.",
+									e);
+							}
+							DistributionPoint dp = new DistributionPoint(
+								new DistributionPointName(0, new GeneralNames(
+									new GeneralName(GeneralName.DirectoryName, issuer))), null, null);
+							PkixParameters paramsPKIXClone = (PkixParameters) paramsPKIX.Clone();
+							CheckCrl(dp, attrCert, paramsPKIXClone, validDate,
+								issuerCert, certStatus, reasonsMask, certPathCerts);
+							validCrlFound = true;
+						}
+						catch (Exception e)
+						{
+							lastException = new Exception(
+								"No valid CRL for distribution point found.", e);
+						}
+					}
+
+					if (!validCrlFound)
+					{
+						throw new PkixCertPathValidatorException(
+							"No valid CRL found.", lastException);
+					}
+					if (certStatus.Status != CertStatus.Unrevoked)
+					{
+						// TODO This format is forced by the NistCertPath tests
+						string formattedDate = certStatus.RevocationDate.Value.ToString(
+                            "G", new CultureInfo("en-us"));
+						string message = "Attribute certificate revocation after "
+							+ formattedDate;
+						message += ", reason: "
+							+ Rfc3280CertPathUtilities.CrlReasons[certStatus.Status];
+						throw new PkixCertPathValidatorException(message);
+					}
+					if (!reasonsMask.IsAllReasons
+						&& certStatus.Status == CertStatus.Unrevoked)
+					{
+						certStatus.Status = CertStatus.Undetermined;
+					}
+					if (certStatus.Status == CertStatus.Undetermined)
+					{
+						throw new PkixCertPathValidatorException(
+							"Attribute certificate status could not be determined.");
+					}
+
+				}
+				else
+				{
+					if (attrCert.GetExtensionValue(X509Extensions.CrlDistributionPoints) != null
+						|| attrCert.GetExtensionValue(X509Extensions.AuthorityInfoAccess) != null)
+					{
+						throw new PkixCertPathValidatorException(
+							"No rev avail extension is set, but also an AC revocation pointer.");
+					}
+				}
+			}
+		}
+
+		internal static void AdditionalChecks(
+			IX509AttributeCertificate	attrCert,
+			PkixParameters				pkixParams)
+		{
+			// 1
+			foreach (string oid in pkixParams.GetProhibitedACAttributes())
+			{
+				if (attrCert.GetAttributes(oid) != null)
+				{
+					throw new PkixCertPathValidatorException(
+						"Attribute certificate contains prohibited attribute: "
+							+ oid + ".");
+				}
+			}
+			foreach (string oid in pkixParams.GetNecessaryACAttributes())
+			{
+				if (attrCert.GetAttributes(oid) == null)
+				{
+					throw new PkixCertPathValidatorException(
+						"Attribute certificate does not contain necessary attribute: "
+							+ oid + ".");
+				}
+			}
+		}
+
+		internal static void ProcessAttrCert5(
+			IX509AttributeCertificate	attrCert,
+			PkixParameters				pkixParams)
+		{
+			try
+			{
+				attrCert.CheckValidity(PkixCertPathValidatorUtilities.GetValidDate(pkixParams));
+			}
+			catch (CertificateExpiredException e)
+			{
+				throw new PkixCertPathValidatorException(
+					"Attribute certificate is not valid.", e);
+			}
+			catch (CertificateNotYetValidException e)
+			{
+				throw new PkixCertPathValidatorException(
+					"Attribute certificate is not valid.", e);
+			}
+		}
+
+		internal static void ProcessAttrCert4(
+			X509Certificate	acIssuerCert,
+			PkixParameters	pkixParams)
+		{
+			ISet set = pkixParams.GetTrustedACIssuers();
+			bool trusted = false;
+			foreach (TrustAnchor anchor in set)
+			{
+                IDictionary symbols = X509Name.RFC2253Symbols;
+                if (acIssuerCert.SubjectDN.ToString(false, symbols).Equals(anchor.CAName)
+					|| acIssuerCert.Equals(anchor.TrustedCert))
+				{
+					trusted = true;
+				}
+			}
+			if (!trusted)
+			{
+				throw new PkixCertPathValidatorException(
+					"Attribute certificate issuer is not directly trusted.");
+			}
+		}
+
+		internal static void ProcessAttrCert3(
+			X509Certificate	acIssuerCert,
+			PkixParameters	pkixParams)
+		{
+			if (acIssuerCert.GetKeyUsage() != null
+				&& (!acIssuerCert.GetKeyUsage()[0] && !acIssuerCert.GetKeyUsage()[1]))
+			{
+				throw new PkixCertPathValidatorException(
+					"Attribute certificate issuer public key cannot be used to validate digital signatures.");
+			}
+			if (acIssuerCert.GetBasicConstraints() != -1)
+			{
+				throw new PkixCertPathValidatorException(
+					"Attribute certificate issuer is also a public key certificate issuer.");
+			}
+		}
+
+		internal static PkixCertPathValidatorResult ProcessAttrCert2(
+			PkixCertPath	certPath,
+			PkixParameters	pkixParams)
+		{
+			PkixCertPathValidator validator = new PkixCertPathValidator();
+
+			try
+			{
+				return validator.Validate(certPath, pkixParams);
+			}
+			catch (PkixCertPathValidatorException e)
+			{
+				throw new PkixCertPathValidatorException(
+					"Certification path for issuer certificate of attribute certificate could not be validated.",
+					e);
+			}
+		}
+
+		/**
+		* Searches for a holder public key certificate and verifies its
+		* certification path.
+		* 
+		* @param attrCert the attribute certificate.
+		* @param pkixParams The PKIX parameters.
+		* @return The certificate path of the holder certificate.
+		* @throws Exception if
+		*             <ul>
+		*             <li>no public key certificate can be found although holder
+		*             information is given by an entity name or a base certificate
+		*             ID</li>
+		*             <li>support classes cannot be created</li>
+		*             <li>no certification path for the public key certificate can
+		*             be built</li>
+		*             </ul>
+		*/
+		internal static PkixCertPath ProcessAttrCert1(
+			IX509AttributeCertificate	attrCert,
+			PkixParameters				pkixParams)
+		{
+			PkixCertPathBuilderResult result = null;
+			// find holder PKCs
+			ISet holderPKCs = new HashSet();
+			if (attrCert.Holder.GetIssuer() != null)
+			{
+				X509CertStoreSelector selector = new X509CertStoreSelector();
+				selector.SerialNumber = attrCert.Holder.SerialNumber;
+				X509Name[] principals = attrCert.Holder.GetIssuer();
+				for (int i = 0; i < principals.Length; i++)
+				{
+					try
+					{
+//						if (principals[i] is X500Principal)
+						{
+							selector.Issuer = principals[i];
+						}
+						holderPKCs.AddAll(PkixCertPathValidatorUtilities
+							.FindCertificates(selector, pkixParams.GetStores()));
+					}
+					catch (Exception e)
+					{
+						throw new PkixCertPathValidatorException(
+							"Public key certificate for attribute certificate cannot be searched.",
+							e);
+					}
+				}
+				if (holderPKCs.IsEmpty)
+				{
+					throw new PkixCertPathValidatorException(
+						"Public key certificate specified in base certificate ID for attribute certificate cannot be found.");
+				}
+			}
+			if (attrCert.Holder.GetEntityNames() != null)
+			{
+				X509CertStoreSelector selector = new X509CertStoreSelector();
+				X509Name[] principals = attrCert.Holder.GetEntityNames();
+				for (int i = 0; i < principals.Length; i++)
+				{
+					try
+					{
+//						if (principals[i] is X500Principal)
+						{
+							selector.Issuer = principals[i];
+						}
+						holderPKCs.AddAll(PkixCertPathValidatorUtilities
+							.FindCertificates(selector, pkixParams.GetStores()));
+					}
+					catch (Exception e)
+					{
+						throw new PkixCertPathValidatorException(
+							"Public key certificate for attribute certificate cannot be searched.",
+							e);
+					}
+				}
+				if (holderPKCs.IsEmpty)
+				{
+					throw new PkixCertPathValidatorException(
+						"Public key certificate specified in entity name for attribute certificate cannot be found.");
+				}
+			}
+
+			// verify cert paths for PKCs
+			PkixBuilderParameters parameters = (PkixBuilderParameters)
+				PkixBuilderParameters.GetInstance(pkixParams);
+
+			PkixCertPathValidatorException lastException = null;
+			foreach (X509Certificate cert in holderPKCs)
+			{
+				X509CertStoreSelector selector = new X509CertStoreSelector();
+				selector.Certificate = cert;
+				parameters.SetTargetConstraints(selector);
+
+				PkixCertPathBuilder builder = new PkixCertPathBuilder();
+
+				try
+				{
+					result = builder.Build(PkixBuilderParameters.GetInstance(parameters));
+				}
+				catch (PkixCertPathBuilderException e)
+				{
+					lastException = new PkixCertPathValidatorException(
+						"Certification path for public key certificate of attribute certificate could not be build.",
+						e);
+				}
+			}
+			if (lastException != null)
+			{
+				throw lastException;
+			}
+			return result.CertPath;
+		}
+
+		/**
+		* 
+		* Checks a distribution point for revocation information for the
+		* certificate <code>attrCert</code>.
+		* 
+		* @param dp The distribution point to consider.
+		* @param attrCert The attribute certificate which should be checked.
+		* @param paramsPKIX PKIX parameters.
+		* @param validDate The date when the certificate revocation status should
+		*            be checked.
+		* @param issuerCert Certificate to check if it is revoked.
+		* @param reasonMask The reasons mask which is already checked.
+		* @param certPathCerts The certificates of the certification path to be
+		*            checked.
+		* @throws Exception if the certificate is revoked or the status
+		*             cannot be checked or some error occurs.
+		*/
+		private static void CheckCrl(
+			DistributionPoint			dp,
+			IX509AttributeCertificate	attrCert,
+			PkixParameters				paramsPKIX,
+			DateTime					validDate,
+			X509Certificate				issuerCert,
+			CertStatus					certStatus,
+			ReasonsMask					reasonMask,
+			IList						certPathCerts)
+		{
+			/*
+			* 4.3.6 No Revocation Available
+			* 
+			* The noRevAvail extension, defined in [X.509-2000], allows an AC
+			* issuer to indicate that no revocation information will be made
+			* available for this AC.
+			*/
+			if (attrCert.GetExtensionValue(X509Extensions.NoRevAvail) != null)
+			{
+				return;
+			}
+
+			DateTime currentDate = DateTime.UtcNow;
+			if (validDate.CompareTo(currentDate) > 0)
+			{
+				throw new Exception("Validation time is in future.");
+			}
+
+			// (a)
+			/*
+			* We always get timely valid CRLs, so there is no step (a) (1).
+			* "locally cached" CRLs are assumed to be in getStore(), additional
+			* CRLs must be enabled in the ExtendedPkixParameters and are in
+			* getAdditionalStore()
+			*/
+			ISet crls = PkixCertPathValidatorUtilities.GetCompleteCrls(dp, attrCert,
+				currentDate, paramsPKIX);
+			bool validCrlFound = false;
+			Exception lastException = null;
+
+			IEnumerator crl_iter = crls.GetEnumerator();
+
+			while (crl_iter.MoveNext()
+				&& certStatus.Status == CertStatus.Unrevoked
+				&& !reasonMask.IsAllReasons)
+			{
+				try
+				{
+					X509Crl crl = (X509Crl) crl_iter.Current;
+
+					// (d)
+					ReasonsMask interimReasonsMask = Rfc3280CertPathUtilities.ProcessCrlD(crl, dp);
+
+					// (e)
+					/*
+					* The reasons mask is updated at the end, so only valid CRLs
+					* can update it. If this CRL does not contain new reasons it
+					* must be ignored.
+					*/
+					if (!interimReasonsMask.HasNewReasons(reasonMask))
+					{
+						continue;
+					}
+
+					// (f)
+					ISet keys = Rfc3280CertPathUtilities.ProcessCrlF(crl, attrCert,
+						null, null, paramsPKIX, certPathCerts);
+					// (g)
+					AsymmetricKeyParameter pubKey = Rfc3280CertPathUtilities.ProcessCrlG(crl, keys);
+
+					X509Crl deltaCRL = null;
+
+					if (paramsPKIX.IsUseDeltasEnabled)
+					{
+						// get delta CRLs
+						ISet deltaCRLs = PkixCertPathValidatorUtilities.GetDeltaCrls(
+							currentDate, paramsPKIX, crl);
+						// we only want one valid delta CRL
+						// (h)
+						deltaCRL = Rfc3280CertPathUtilities.ProcessCrlH(deltaCRLs, pubKey);
+					}
+
+					/*
+					* CRL must be be valid at the current time, not the validation
+					* time. If a certificate is revoked with reason keyCompromise,
+					* cACompromise, it can be used for forgery, also for the past.
+					* This reason may not be contained in older CRLs.
+					*/
+
+					/*
+					* in the chain model signatures stay valid also after the
+					* certificate has been expired, so they do not have to be in
+					* the CRL vality time
+					*/
+					if (paramsPKIX.ValidityModel != PkixParameters.ChainValidityModel)
+					{
+						/*
+						* if a certificate has expired, but was revoked, it is not
+						* more in the CRL, so it would be regarded as valid if the
+						* first check is not done
+						*/
+						if (attrCert.NotAfter.CompareTo(crl.ThisUpdate) < 0)
+						{
+							throw new Exception(
+								"No valid CRL for current time found.");
+						}
+					}
+
+					Rfc3280CertPathUtilities.ProcessCrlB1(dp, attrCert, crl);
+
+					// (b) (2)
+					Rfc3280CertPathUtilities.ProcessCrlB2(dp, attrCert, crl);
+
+					// (c)
+					Rfc3280CertPathUtilities.ProcessCrlC(deltaCRL, crl, paramsPKIX);
+
+					// (i)
+					Rfc3280CertPathUtilities.ProcessCrlI(validDate, deltaCRL,
+						attrCert, certStatus, paramsPKIX);
+
+					// (j)
+					Rfc3280CertPathUtilities.ProcessCrlJ(validDate, crl, attrCert,
+						certStatus);
+
+					// (k)
+					if (certStatus.Status == CrlReason.RemoveFromCrl)
+					{
+						certStatus.Status = CertStatus.Unrevoked;
+					}
+
+					// update reasons mask
+					reasonMask.AddReasons(interimReasonsMask);
+					validCrlFound = true;
+				}
+				catch (Exception e)
+				{
+					lastException = e;
+				}
+			}
+			if (!validCrlFound)
+			{
+				throw lastException;
+			}
+		}
+	}
+}
diff --git a/crypto/src/pkix/TrustAnchor.cs b/crypto/src/pkix/TrustAnchor.cs
new file mode 100644
index 000000000..22078baf2
--- /dev/null
+++ b/crypto/src/pkix/TrustAnchor.cs
@@ -0,0 +1,259 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Pkix
+{
+	/// <summary>
+	/// A trust anchor or most-trusted Certification Authority (CA).
+	/// 
+	/// This class represents a "most-trusted CA", which is used as a trust anchor
+	/// for validating X.509 certification paths. A most-trusted CA includes the
+	/// public key of the CA, the CA's name, and any constraints upon the set of
+	/// paths which may be validated using this key. These parameters can be
+	/// specified in the form of a trusted X509Certificate or as individual
+	/// parameters.
+	/// </summary>
+	public class TrustAnchor
+	{
+		private readonly AsymmetricKeyParameter pubKey;
+		private readonly string caName;
+		private readonly X509Name caPrincipal;
+		private readonly X509Certificate trustedCert;
+		private byte[] ncBytes;
+		private NameConstraints nc;
+
+		/// <summary>
+		/// Creates an instance of TrustAnchor with the specified X509Certificate and
+	    /// optional name constraints, which are intended to be used as additional
+	    /// constraints when validating an X.509 certification path.
+	    ///	The name constraints are specified as a byte array. This byte array
+	    ///	should contain the DER encoded form of the name constraints, as they
+	    ///	would appear in the NameConstraints structure defined in RFC 2459 and
+	    ///	X.509. The ASN.1 definition of this structure appears below.
+	    ///	
+	    ///	<pre>
+	    ///	NameConstraints ::= SEQUENCE {
+	    ///		permittedSubtrees       [0]     GeneralSubtrees OPTIONAL,
+	    ///		excludedSubtrees        [1]     GeneralSubtrees OPTIONAL }
+	    ///	   
+        /// GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
+        /// 
+        ///		GeneralSubtree ::= SEQUENCE {
+        ///		base                    GeneralName,
+        ///		minimum         [0]     BaseDistance DEFAULT 0,
+        ///		maximum         [1]     BaseDistance OPTIONAL }
+        ///		
+        ///		BaseDistance ::= INTEGER (0..MAX)
+		///
+		///		GeneralName ::= CHOICE {
+		///		otherName                       [0]     OtherName,
+		///		rfc822Name                      [1]     IA5String,
+		///		dNSName                         [2]     IA5String,
+		///		x400Address                     [3]     ORAddress,
+		///		directoryName                   [4]     Name,
+		///		ediPartyName                    [5]     EDIPartyName,
+		///		uniformResourceIdentifier       [6]     IA5String,
+		///		iPAddress                       [7]     OCTET STRING,
+		///		registeredID                    [8]     OBJECT IDENTIFIER}
+		///	</pre>
+		///	
+		///	Note that the name constraints byte array supplied is cloned to protect
+		///	against subsequent modifications.
+		/// </summary>
+		/// <param name="trustedCert">a trusted X509Certificate</param>
+		/// <param name="nameConstraints">a byte array containing the ASN.1 DER encoding of a
+		/// NameConstraints extension to be used for checking name
+		/// constraints. Only the value of the extension is included, not
+		/// the OID or criticality flag. Specify null to omit the
+		/// parameter.</param>
+		/// <exception cref="ArgumentNullException">if the specified X509Certificate is null</exception>
+		public TrustAnchor(
+			X509Certificate	trustedCert,
+			byte[]			nameConstraints)
+		{
+			if (trustedCert == null)
+				throw new ArgumentNullException("trustedCert");
+
+			this.trustedCert = trustedCert;
+			this.pubKey = null;
+			this.caName = null;
+			this.caPrincipal = null;
+			setNameConstraints(nameConstraints);
+		}
+
+		/// <summary>
+		/// Creates an instance of <c>TrustAnchor</c> where the
+		/// most-trusted CA is specified as an X500Principal and public key.
+		/// </summary>
+		/// <remarks>
+		/// <p>
+		/// Name constraints are an optional parameter, and are intended to be used
+		/// as additional constraints when validating an X.509 certification path.
+		/// </p><p>
+		/// The name constraints are specified as a byte array. This byte array
+		/// contains the DER encoded form of the name constraints, as they
+		/// would appear in the NameConstraints structure defined in RFC 2459
+		/// and X.509. The ASN.1 notation for this structure is supplied in the
+		/// documentation for the other constructors.
+		/// </p><p>
+		/// Note that the name constraints byte array supplied here is cloned to
+		/// protect against subsequent modifications.
+		/// </p>
+		/// </remarks>
+		/// <param name="caPrincipal">the name of the most-trusted CA as X509Name</param>
+		/// <param name="pubKey">the public key of the most-trusted CA</param>
+		/// <param name="nameConstraints">
+		/// a byte array containing the ASN.1 DER encoding of a NameConstraints extension to
+		/// be used for checking name constraints. Only the value of the extension is included,
+		/// not the OID or criticality flag. Specify <c>null</c> to omit the parameter.
+		/// </param>
+		/// <exception cref="ArgumentNullException">
+		/// if <c>caPrincipal</c> or <c>pubKey</c> is null
+		/// </exception>
+		public TrustAnchor(
+			X509Name				caPrincipal,
+			AsymmetricKeyParameter	pubKey,
+			byte[]					nameConstraints) 
+		{
+			if (caPrincipal == null)
+				throw new ArgumentNullException("caPrincipal");
+			if (pubKey == null)
+				throw new ArgumentNullException("pubKey");
+
+			this.trustedCert = null;
+			this.caPrincipal = caPrincipal;
+			this.caName = caPrincipal.ToString();
+			this.pubKey = pubKey;
+			setNameConstraints(nameConstraints);
+		}
+
+		/// <summary>
+		/// Creates an instance of <code>TrustAnchor</code> where the most-trusted
+		/// CA is specified as a distinguished name and public key. Name constraints
+		/// are an optional parameter, and are intended to be used as additional
+		/// constraints when validating an X.509 certification path.
+		/// <br/>
+		/// The name constraints are specified as a byte array. This byte array
+		/// contains the DER encoded form of the name constraints, as they would
+		/// appear in the NameConstraints structure defined in RFC 2459 and X.509.
+		/// </summary>
+		/// <param name="caName">the X.500 distinguished name of the most-trusted CA in RFC
+		/// 2253 string format</param>
+		/// <param name="pubKey">the public key of the most-trusted CA</param>
+		/// <param name="nameConstraints">a byte array containing the ASN.1 DER encoding of a
+		/// NameConstraints extension to be used for checking name
+		/// constraints. Only the value of the extension is included, not 
+		/// the OID or criticality flag. Specify null to omit the 
+		/// parameter.</param>
+		/// throws NullPointerException, IllegalArgumentException
+		public TrustAnchor(
+			string					caName,
+			AsymmetricKeyParameter	pubKey,
+			byte[]					nameConstraints)
+		{
+			if (caName == null)
+				throw new ArgumentNullException("caName");
+			if (pubKey == null)
+				throw new ArgumentNullException("pubKey");
+			if (caName.Length == 0)
+				throw new ArgumentException("caName can not be an empty string");
+
+			this.caPrincipal = new X509Name(caName);
+			this.pubKey = pubKey;
+			this.caName = caName;
+			this.trustedCert = null;
+			setNameConstraints(nameConstraints);
+		}
+
+		/// <summary>
+		/// Returns the most-trusted CA certificate.
+		/// </summary>
+		public X509Certificate TrustedCert
+		{
+			get { return this.trustedCert; }
+		}
+
+		/// <summary>
+		/// Returns the name of the most-trusted CA as an X509Name.
+		/// </summary>
+		public X509Name CA
+		{
+			get { return this.caPrincipal; }
+		}
+
+		/// <summary>
+		/// Returns the name of the most-trusted CA in RFC 2253 string format.
+		/// </summary>
+		public string CAName
+		{
+			get { return this.caName; }
+		}
+
+		/// <summary>
+		/// Returns the public key of the most-trusted CA.
+		/// </summary>
+		public AsymmetricKeyParameter CAPublicKey
+		{
+			get { return this.pubKey; }
+		}
+
+		/// <summary>
+		/// Decode the name constraints and clone them if not null.
+		/// </summary>
+		private void setNameConstraints(
+			byte[] bytes) 
+		{
+			if (bytes == null) 
+			{
+				ncBytes = null;
+				nc = null;
+			} 
+			else 
+			{
+				ncBytes = (byte[]) bytes.Clone();
+				// validate DER encoding
+				//nc = new NameConstraintsExtension(Boolean.FALSE, bytes);
+				nc = NameConstraints.GetInstance(Asn1Object.FromByteArray(bytes));
+			}
+		}
+
+		public byte[] GetNameConstraints
+		{
+			get { return Arrays.Clone(ncBytes); }
+		}
+
+		/// <summary>
+		/// Returns a formatted string describing the <code>TrustAnchor</code>.
+		/// </summary>
+		/// <returns>a formatted string describing the <code>TrustAnchor</code></returns>
+		public override string ToString()
+		{
+			// TODO Some of the sub-objects might not implement ToString() properly
+			string nl = Platform.NewLine;
+			StringBuilder sb = new StringBuilder();
+			sb.Append("[");
+			sb.Append(nl);
+			if (this.pubKey != null)
+			{
+				sb.Append("  Trusted CA Public Key: ").Append(this.pubKey).Append(nl);
+				sb.Append("  Trusted CA Issuer Name: ").Append(this.caName).Append(nl);
+			}
+			else
+			{
+				sb.Append("  Trusted CA cert: ").Append(this.TrustedCert).Append(nl);
+			}
+			if (nc != null)
+			{
+				sb.Append("  Name Constraints: ").Append(nc).Append(nl);
+			}
+			return sb.ToString();
+		}
+	}
+}
\ No newline at end of file