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.cs2
-rw-r--r--crypto/src/pkix/PkixCertPathBuilder.cs205
-rw-r--r--crypto/src/pkix/PkixCertPathBuilderException.cs5
-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.cs7
-rw-r--r--crypto/src/pkix/PkixCertPathValidatorResult.cs69
-rw-r--r--crypto/src/pkix/PkixCertPathValidatorUtilities.cs10
-rw-r--r--crypto/src/pkix/PkixCrlUtilities.cs114
-rw-r--r--crypto/src/pkix/PkixNameConstraintValidator.cs1937
-rw-r--r--crypto/src/pkix/PkixNameConstraintValidatorException.cs6
-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.cs8
-rw-r--r--crypto/src/pkix/Rfc3281CertPathUtilities.cs8
-rw-r--r--crypto/src/pkix/TrustAnchor.cs259
23 files changed, 4848 insertions, 18 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
index e13e50995..e3d3ea7fe 100644
--- a/crypto/src/pkix/PkixCertPath.cs
+++ b/crypto/src/pkix/PkixCertPath.cs
@@ -401,7 +401,7 @@ namespace Org.BouncyCastle.Pkix
 						pWrt.WriteObject(certificates[i]);
 					}
 
-                    pWrt.Writer.Dispose();
+					pWrt.Writer.Close();
 				}
 				catch (Exception)
 				{
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
index 106075a63..5a4944dd8 100644
--- a/crypto/src/pkix/PkixCertPathBuilderException.cs
+++ b/crypto/src/pkix/PkixCertPathBuilderException.cs
@@ -7,7 +7,10 @@ namespace Org.BouncyCastle.Pkix
 	/// <summary>
 	/// Summary description for PkixCertPathBuilderException.
 	/// </summary>
-	public class PkixCertPathBuilderException : GeneralSecurityException
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+    [Serializable]
+#endif
+    public class PkixCertPathBuilderException : GeneralSecurityException
 	{
 		public PkixCertPathBuilderException() : base() { }
 		
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
index 504a3c646..35522c6f8 100644
--- a/crypto/src/pkix/PkixCertPathValidatorException.cs
+++ b/crypto/src/pkix/PkixCertPathValidatorException.cs
@@ -27,8 +27,11 @@ namespace Org.BouncyCastle.Pkix
 	 *
 	 * @see CertPathValidator
 	 **/
-
-	public class PkixCertPathValidatorException : GeneralSecurityException
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+    [Serializable]
+#endif
+    public class PkixCertPathValidatorException
+        : GeneralSecurityException
 	{
 		private Exception cause;
 		private PkixCertPath certPath;
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
index 305b2de35..acea77856 100644
--- a/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
+++ b/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
@@ -528,7 +528,7 @@ namespace Org.BouncyCastle.Pkix
 					}
 					catch (Exception e)
 					{
-						new Exception(
+						throw new Exception(
 							"Reason code CRL entry extension could not be decoded.",
 							e);
 					}
@@ -683,8 +683,8 @@ namespace Org.BouncyCastle.Pkix
 		/// 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.
+		/// <returns>a Collection of all found <see cref="X509Certificate"/> or
+		/// <see cref="Org.BouncyCastle.X509.IX509AttributeCertificate"/> objects.
 		/// May be empty but never <code>null</code>.</returns>
 		/// <exception cref="Exception"></exception>
 		internal static ICollection FindCertificates(
@@ -864,7 +864,7 @@ namespace Org.BouncyCastle.Pkix
 			}
 			catch (Exception e)
 			{
-				new Exception("Could not get issuer information from distribution point.", e);
+				throw new Exception("Could not get issuer information from distribution point.", e);
 			}
 
 			if (cert is X509Certificate)
@@ -924,7 +924,7 @@ namespace Org.BouncyCastle.Pkix
 			}
 			catch (IOException e)
 			{
-				new Exception("Cannot extract issuer from CRL.", e);
+				throw new Exception("Cannot extract issuer from CRL.", e);
 			}
 
 			BigInteger completeCRLNumber = null;
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
index 9f01e8765..432d7bd6b 100644
--- a/crypto/src/pkix/PkixNameConstraintValidatorException.cs
+++ b/crypto/src/pkix/PkixNameConstraintValidatorException.cs
@@ -2,7 +2,11 @@ using System;
 
 namespace Org.BouncyCastle.Pkix
 {
-    public class PkixNameConstraintValidatorException : Exception
+#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
index bae657d90..c6f3fbff9 100644
--- a/crypto/src/pkix/Rfc3280CertPathUtilities.cs
+++ b/crypto/src/pkix/Rfc3280CertPathUtilities.cs
@@ -1197,10 +1197,10 @@ namespace Org.BouncyCastle.Pkix
 			}
 			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;
+				// This format is enforced by the NistCertPath tests
+                string formattedDate = certStatus.RevocationDate.Value.ToString(
+                    "ddd MMM dd HH:mm:ss K yyyy");
+                string message = "Certificate revocation after " + formattedDate;
 				message += ", reason: " + CrlReasons[certStatus.Status];
 				throw new Exception(message);
 			}
diff --git a/crypto/src/pkix/Rfc3281CertPathUtilities.cs b/crypto/src/pkix/Rfc3281CertPathUtilities.cs
index bda2aa737..101ef5e11 100644
--- a/crypto/src/pkix/Rfc3281CertPathUtilities.cs
+++ b/crypto/src/pkix/Rfc3281CertPathUtilities.cs
@@ -195,10 +195,10 @@ namespace Org.BouncyCastle.Pkix
 					}
 					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 "
+                        // This format is enforced by the NistCertPath tests
+                        string formattedDate = certStatus.RevocationDate.Value.ToString(
+                            "ddd MMM dd HH:mm:ss K yyyy");
+                        string message = "Attribute certificate revocation after "
 							+ formattedDate;
 						message += ", reason: "
 							+ Rfc3280CertPathUtilities.CrlReasons[certStatus.Status];
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