summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2019-06-01 19:11:34 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2019-06-01 19:11:34 +0700
commit5cc0a46f4dbaafd9e3104643ec1fa5bf69ac5a6c (patch)
tree0ac8a1bd4557e3682c2fb2cbf889bd992402b1db
parentRemove extraneous output (diff)
downloadBouncyCastle.NET-ed25519-5cc0a46f4dbaafd9e3104643ec1fa5bf69ac5a6c.tar.xz
Name constraint validation updates from bc-java
-rw-r--r--crypto/BouncyCastle.Android.csproj3
-rw-r--r--crypto/BouncyCastle.csproj3
-rw-r--r--crypto/BouncyCastle.iOS.csproj3
-rw-r--r--crypto/crypto.csproj20
-rw-r--r--crypto/src/asn1/x500/AttributeTypeAndValue.cs60
-rw-r--r--crypto/src/asn1/x500/Rdn.cs104
-rw-r--r--crypto/src/asn1/x500/style/IetfUtilities.cs214
-rw-r--r--crypto/src/pkix/PkixNameConstraintValidator.cs206
-rw-r--r--crypto/src/pkix/Rfc3280CertPathUtilities.cs24
-rw-r--r--crypto/test/UnitTests.csproj1
-rw-r--r--crypto/test/src/test/PkixNameConstraintsTest.cs34
-rw-r--r--crypto/test/src/test/nist/NistCertPathTest.cs139
-rw-r--r--crypto/test/src/test/nist/NistCertPathTest2.cs499
13 files changed, 1104 insertions, 206 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index 96564a984..a356bce2c 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -394,7 +394,10 @@
     <Compile Include="src\asn1\util\Asn1Dump.cs" />
     <Compile Include="src\asn1\util\Dump.cs" />
     <Compile Include="src\asn1\util\FilterStream.cs" />
+    <Compile Include="src\asn1\x500\AttributeTypeAndValue.cs" />
     <Compile Include="src\asn1\x500\DirectoryString.cs" />
+    <Compile Include="src\asn1\x500\Rdn.cs" />
+    <Compile Include="src\asn1\x500\style\IetfUtilities.cs" />
     <Compile Include="src\asn1\x509\AccessDescription.cs" />
     <Compile Include="src\asn1\x509\AlgorithmIdentifier.cs" />
     <Compile Include="src\asn1\x509\AttCertIssuer.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index 8b7ae06e9..c79463f7c 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -388,7 +388,10 @@
     <Compile Include="src\asn1\util\Asn1Dump.cs" />
     <Compile Include="src\asn1\util\Dump.cs" />
     <Compile Include="src\asn1\util\FilterStream.cs" />
+    <Compile Include="src\asn1\x500\AttributeTypeAndValue.cs" />
     <Compile Include="src\asn1\x500\DirectoryString.cs" />
+    <Compile Include="src\asn1\x500\Rdn.cs" />
+    <Compile Include="src\asn1\x500\style\IetfUtilities.cs" />
     <Compile Include="src\asn1\x509\AccessDescription.cs" />
     <Compile Include="src\asn1\x509\AlgorithmIdentifier.cs" />
     <Compile Include="src\asn1\x509\AttCertIssuer.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index 03bcf2835..630df8a1f 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -389,7 +389,10 @@
     <Compile Include="src\asn1\util\Asn1Dump.cs" />
     <Compile Include="src\asn1\util\Dump.cs" />
     <Compile Include="src\asn1\util\FilterStream.cs" />
+    <Compile Include="src\asn1\x500\AttributeTypeAndValue.cs" />
     <Compile Include="src\asn1\x500\DirectoryString.cs" />
+    <Compile Include="src\asn1\x500\Rdn.cs" />
+    <Compile Include="src\asn1\x500\style\IetfUtilities.cs" />
     <Compile Include="src\asn1\x509\AccessDescription.cs" />
     <Compile Include="src\asn1\x509\AlgorithmIdentifier.cs" />
     <Compile Include="src\asn1\x509\AttCertIssuer.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 9a51f5bd3..38f4544d8 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -1829,11 +1829,26 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\x500\AttributeTypeAndValue.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\x500\DirectoryString.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\x500\Rdn.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\asn1\x500\style\IetfUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\x509\AccessDescription.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -13488,6 +13503,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\test\nist\NistCertPathTest2.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\test\rsa3\RSA3CertTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/asn1/x500/AttributeTypeAndValue.cs b/crypto/src/asn1/x500/AttributeTypeAndValue.cs
new file mode 100644
index 000000000..eb6b3ca30
--- /dev/null
+++ b/crypto/src/asn1/x500/AttributeTypeAndValue.cs
@@ -0,0 +1,60 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.X500
+{
+    /**
+     * Holding class for the AttributeTypeAndValue structures that make up an RDN.
+     */
+    public class AttributeTypeAndValue
+        : Asn1Encodable
+    {
+        private readonly DerObjectIdentifier type;
+        private readonly Asn1Encodable value;
+
+        private AttributeTypeAndValue(Asn1Sequence seq)
+        {
+            type = (DerObjectIdentifier)seq[0];
+            value = seq[1];
+        }
+
+        public static AttributeTypeAndValue GetInstance(object obj)
+        {
+            if (obj is AttributeTypeAndValue)
+                return (AttributeTypeAndValue)obj;
+            if (null != obj)
+                return new AttributeTypeAndValue(Asn1Sequence.GetInstance(obj));
+            throw new ArgumentNullException("obj");
+        }
+
+        public AttributeTypeAndValue(
+            DerObjectIdentifier type,
+            Asn1Encodable value)
+        {
+            this.type = type;
+            this.value = value;
+        }
+
+        public virtual DerObjectIdentifier Type
+        {
+            get { return type; }
+        }
+
+        public virtual Asn1Encodable Value
+        {
+            get { return value; }
+        }
+
+        /**
+         * <pre>
+         * AttributeTypeAndValue ::= SEQUENCE {
+         *           type         OBJECT IDENTIFIER,
+         *           value        ANY DEFINED BY type }
+         * </pre>
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return new DerSequence(type, value);
+        }
+    }
+}
diff --git a/crypto/src/asn1/x500/Rdn.cs b/crypto/src/asn1/x500/Rdn.cs
new file mode 100644
index 000000000..4881d0890
--- /dev/null
+++ b/crypto/src/asn1/x500/Rdn.cs
@@ -0,0 +1,104 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.X500
+{
+    /**
+     * Holding class for a single Relative Distinguished Name (RDN).
+     */
+    public class Rdn
+        : Asn1Encodable
+    {
+        private readonly Asn1Set values;
+
+        private Rdn(Asn1Set values)
+        {
+            this.values = values;
+        }
+
+        public static Rdn GetInstance(object obj)
+        {
+            if (obj is Rdn)
+                return (Rdn)obj;
+            if (null != obj)
+                return new Rdn(Asn1Set.GetInstance(obj));
+            return null;
+        }
+
+        /**
+         * Create a single valued RDN.
+         *
+         * @param oid RDN type.
+         * @param value RDN value.
+         */
+        public Rdn(DerObjectIdentifier oid, Asn1Encodable value)
+        {
+            this.values = new DerSet(new DerSequence(oid, value));
+        }
+
+        public Rdn(AttributeTypeAndValue attrTAndV)
+        {
+            this.values = new DerSet(attrTAndV);
+        }
+
+        /**
+         * Create a multi-valued RDN.
+         *
+         * @param aAndVs attribute type/value pairs making up the RDN
+         */
+        public Rdn(AttributeTypeAndValue[] aAndVs)
+        {
+            this.values = new DerSet(aAndVs);
+        }
+
+        public virtual bool IsMultiValued
+        {
+            get { return this.values.Count > 1; }
+        }
+
+        /**
+         * Return the number of AttributeTypeAndValue objects in this RDN,
+         *
+         * @return size of RDN, greater than 1 if multi-valued.
+         */
+        public virtual int Count
+        {
+            get { return this.values.Count; }
+        }
+
+        public virtual AttributeTypeAndValue GetFirst()
+        {
+            if (this.values.Count == 0)
+                return null;
+
+            return AttributeTypeAndValue.GetInstance(this.values[0]);
+        }
+
+        public virtual AttributeTypeAndValue[] GetTypesAndValues()
+        {
+            AttributeTypeAndValue[] tmp = new AttributeTypeAndValue[values.Count];
+
+            for (int i = 0; i < tmp.Length; ++i)
+            {
+                tmp[i] = AttributeTypeAndValue.GetInstance(values[i]);
+            }
+
+            return tmp;
+        }
+
+        /**
+         * <pre>
+         * RelativeDistinguishedName ::=
+         *                     SET OF AttributeTypeAndValue
+
+         * AttributeTypeAndValue ::= SEQUENCE {
+         *        type     AttributeType,
+         *        value    AttributeValue }
+         * </pre>
+         * @return this object as its ASN1Primitive type
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return values;
+        }
+    }
+}
diff --git a/crypto/src/asn1/x500/style/IetfUtilities.cs b/crypto/src/asn1/x500/style/IetfUtilities.cs
new file mode 100644
index 000000000..e3236aaec
--- /dev/null
+++ b/crypto/src/asn1/x500/style/IetfUtilities.cs
@@ -0,0 +1,214 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Asn1.X500.Style
+{
+    public abstract class IetfUtilities
+    {
+        public static string ValueToString(Asn1Encodable value)
+        {
+            StringBuilder vBuf = new StringBuilder();
+
+            if (value is IAsn1String && !(value is DerUniversalString))
+            {
+                string v = ((IAsn1String)value).GetString();
+                if (v.Length > 0 && v[0] == '#')
+                {
+                    vBuf.Append('\\');
+                }
+
+                vBuf.Append(v);
+            }
+            else
+            {
+                try
+                {
+                    vBuf.Append('#');
+                    vBuf.Append(Hex.ToHexString(value.ToAsn1Object().GetEncoded(Asn1Encodable.Der)));
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("Other value has no encoded form", "value", e);
+                }
+            }
+
+            int end = vBuf.Length;
+            int index = 0;
+
+            if (vBuf.Length >= 2 && vBuf[0] == '\\' && vBuf[1] == '#')
+            {
+                index += 2;
+            }
+
+            while (index != end)
+            {
+                switch (vBuf[index])
+                {
+                    case ',':
+                    case '"':
+                    case '\\':
+                    case '+':
+                    case '=':
+                    case '<':
+                    case '>':
+                    case ';':
+                    {
+                        vBuf.Insert(index, "\\");
+                        index += 2;
+                        ++end;
+                        break;
+                    }
+                    default:
+                    {
+                        ++index;
+                        break;
+                    }
+                }
+            }
+
+            int start = 0;
+            if (vBuf.Length > 0)
+            {
+                while (vBuf.Length > start && vBuf[start] == ' ')
+                {
+                    vBuf.Insert(start, "\\");
+                    start += 2;
+                }
+            }
+
+            int endBuf = vBuf.Length - 1;
+
+            while (endBuf >= 0 && vBuf[endBuf] == ' ')
+            {
+                vBuf.Insert(endBuf, '\\');
+                endBuf--;
+            }
+
+            return vBuf.ToString();
+        }
+
+        public static string Canonicalize(string s)
+        {
+            string value = Platform.ToLowerInvariant(s);
+
+            if (value.Length > 0 && value[0] == '#')
+            {
+                Asn1Object obj = DecodeObject(value);
+
+                if (obj is IAsn1String)
+                {
+                    value = Platform.ToLowerInvariant(((IAsn1String)obj).GetString());
+                }
+            }
+
+            if (value.Length > 1)
+            {
+                int start = 0;
+                while (start + 1 < value.Length && value[start] == '\\' && value[start + 1] == ' ')
+                {
+                    start += 2;
+                }
+
+                int end = value.Length - 1;
+                while (end - 1 > 0 && value[end - 1] == '\\' && value[end] == ' ')
+                {
+                    end -= 2;
+                }
+
+                if (start > 0 || end < value.Length - 1)
+                {
+                    value = value.Substring(start, end + 1 - start);
+                }
+            }
+
+            return StripInternalSpaces(value);
+        }
+
+        public static string CanonicalString(Asn1Encodable value)
+        {
+            return Canonicalize(ValueToString(value));
+        }
+
+        private static Asn1Object DecodeObject(string oValue)
+        {
+            try
+            {
+                return Asn1Object.FromByteArray(Hex.Decode(oValue.Substring(1)));
+            }
+            catch (IOException e)
+            {
+                throw new InvalidOperationException("unknown encoding in name: " + e);
+            }
+        }
+
+        public static string StripInternalSpaces(string str)
+        {
+            if (str.IndexOf("  ") < 0)
+                return str;
+
+            StringBuilder res = new StringBuilder();
+
+            char c1 = str[0];
+            res.Append(c1);
+
+            for (int k = 1; k < str.Length; k++)
+            {
+                char c2 = str[k];
+                if (!(' ' == c1 && ' ' == c2))
+                {
+                    res.Append(c2);
+                    c1 = c2;
+                }
+            }
+
+            return res.ToString();
+        }
+
+        public static bool RdnAreEqual(Rdn rdn1, Rdn rdn2)
+        {
+            if (rdn1.Count != rdn2.Count)
+                return false;
+
+            AttributeTypeAndValue[] atvs1 = rdn1.GetTypesAndValues();
+            AttributeTypeAndValue[] atvs2 = rdn2.GetTypesAndValues();
+
+            if (atvs1.Length != atvs2.Length)
+                return false;
+
+            for (int i = 0; i != atvs1.Length; i++)
+            {
+                if (!AtvAreEqual(atvs1[i], atvs2[i]))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static bool AtvAreEqual(AttributeTypeAndValue atv1, AttributeTypeAndValue atv2)
+        {
+            if (atv1 == atv2)
+                return true;
+            if (null == atv1 || null == atv2)
+                return false;
+
+            DerObjectIdentifier o1 = atv1.Type;
+            DerObjectIdentifier o2 = atv2.Type;
+
+            if (!o1.Equals(o2))
+                return false;
+
+            string v1 = CanonicalString(atv1.Value);
+            string v2 = CanonicalString(atv2.Value);
+
+            if (!v1.Equals(v2))
+                return false;
+
+            return true;
+        }
+    }
+}
diff --git a/crypto/src/pkix/PkixNameConstraintValidator.cs b/crypto/src/pkix/PkixNameConstraintValidator.cs
index f4ae73925..4d5af28ea 100644
--- a/crypto/src/pkix/PkixNameConstraintValidator.cs
+++ b/crypto/src/pkix/PkixNameConstraintValidator.cs
@@ -2,6 +2,8 @@ using System;
 using System.Collections;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X500;
+using Org.BouncyCastle.Asn1.X500.Style;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Collections;
@@ -10,6 +12,10 @@ namespace Org.BouncyCastle.Pkix
 {
     public class PkixNameConstraintValidator
     {
+        // TODO Implement X500Name and styles
+        //private static readonly DerObjectIdentifier SerialNumberOid = Rfc4519Style.SerialNumber;
+        private static readonly DerObjectIdentifier SerialNumberOid = new DerObjectIdentifier("2.5.4.5");
+
         private ISet excludedSubtreesDN = new HashSet();
 
         private ISet excludedSubtreesDNS = new HashSet();
@@ -38,19 +44,32 @@ namespace Org.BouncyCastle.Pkix
             Asn1Sequence dns,
             Asn1Sequence subtree)
         {
-            if (subtree.Count < 1)
-            {
+            if (subtree.Count < 1 || subtree.Count > dns.Count)
                 return false;
-            }
 
-            if (subtree.Count > dns.Count)
+            for (int j = 0; j < subtree.Count; ++j)
             {
-                return false;
-            }
+                // both subtree and dns are a ASN.1 Name and the elements are a RDN
+                Rdn subtreeRdn = Rdn.GetInstance(subtree[j]);
+                Rdn dnsRdn = Rdn.GetInstance(dns[j]);
 
-            for (int j = subtree.Count - 1; j >= 0; j--)
-            {
-                if (!(subtree[j].Equals(dns[j])))
+                // check if types and values of all naming attributes are matching, other types which are not restricted are allowed, see https://tools.ietf.org/html/rfc5280#section-7.1
+
+                // Two relative distinguished names
+                //   RDN1 and RDN2 match if they have the same number of naming attributes
+                //   and for each naming attribute in RDN1 there is a matching naming attribute in RDN2.
+                //   NOTE: this is checking the attributes in the same order, which might be not necessary, if this is a problem also IETFUtils.rDNAreEqual mus tbe changed.
+                // use new RFC 5280 comparison, NOTE: this is now different from with RFC 3280, where only binary comparison is used
+                // obey RFC 5280 7.1
+                // special treatment of serialNumber for GSMA SGP.22 RSP specification
+                if (subtreeRdn.Count == 1 && dnsRdn.Count == 1
+                    && subtreeRdn.GetFirst().GetType().Equals(SerialNumberOid)
+                    && dnsRdn.GetFirst().GetType().Equals(SerialNumberOid))
+                {
+                    if (!Platform.StartsWith(dnsRdn.GetFirst().Value.ToString(), subtreeRdn.GetFirst().Value.ToString()))
+                        return false;
+                }
+                else if (!IetfUtilities.RdnAreEqual(subtreeRdn, dnsRdn))
                 {
                     return false;
                 }
@@ -202,7 +221,7 @@ namespace Org.BouncyCastle.Pkix
             ISet intersect = new HashSet();
             for (IEnumerator it = emails.GetEnumerator(); it.MoveNext(); )
             {
-                String email = ExtractNameAsString(((GeneralSubtree)it.Current)
+                string email = ExtractNameAsString(((GeneralSubtree)it.Current)
                     .Base);
 
                 if (permitted == null)
@@ -217,7 +236,7 @@ namespace Org.BouncyCastle.Pkix
                     IEnumerator it2 = permitted.GetEnumerator();
                     while (it2.MoveNext())
                     {
-                        String _permitted = (String)it2.Current;
+                        string _permitted = (string)it2.Current;
 
                         intersectEmail(email, _permitted, intersect);
                     }
@@ -226,7 +245,7 @@ namespace Org.BouncyCastle.Pkix
             return intersect;
         }
 
-        private ISet UnionEmail(ISet excluded, String email)
+        private ISet UnionEmail(ISet excluded, string email)
         {
             if (excluded.IsEmpty)
             {
@@ -244,9 +263,9 @@ namespace Org.BouncyCastle.Pkix
                 IEnumerator it = excluded.GetEnumerator();
                 while (it.MoveNext())
                 {
-                    String _excluded = (String)it.Current;
+                    string _excluded = (string)it.Current;
 
-                    unionEmail(_excluded, email, union);
+                    UnionEmail(_excluded, email, union);
                 }
 
                 return union;
@@ -476,7 +495,7 @@ namespace Org.BouncyCastle.Pkix
             return new byte[][] { min1, max1, min2, max2 };
         }
 
-        private void CheckPermittedEmail(ISet permitted, String email)
+        private void CheckPermittedEmail(ISet permitted, string email)
         //throws PkixNameConstraintValidatorException
         {
             if (permitted == null)
@@ -488,7 +507,7 @@ namespace Org.BouncyCastle.Pkix
 
             while (it.MoveNext())
             {
-                String str = ((String)it.Current);
+                string str = ((string)it.Current);
 
                 if (EmailIsConstrained(email, str))
                 {
@@ -505,7 +524,7 @@ namespace Org.BouncyCastle.Pkix
                 "Subject email address is not from a permitted subtree.");
         }
 
-        private void CheckExcludedEmail(ISet excluded, String email)
+        private void CheckExcludedEmail(ISet excluded, string email)
         //throws PkixNameConstraintValidatorException
         {
             if (excluded.IsEmpty)
@@ -517,7 +536,7 @@ namespace Org.BouncyCastle.Pkix
 
             while (it.MoveNext())
             {
-                String str = (String)it.Current;
+                string str = (string)it.Current;
 
                 if (EmailIsConstrained(email, str))
                 {
@@ -574,7 +593,7 @@ namespace Org.BouncyCastle.Pkix
          * @throws PkixNameConstraintValidatorException
          *          if the IP is excluded.
          */
-        private void checkExcludedIP(ISet excluded, byte[] ip)
+        private void CheckExcludedIP(ISet excluded, byte[] ip)
         //throws PkixNameConstraintValidatorException
         {
             if (excluded.IsEmpty)
@@ -632,9 +651,9 @@ namespace Org.BouncyCastle.Pkix
             return Org.BouncyCastle.Utilities.Arrays.AreEqual(permittedSubnetAddress, ipSubnetAddress);
         }
 
-        private bool EmailIsConstrained(String email, String constraint)
+        private bool EmailIsConstrained(string email, string constraint)
         {
-            String sub = email.Substring(email.IndexOf('@') + 1);
+            string sub = email.Substring(email.IndexOf('@') + 1);
             // a particular mailbox
             if (constraint.IndexOf('@') != -1)
             {
@@ -659,28 +678,27 @@ namespace Org.BouncyCastle.Pkix
             return false;
         }
 
-        private bool WithinDomain(String testDomain, String domain)
+        private bool WithinDomain(string testDomain, string domain)
         {
-            String tempDomain = domain;
+            string tempDomain = domain;
             if (Platform.StartsWith(tempDomain, "."))
             {
                 tempDomain = tempDomain.Substring(1);
             }
-            String[] domainParts = tempDomain.Split('.'); // Strings.split(tempDomain, '.');
-            String[] testDomainParts = testDomain.Split('.'); // Strings.split(testDomain, '.');
+
+            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(""))
+                    if (testDomainParts[i + d].Length < 1)
                     {
                         return false;
                     }
@@ -693,55 +711,33 @@ namespace Org.BouncyCastle.Pkix
             return true;
         }
 
-        private void CheckPermittedDNS(ISet permitted, String dns)
-        //throws PkixNameConstraintValidatorException
+        private void CheckPermittedDns(ISet permitted, string dns)
+            //throws PkixNameConstraintValidatorException
         {
-            if (permitted == null)
-            {
+            if (null == permitted)
                 return;
-            }
-
-            IEnumerator it = permitted.GetEnumerator();
 
-            while (it.MoveNext())
+            foreach (string str in permitted)
             {
-                String str = ((String)it.Current);
-
-                // is sub domain
-                if (WithinDomain(dns, str)
-                    || Platform.ToUpperInvariant(dns).Equals(Platform.ToUpperInvariant(str)))
-                {
+                // is sub domain or the same
+                if (WithinDomain(dns, str) || Platform.EqualsIgnoreCase(dns, str))
                     return;
-                }
             }
+
             if (dns.Length == 0 && permitted.Count == 0)
-            {
                 return;
-            }
-            throw new PkixNameConstraintValidatorException(
-                "DNS is not from a permitted subtree.");
+
+            throw new PkixNameConstraintValidatorException("DNS is not from a permitted subtree.");
         }
 
-        private void checkExcludedDNS(ISet excluded, String dns)
-        //     throws PkixNameConstraintValidatorException
+        private void CheckExcludedDns(ISet excluded, string dns)
+            //throws PkixNameConstraintValidatorException
         {
-            if (excluded.IsEmpty)
-            {
-                return;
-            }
-
-            IEnumerator it = excluded.GetEnumerator();
-
-            while (it.MoveNext())
+            foreach (string str in excluded)
             {
-                String str = ((String)it.Current);
-
                 // is sub domain or the same
 				if (WithinDomain(dns, str) || Platform.EqualsIgnoreCase(dns, str))
-                {
-                    throw new PkixNameConstraintValidatorException(
-                        "DNS is from an excluded subtree.");
-                }
+                    throw new PkixNameConstraintValidatorException("DNS is from an excluded subtree.");
             }
         }
 
@@ -754,12 +750,12 @@ namespace Org.BouncyCastle.Pkix
          * @param email2 Email address constraint 2.
          * @param union  The union.
          */
-        private void unionEmail(String email1, String email2, ISet 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);
+                string _sub = email1.Substring(email1.IndexOf('@') + 1);
                 // both are a particular mailbox
                 if (email2.IndexOf('@') != -1)
                 {
@@ -805,7 +801,7 @@ namespace Org.BouncyCastle.Pkix
             {
                 if (email2.IndexOf('@') != -1)
                 {
-                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    string _sub = email2.Substring(email1.IndexOf('@') + 1);
                     if (WithinDomain(_sub, email1))
                     {
                         union.Add(email1);
@@ -851,7 +847,7 @@ namespace Org.BouncyCastle.Pkix
             {
                 if (email2.IndexOf('@') != -1)
                 {
-                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    string _sub = email2.Substring(email1.IndexOf('@') + 1);
                     if (Platform.EqualsIgnoreCase(_sub, email1))
                     {
                         union.Add(email1);
@@ -891,12 +887,12 @@ namespace Org.BouncyCastle.Pkix
             }
         }
 
-        private void unionURI(String email1, String email2, ISet union)
+        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);
+                string _sub = email1.Substring(email1.IndexOf('@') + 1);
                 // both are a particular mailbox
                 if (email2.IndexOf('@') != -1)
                 {
@@ -943,7 +939,7 @@ namespace Org.BouncyCastle.Pkix
             {
                 if (email2.IndexOf('@') != -1)
                 {
-                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    string _sub = email2.Substring(email1.IndexOf('@') + 1);
                     if (WithinDomain(_sub, email1))
                     {
                         union.Add(email1);
@@ -989,7 +985,7 @@ namespace Org.BouncyCastle.Pkix
             {
                 if (email2.IndexOf('@') != -1)
                 {
-                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    string _sub = email2.Substring(email1.IndexOf('@') + 1);
                     if (Platform.EqualsIgnoreCase(_sub, email1))
                     {
                         union.Add(email1);
@@ -1034,7 +1030,7 @@ namespace Org.BouncyCastle.Pkix
             ISet intersect = new HashSet();
             for (IEnumerator it = dnss.GetEnumerator(); it.MoveNext(); )
             {
-                String dns = ExtractNameAsString(((GeneralSubtree)it.Current)
+                string dns = ExtractNameAsString(((GeneralSubtree)it.Current)
                     .Base);
                 if (permitted == null)
                 {
@@ -1048,7 +1044,7 @@ namespace Org.BouncyCastle.Pkix
                     IEnumerator _iter = permitted.GetEnumerator();
                     while (_iter.MoveNext())
                     {
-                        String _permitted = (String)_iter.Current;
+                        string _permitted = (string)_iter.Current;
 
                         if (WithinDomain(_permitted, dns))
                         {
@@ -1065,7 +1061,7 @@ namespace Org.BouncyCastle.Pkix
             return intersect;
         }
 
-        protected ISet unionDNS(ISet excluded, String dns)
+        protected ISet unionDNS(ISet excluded, string dns)
         {
             if (excluded.IsEmpty)
             {
@@ -1084,7 +1080,7 @@ namespace Org.BouncyCastle.Pkix
                 IEnumerator _iter = excluded.GetEnumerator();
                 while (_iter.MoveNext())
                 {
-                    String _permitted = (String)_iter.Current;
+                    string _permitted = (string)_iter.Current;
 
                     if (WithinDomain(_permitted, dns))
                     {
@@ -1113,12 +1109,12 @@ namespace Org.BouncyCastle.Pkix
          * @param email2    Email address constraint 2.
          * @param intersect The intersection.
          */
-        private void intersectEmail(String email1, String email2, ISet intersect)
+        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);
+                string _sub = email1.Substring(email1.IndexOf('@') + 1);
                 // both are a particular mailbox
                 if (email2.IndexOf('@') != -1)
                 {
@@ -1149,7 +1145,7 @@ namespace Org.BouncyCastle.Pkix
             {
                 if (email2.IndexOf('@') != -1)
                 {
-                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    string _sub = email2.Substring(email1.IndexOf('@') + 1);
                     if (WithinDomain(_sub, email1))
                     {
                         intersect.Add(email2);
@@ -1180,7 +1176,7 @@ namespace Org.BouncyCastle.Pkix
             {
                 if (email2.IndexOf('@') != -1)
                 {
-                    String _sub = email2.Substring(email2.IndexOf('@') + 1);
+                    string _sub = email2.Substring(email2.IndexOf('@') + 1);
                     if (Platform.EqualsIgnoreCase(_sub, email1))
                     {
                         intersect.Add(email2);
@@ -1205,7 +1201,7 @@ namespace Org.BouncyCastle.Pkix
             }
         }
 
-        private void checkExcludedURI(ISet excluded, String uri)
+        private void checkExcludedURI(ISet excluded, string uri)
         //       throws PkixNameConstraintValidatorException
         {
             if (excluded.IsEmpty)
@@ -1217,7 +1213,7 @@ namespace Org.BouncyCastle.Pkix
 
             while (it.MoveNext())
             {
-                String str = ((String)it.Current);
+                string str = ((string)it.Current);
 
                 if (IsUriConstrained(uri, str))
                 {
@@ -1232,7 +1228,7 @@ namespace Org.BouncyCastle.Pkix
             ISet intersect = new HashSet();
             for (IEnumerator it = uris.GetEnumerator(); it.MoveNext(); )
             {
-                String uri = ExtractNameAsString(((GeneralSubtree)it.Current)
+                string uri = ExtractNameAsString(((GeneralSubtree)it.Current)
                     .Base);
                 if (permitted == null)
                 {
@@ -1246,7 +1242,7 @@ namespace Org.BouncyCastle.Pkix
                     IEnumerator _iter = permitted.GetEnumerator();
                     while (_iter.MoveNext())
                     {
-                        String _permitted = (String)_iter.Current;
+                        string _permitted = (string)_iter.Current;
                         intersectURI(_permitted, uri, intersect);
                     }
                 }
@@ -1254,7 +1250,7 @@ namespace Org.BouncyCastle.Pkix
             return intersect;
         }
 
-        private ISet unionURI(ISet excluded, String uri)
+        private ISet unionURI(ISet excluded, string uri)
         {
             if (excluded.IsEmpty)
             {
@@ -1273,7 +1269,7 @@ namespace Org.BouncyCastle.Pkix
                 IEnumerator _iter = excluded.GetEnumerator();
                 while (_iter.MoveNext())
                 {
-                    String _excluded = (String)_iter.Current;
+                    string _excluded = (string)_iter.Current;
 
                     unionURI(_excluded, uri, union);
                 }
@@ -1282,12 +1278,12 @@ namespace Org.BouncyCastle.Pkix
             }
         }
 
-        private void intersectURI(String email1, String email2, ISet intersect)
+        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);
+                string _sub = email1.Substring(email1.IndexOf('@') + 1);
                 // both are a particular mailbox
                 if (email2.IndexOf('@') != -1)
                 {
@@ -1318,7 +1314,7 @@ namespace Org.BouncyCastle.Pkix
             {
                 if (email2.IndexOf('@') != -1)
                 {
-                    String _sub = email2.Substring(email1.IndexOf('@') + 1);
+                    string _sub = email2.Substring(email1.IndexOf('@') + 1);
                     if (WithinDomain(_sub, email1))
                     {
                         intersect.Add(email2);
@@ -1349,7 +1345,7 @@ namespace Org.BouncyCastle.Pkix
             {
                 if (email2.IndexOf('@') != -1)
                 {
-                    String _sub = email2.Substring(email2.IndexOf('@') + 1);
+                    string _sub = email2.Substring(email2.IndexOf('@') + 1);
                     if (Platform.EqualsIgnoreCase(_sub, email1))
                     {
                         intersect.Add(email2);
@@ -1374,7 +1370,7 @@ namespace Org.BouncyCastle.Pkix
             }
         }
 
-        private void CheckPermittedURI(ISet permitted, String uri)
+        private void CheckPermittedURI(ISet permitted, string uri)
         //        throws PkixNameConstraintValidatorException
         {
             if (permitted == null)
@@ -1386,7 +1382,7 @@ namespace Org.BouncyCastle.Pkix
 
             while (it.MoveNext())
             {
-                String str = ((String)it.Current);
+                string str = ((string)it.Current);
 
                 if (IsUriConstrained(uri, str))
                 {
@@ -1401,9 +1397,9 @@ namespace Org.BouncyCastle.Pkix
                 "URI is not from a permitted subtree.");
         }
 
-        private bool IsUriConstrained(String uri, String constraint)
+        private bool IsUriConstrained(string uri, string constraint)
         {
-            String host = ExtractHostFromURL(uri);
+            string host = ExtractHostFromURL(uri);
             // a host
             if (!Platform.StartsWith(constraint, "."))
             {
@@ -1422,11 +1418,11 @@ namespace Org.BouncyCastle.Pkix
             return false;
         }
 
-        private static String ExtractHostFromURL(String url)
+        private static string ExtractHostFromURL(string url)
         {
             // see RFC 1738
             // remove ':' after protocol, e.g. http:
-            String sub = url.Substring(url.IndexOf(':') + 1);
+            string sub = url.Substring(url.IndexOf(':') + 1);
             // extract host from Common Internet Scheme Syntax, e.g. http://
             int idxOfSlashes = Platform.IndexOf(sub, "//");
             if (idxOfSlashes != -1)
@@ -1466,7 +1462,7 @@ namespace Org.BouncyCastle.Pkix
                         ExtractNameAsString(name));
                     break;
                 case 2:
-                    CheckPermittedDNS(permittedSubtreesDNS, DerIA5String.GetInstance(
+                    CheckPermittedDns(permittedSubtreesDNS, DerIA5String.GetInstance(
                         name.Name).GetString());
                     break;
                 case 4:
@@ -1501,7 +1497,7 @@ namespace Org.BouncyCastle.Pkix
                     CheckExcludedEmail(excludedSubtreesEmail, ExtractNameAsString(name));
                     break;
                 case 2:
-                    checkExcludedDNS(excludedSubtreesDNS, DerIA5String.GetInstance(
+                    CheckExcludedDns(excludedSubtreesDNS, DerIA5String.GetInstance(
                         name.Name).GetString());
                     break;
                 case 4:
@@ -1514,7 +1510,7 @@ namespace Org.BouncyCastle.Pkix
                 case 7:
                     byte[] ip = Asn1OctetString.GetInstance(name.Name).GetOctets();
 
-                    checkExcludedIP(excludedSubtreesIP, ip);
+                    CheckExcludedIP(excludedSubtreesIP, ip);
                     break;
             }
         }
@@ -1575,7 +1571,7 @@ namespace Org.BouncyCastle.Pkix
             }
         }
 
-        private String ExtractNameAsString(GeneralName name)
+        private string ExtractNameAsString(GeneralName name)
         {
             return DerIA5String.GetInstance(name.Name).GetString();
         }
@@ -1840,9 +1836,9 @@ namespace Org.BouncyCastle.Pkix
          * @param ip The IP with subnet mask.
          * @return The stringified IP address.
          */
-        private String StringifyIP(byte[] ip)
+        private string StringifyIP(byte[] ip)
         {
-            String temp = "";
+            string temp = "";
             for (int i = 0; i < ip.Length / 2; i++)
             {
                 //temp += Integer.toString(ip[i] & 0x00FF) + ".";
@@ -1859,9 +1855,9 @@ namespace Org.BouncyCastle.Pkix
             return temp;
         }
 
-        private String StringifyIPCollection(ISet ips)
+        private string StringifyIPCollection(ISet ips)
         {
-            String temp = "";
+            string temp = "";
             temp += "[";
             for (IEnumerator it = ips.GetEnumerator(); it.MoveNext(); )
             {
@@ -1876,9 +1872,9 @@ namespace Org.BouncyCastle.Pkix
             return temp;
         }
 
-        public override String ToString()
+        public override string ToString()
         {
-            String temp = "";
+            string temp = "";
 
             temp += "permitted:\n";
             if (permittedSubtreesDN != null)
diff --git a/crypto/src/pkix/Rfc3280CertPathUtilities.cs b/crypto/src/pkix/Rfc3280CertPathUtilities.cs
index c6f3fbff9..cdb69b7e0 100644
--- a/crypto/src/pkix/Rfc3280CertPathUtilities.cs
+++ b/crypto/src/pkix/Rfc3280CertPathUtilities.cs
@@ -810,18 +810,18 @@ namespace Org.BouncyCastle.Pkix
 					validCerts.Add(signingCert);
 					validKeys.Add(PkixCertPathValidatorUtilities.GetNextWorkingKey(certs, 0));
 				}
-				catch (PkixCertPathBuilderException e)
-				{
-					throw new Exception("Internal error.", e);
-				}
-				catch (PkixCertPathValidatorException e)
-				{
-					throw new Exception("Public key of issuer certificate of CRL could not be retrieved.", e);
-				}
-				//catch (Exception e)
-				//{
-				//    throw new Exception(e.Message);
-				//}
+                catch (PkixCertPathBuilderException e)
+                {
+                    throw new Exception("CertPath for CRL signer failed to validate.", e);
+                }
+                catch (PkixCertPathValidatorException e)
+                {
+                    throw new Exception("Public key of issuer certificate of CRL could not be retrieved.", e);
+                }
+                //catch (Exception e)
+                //{
+                //    throw new Exception(e.Message);
+                //}
 			}
 
 			ISet checkKeys = new HashSet();
diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj
index 63cf0b713..9420cc832 100644
--- a/crypto/test/UnitTests.csproj
+++ b/crypto/test/UnitTests.csproj
@@ -451,6 +451,7 @@
     <Compile Include="src\test\X509CertificatePairTest.cs" />
     <Compile Include="src\test\X509StoreTest.cs" />
     <Compile Include="src\test\nist\NistCertPathTest.cs" />
+    <Compile Include="src\test\nist\NistCertPathTest2.cs" />
     <Compile Include="src\test\rsa3\RSA3CertTest.cs" />
     <Compile Include="src\tsp\test\AllTests.cs" />
     <Compile Include="src\tsp\test\GenTimeAccuracyTest.cs" />
diff --git a/crypto/test/src/test/PkixNameConstraintsTest.cs b/crypto/test/src/test/PkixNameConstraintsTest.cs
index a20fc33c4..073f9f2a3 100644
--- a/crypto/test/src/test/PkixNameConstraintsTest.cs
+++ b/crypto/test/src/test/PkixNameConstraintsTest.cs
@@ -72,19 +72,23 @@ namespace Org.BouncyCastle.Tests
 
 		private readonly string[] dnIntersection = { "O=test org, OU=test org unit, CN=John Doe" };
 
-		private readonly string testDN = "O=test org, OU=test org unit, CN=John Doe";
+        // Note: In BC text conversion is ISO format - IETF starts at the back.
+        private readonly string testDN = "O=test org, OU=test org unit, CN=John Doe";
 
-		private readonly string[] testDNIsConstraint = {
-			"O=test org, OU=test org unit",
-			"O=test org, OU=test org unit, CN=John Doe" };
+        private readonly string[] testDNIsConstraint =
+        {
+            "O=test org, OU=test org unit",
+            "O=test org, OU=test org unit, CN=John Doe",
+        };
 
-		private readonly string[] testDNIsNotConstraint = {
-			"O=test org, OU=test org unit, CN=John Doe2",
-			"O=test org, OU=test org unit2",
-			"OU=test org unit, O=test org, CN=John Doe",
-			"O=test org, OU=test org unit, CN=John Doe, L=USA" };
+        private readonly string[] testDNIsNotConstraint =
+        {
+            "O=test org, OU=test org unit, CN=John Doe2",
+            "O=test org, OU=test org unit2",
+            "O=test org, OU=test org unit, CN=John Doe, L=USA"
+        };
 
-		private readonly string testDNS = "abc.test.com";
+        private readonly string testDNS = "abc.test.com";
 
 		private readonly string[] testDNSIsConstraint = { "test.com", "abc.test.com", "test.com" };
 
@@ -185,7 +189,15 @@ namespace Org.BouncyCastle.Tests
 				uriintersect);
 			TestConstraints(GeneralName.IPAddress, testIP, testIPIsConstraint,
 				testIPIsNotConstraint, ip1, ip2, ipunion, ipintersect);
-		}
+
+            PkixNameConstraintValidator constraintValidator = new PkixNameConstraintValidator();
+            constraintValidator.IntersectPermittedSubtree(new DerSequence(new GeneralSubtree(
+                new GeneralName(GeneralName.DirectoryName,
+                    new X509Name(true, "ou=permittedSubtree1, o=Test Certificates 2011, c=US")))));
+            constraintValidator.checkPermitted(
+                new GeneralName(GeneralName.DirectoryName,
+                    new X509Name(true, "cn=Valid DN nameConstraints EE Certificate Test1, ou=permittedSubtree1, o=Test Certificates 2011, c=US")));
+        }
 
 		/**
 		 * Tests string based GeneralNames for inclusion or exclusion.
diff --git a/crypto/test/src/test/nist/NistCertPathTest.cs b/crypto/test/src/test/nist/NistCertPathTest.cs
index 6a23cac22..13a85a980 100644
--- a/crypto/test/src/test/nist/NistCertPathTest.cs
+++ b/crypto/test/src/test/nist/NistCertPathTest.cs
@@ -35,15 +35,15 @@ namespace Org.BouncyCastle.Tests.Nist
 		private static readonly string NIST_TEST_POLICY_2 = "2.16.840.1.101.3.2.1.48.2";
 		private static readonly string NIST_TEST_POLICY_3 = "2.16.840.1.101.3.2.1.48.3";
 
-		private static IDictionary certs = new Hashtable();
-		private static IDictionary crls = new Hashtable();
+		private static readonly IDictionary certs = new Hashtable();
+        private static readonly IDictionary crls = new Hashtable();
 
-		private static ISet noPolicies = new HashSet();
-		private static ISet anyPolicy = new HashSet();
-		private static ISet nistTestPolicy1 = new HashSet();
-		private static ISet nistTestPolicy2 = new HashSet();
-		private static ISet nistTestPolicy3 = new HashSet();
-		private static ISet nistTestPolicy1And2 = new HashSet();
+        private static readonly ISet noPolicies = new HashSet();
+        private static readonly ISet anyPolicy = new HashSet();
+        private static readonly ISet nistTestPolicy1 = new HashSet();
+        private static readonly ISet nistTestPolicy2 = new HashSet();
+        private static readonly ISet nistTestPolicy3 = new HashSet();
+        private static readonly ISet nistTestPolicy1And2 = new HashSet();
 
 		static NistCertPathTest()
 		{
@@ -54,7 +54,6 @@ namespace Org.BouncyCastle.Tests.Nist
 			nistTestPolicy3.Add(NIST_TEST_POLICY_3);
 			nistTestPolicy1And2.Add(NIST_TEST_POLICY_1);
 			nistTestPolicy1And2.Add(NIST_TEST_POLICY_2);
-
 		}
 
 		[Test]
@@ -474,7 +473,7 @@ namespace Org.BouncyCastle.Tests.Nist
 			string[] certList = new string[] { "inhibitAnyPolicy1CACert", "inhibitAnyPolicy1SelfIssuedCACert", "inhibitAnyPolicy1subCA2Cert", "ValidSelfIssuedinhibitAnyPolicyTest7EE" };
 			string[] crlList = new string[] { TRUST_ANCHOR_ROOT_CRL, "inhibitAnyPolicy1CACRL", "inhibitAnyPolicy1subCA2CRL" };
 
-			doBuilderTest(TRUST_ANCHOR_ROOT_CERTIFICATE, certList, crlList, null, false, false);
+			DoBuilderTest(TRUST_ANCHOR_ROOT_CERTIFICATE, certList, crlList, null, false, false);
 		}
 
 		// 4.4.19
@@ -484,7 +483,7 @@ namespace Org.BouncyCastle.Tests.Nist
 			string[] certList = new string[] { "SeparateCertificateandCRLKeysCertificateSigningCACert", "SeparateCertificateandCRLKeysCRLSigningCert", "ValidSeparateCertificateandCRLKeysTest19EE" };
 			string[] crlList = new string[] { TRUST_ANCHOR_ROOT_CRL, "SeparateCertificateandCRLKeysCRL" };
 
-			doBuilderTest(TRUST_ANCHOR_ROOT_CERTIFICATE, certList, crlList, null, false, false);
+			DoBuilderTest(TRUST_ANCHOR_ROOT_CERTIFICATE, certList, crlList, null, false, false);
 		}
 
 		[Test]
@@ -600,6 +599,7 @@ namespace Org.BouncyCastle.Tests.Nist
 			try
 			{
 				DoTest(trustAnchor, certs, crls, policies);
+
 				Assert.Fail("path accepted when should be rejected");
 			}
 			catch (PkixCertPathValidatorException e)
@@ -673,7 +673,6 @@ namespace Org.BouncyCastle.Tests.Nist
 				"CRL/Collection",
 				new X509CollectionStoreParameters(x509Crls));
 
-//			CertPathValidator validator = CertPathValidator.GetInstance("PKIX");
 			PkixCertPathValidator validator = new PkixCertPathValidator();
 			PkixParameters parameters = new PkixParameters(trustedSet);
 
@@ -693,7 +692,7 @@ namespace Org.BouncyCastle.Tests.Nist
 			return validator.Validate(certPath, parameters);
 		}
 
-		private PkixCertPathBuilderResult doBuilderTest(
+		private PkixCertPathBuilderResult DoBuilderTest(
 			string		trustAnchor,
 			string[]	certs,
 			string[]	crls,
@@ -727,7 +726,6 @@ namespace Org.BouncyCastle.Tests.Nist
 				"CRL/Collection",
 				new X509CollectionStoreParameters(x509Crls));
 
-//			CertPathBuilder builder = CertPathBuilder.GetInstance("PKIX");   
 			PkixCertPathBuilder builder = new PkixCertPathBuilder();
 
 			X509CertStoreSelector endSelector = new X509CertStoreSelector();
@@ -766,70 +764,55 @@ namespace Org.BouncyCastle.Tests.Nist
 			}
 		}
 
-		private X509Certificate LoadCert(
-			string certName)
-		{
-			X509Certificate cert = (X509Certificate)certs[certName];
-
-			if (cert != null)
-			{
-				return cert;
-			}
-
-			Stream fs = null;
-
-			try
-			{
-				fs = SimpleTest.GetTestDataAsStream("PKITS.certs." + certName + ".crt");
-
-				cert = new X509CertificateParser().ReadCertificate(fs);
-
-				certs[certName] = cert;
-
-				return cert;
-			}
-			catch (Exception e)
-			{
-				throw new InvalidOperationException("exception loading certificate " + certName + ": " + e);
-			}
-			finally
-			{
-				fs.Close();
-			}
-		}
-
-		private X509Crl LoadCrl(
-			string crlName)
-			//throws Exception
-		{
-			X509Crl crl = (X509Crl)certs[crlName];
-        
-			if (crl != null)
-			{
-				return crl;
-			}
-
-			Stream fs = null;
-
-			try
-			{
-				fs = SimpleTest.GetTestDataAsStream("PKITS.crls." + crlName + ".crl");
-
-				crl = new X509CrlParser().ReadCrl(fs);
-
-				crls[crlName] = crl;
-
-				return crl;
-			}
-			catch (Exception)
-			{
-				throw new InvalidOperationException("exception loading CRL: " + crlName);
-			}
-			finally
-			{
-				fs.Close();
-			}
-		}
+        private X509Certificate LoadCert(string certName)
+        {
+            X509Certificate cert = (X509Certificate)certs[certName];
+            if (null != cert)
+                return cert;
+
+            Stream fs = null;
+
+            try
+            {
+                fs = SimpleTest.GetTestDataAsStream("PKITS.certs." + certName + ".crt");
+                cert = new X509CertificateParser().ReadCertificate(fs);
+                certs[certName] = cert;
+                return cert;
+            }
+            catch (Exception e)
+            {
+                throw new InvalidOperationException("exception loading certificate " + certName + ": " + e);
+            }
+            finally
+            {
+                fs.Close();
+            }
+        }
+
+        private X509Crl LoadCrl(string crlName)
+        {
+            X509Crl crl = (X509Crl)crls[crlName];
+            if (null != crl)
+                return crl;
+
+            Stream fs = null;
+
+            try
+            {
+                fs = SimpleTest.GetTestDataAsStream("PKITS.crls." + crlName + ".crl");
+                crl = new X509CrlParser().ReadCrl(fs);
+                crls[crlName] = crl;
+                return crl;
+            }
+            catch (Exception)
+            {
+                throw new InvalidOperationException("exception loading CRL: " + crlName);
+            }
+            finally
+            {
+                fs.Close();
+            }
+        }
 
 		private TrustAnchor GetTrustAnchor(string trustAnchorName)
 		{
diff --git a/crypto/test/src/test/nist/NistCertPathTest2.cs b/crypto/test/src/test/nist/NistCertPathTest2.cs
new file mode 100644
index 000000000..e9dd7f959
--- /dev/null
+++ b/crypto/test/src/test/nist/NistCertPathTest2.cs
@@ -0,0 +1,499 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Reflection;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Pkix;
+using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.Utilities.Test;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Extension;
+using Org.BouncyCastle.X509.Store;
+
+namespace Org.BouncyCastle.Tests.Nist
+{
+    /**
+     * NIST CertPath test data for RFC 3280
+     */
+	[TestFixture]
+    public class NistCertPathTest2
+    {
+        private static readonly string ANY_POLICY = "2.5.29.32.0";
+        private static readonly string NIST_TEST_POLICY_1 = "2.16.840.1.101.3.2.1.48.1";
+        private static readonly string NIST_TEST_POLICY_2 = "2.16.840.1.101.3.2.1.48.2";
+        private static readonly string NIST_TEST_POLICY_3 = "2.16.840.1.101.3.2.1.48.3";
+
+		private static readonly IDictionary certs = new Hashtable();
+		private static readonly IDictionary crls = new Hashtable();
+
+        private static readonly ISet noPolicies = new HashSet();
+        private static readonly ISet anyPolicy = new HashSet();
+        private static readonly ISet nistTestPolicy1 = new HashSet();
+        private static readonly ISet nistTestPolicy2 = new HashSet();
+        private static readonly ISet nistTestPolicy3 = new HashSet();
+        private static readonly ISet nistTestPolicy1And2 = new HashSet();
+
+        static NistCertPathTest2()
+		{
+			anyPolicy.Add(ANY_POLICY);
+
+			nistTestPolicy1.Add(NIST_TEST_POLICY_1);
+			nistTestPolicy2.Add(NIST_TEST_POLICY_2);
+			nistTestPolicy3.Add(NIST_TEST_POLICY_3);
+			nistTestPolicy1And2.Add(NIST_TEST_POLICY_1);
+			nistTestPolicy1And2.Add(NIST_TEST_POLICY_2);
+		}
+
+        // 4.13
+		[Test]
+        public void TestValidDNnameConstraintsTest1()
+        {
+            DoTest("TrustAnchorRootCertificate",
+                new string[] { "ValidDNnameConstraintsTest1EE", "nameConstraintsDN1CACert" },
+                new string[] { "nameConstraintsDN1CACRL", "TrustAnchorRootCRL" });
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest2()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest2EE", "nameConstraintsDN1CACert"},
+                new string[]{"nameConstraintsDN1CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest3()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest3EE", "nameConstraintsDN1CACert"},
+                new string[]{"nameConstraintsDN1CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject alternative name failed.");
+        }
+
+ 		[Test]
+       public void TestValidDNnameConstraintsTest4()
+        {
+            DoTest("TrustAnchorRootCertificate",
+                new string[] { "ValidDNnameConstraintsTest4EE", "nameConstraintsDN1CACert" },
+                new string[] { "nameConstraintsDN1CACRL", "TrustAnchorRootCRL" });
+        }
+
+		[Test]
+        public void TestValidDNnameConstraintsTest5()
+        {
+            DoTest("TrustAnchorRootCertificate",
+                new string[] { "ValidDNnameConstraintsTest5EE", "nameConstraintsDN2CACert" },
+                new string[] { "nameConstraintsDN2CACRL", "TrustAnchorRootCRL" });
+        }
+
+		[Test]
+        public void TestValidDNnameConstraintsTest6()
+        {
+            DoTest("TrustAnchorRootCertificate",
+                new string[] { "ValidDNnameConstraintsTest6EE", "nameConstraintsDN3CACert" },
+                new string[] { "nameConstraintsDN3CACRL", "TrustAnchorRootCRL" });
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest7()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest7EE", "nameConstraintsDN3CACert"},
+                new string[]{"nameConstraintsDN3CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest8()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest8EE", "nameConstraintsDN4CACert"},
+                new string[]{"nameConstraintsDN4CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest9()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest9EE", "nameConstraintsDN4CACert"},
+                new string[]{"nameConstraintsDN4CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest10()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest10EE", "nameConstraintsDN5CACert"},
+                new string[]{"nameConstraintsDN5CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+		[Test]
+        public void TestValidDNnameConstraintsTest11()
+        {
+            DoTest("TrustAnchorRootCertificate",
+                new string[] { "ValidDNnameConstraintsTest11EE", "nameConstraintsDN5CACert" },
+                new string[] { "nameConstraintsDN5CACRL", "TrustAnchorRootCRL" });
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest12()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest10EE", "nameConstraintsDN5CACert"},
+                new string[]{"nameConstraintsDN5CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest13()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest13EE", "nameConstraintsDN1subCA2Cert", "nameConstraintsDN1CACert"},
+                new string[]{"nameConstraintsDN1subCA2CRL", "nameConstraintsDN1CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+		[Test]
+        public void TestValidDNnameConstraintsTest14()
+        {
+            DoTest("TrustAnchorRootCertificate",
+                new string[] { "ValidDNnameConstraintsTest14EE", "nameConstraintsDN1subCA2Cert", "nameConstraintsDN1CACert" },
+                new string[] { "nameConstraintsDN1subCA2CRL", "nameConstraintsDN1CACRL", "TrustAnchorRootCRL" });
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest15()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest15EE", "nameConstraintsDN3subCA1Cert", "nameConstraintsDN3CACert"},
+                new string[]{"nameConstraintsDN3subCA1CRL", "nameConstraintsDN3CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest16()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest16EE", "nameConstraintsDN3subCA1Cert", "nameConstraintsDN3CACert"},
+                new string[]{"nameConstraintsDN3subCA1CRL", "nameConstraintsDN3CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+		[Test]
+        public void TestInvalidDNnameConstraintsTest17()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest17EE", "nameConstraintsDN3subCA2Cert", "nameConstraintsDN3CACert"},
+                new string[]{"nameConstraintsDN3subCA2CRL", "nameConstraintsDN3CACRL", "TrustAnchorRootCRL"},
+                0,
+                "Subtree check for certificate subject failed.");
+        }
+
+ 		[Test]
+        public void TestValidDNnameConstraintsTest18()
+        {
+            DoTest("TrustAnchorRootCertificate",
+                new string[] { "ValidDNnameConstraintsTest18EE", "nameConstraintsDN3subCA2Cert", "nameConstraintsDN3CACert" },
+                new string[] { "nameConstraintsDN3subCA2CRL", "nameConstraintsDN3CACRL", "TrustAnchorRootCRL" });
+        }
+
+ 		[Test]
+        public void TestValidDNnameConstraintsTest19()
+        {
+            DoBuilderTest("TrustAnchorRootCertificate",
+                new string[] { "ValidDNnameConstraintsTest19EE", "nameConstraintsDN1SelfIssuedCACert", "nameConstraintsDN1CACert" },
+                new string[] { "nameConstraintsDN1CACRL", "TrustAnchorRootCRL" },
+                null, false, false);
+        }
+
+ 		[Test]
+        public void TestInvalidDNnameConstraintsTest20()
+        {
+            DoExceptionTest("TrustAnchorRootCertificate",
+                new string[]{"InvalidDNnameConstraintsTest20EE", "nameConstraintsDN1CACert"},
+                new string[]{"nameConstraintsDN1CACRL", "TrustAnchorRootCRL"},
+                0,
+                "CertPath for CRL signer failed to validate.");   // due to a subtree failure
+        }
+
+		private void DoExceptionTest(
+			string		trustAnchor,
+			string[]	certs,
+			string[]	crls,
+			int			index,
+			string		message)
+		{
+			try
+			{
+				DoTest(trustAnchor, certs, crls);
+
+				Assert.Fail("path accepted when should be rejected");
+			}
+			catch (PkixCertPathValidatorException e)
+			{
+				Assert.AreEqual(index, e.Index);
+				Assert.AreEqual(message, e.Message);
+			}
+		}
+
+		private void DoExceptionTest(
+			string		trustAnchor,
+			string[]	certs,
+			string[]	crls,
+			ISet		policies,
+			int			index,
+			string		message)
+		{
+			try
+			{
+				DoTest(trustAnchor, certs, crls, policies);
+
+				Assert.Fail("path accepted when should be rejected");
+			}
+			catch (PkixCertPathValidatorException e)
+			{
+				Assert.AreEqual(index, e.Index);
+				Assert.AreEqual(message, e.Message);
+			}
+		}
+
+		private void DoExceptionTest(
+			string		trustAnchor,
+			string[]	certs,
+			string[]	crls,
+			int			index,
+			string		mesStart,
+			string		mesEnd)
+		{
+			try
+			{
+				DoTest(trustAnchor, certs, crls);
+
+				Assert.Fail("path accepted when should be rejected");
+			}
+			catch (PkixCertPathValidatorException e)
+			{
+				Assert.AreEqual(index, e.Index);
+				Assert.IsTrue(e.Message.StartsWith(mesStart));
+				Assert.IsTrue(e.Message.EndsWith(mesEnd));
+			}
+		}
+
+		private PkixCertPathValidatorResult DoTest(
+			string trustAnchor,
+			string[] certs,
+			string[] crls)
+		{
+			return DoTest(trustAnchor, certs, crls, null);
+		}
+
+		private PkixCertPathValidatorResult DoTest(
+			string trustAnchor,
+			string[] certs,
+			string[] crls,
+			ISet policies)
+		{
+			ISet trustedSet = new HashSet();
+			trustedSet.Add(GetTrustAnchor(trustAnchor));
+
+			IList x509Certs = new ArrayList();
+			IList x509Crls = new ArrayList();
+			X509Certificate endCert = LoadCert(certs[certs.Length - 1]);
+
+			for (int i = 0; i != certs.Length - 1; i++)
+			{
+				x509Certs.Add(LoadCert(certs[i]));
+			}
+
+			x509Certs.Add(endCert);
+
+			PkixCertPath certPath = new PkixCertPath(x509Certs);
+
+			for (int i = 0; i != crls.Length; i++)
+			{
+				x509Crls.Add(LoadCrl(crls[i]));
+			}
+
+			IX509Store x509CertStore = X509StoreFactory.Create(
+				"Certificate/Collection",
+				new X509CollectionStoreParameters(x509Certs));
+			IX509Store x509CrlStore = X509StoreFactory.Create(
+				"CRL/Collection",
+				new X509CollectionStoreParameters(x509Crls));
+
+            PkixCertPathValidator validator = new PkixCertPathValidator();
+			PkixParameters parameters = new PkixParameters(trustedSet);
+
+			parameters.AddStore(x509CertStore);
+			parameters.AddStore(x509CrlStore);
+			parameters.IsRevocationEnabled = true;
+
+			if (policies != null)
+			{
+				parameters.IsExplicitPolicyRequired = true;
+				parameters.SetInitialPolicies(policies);
+			}
+
+			// Perform validation as of this date since test certs expired
+			parameters.Date = new DateTimeObject(DateTime.Parse("1/1/2011"));
+
+			return validator.Validate(certPath, parameters);
+        }
+
+        private PkixCertPathBuilderResult DoBuilderTest(
+            string trustAnchor,
+            string[] certs,
+            string[] crls,
+            ISet initialPolicies,
+            bool policyMappingInhibited,
+            bool anyPolicyInhibited)
+        {
+            ISet trustedSet = new HashSet();
+            trustedSet.Add(GetTrustAnchor(trustAnchor));
+
+            IList x509Certs = new ArrayList();
+            IList x509Crls = new ArrayList();
+            X509Certificate endCert = LoadCert(certs[certs.Length - 1]);
+
+            for (int i = 0; i != certs.Length - 1; i++)
+            {
+                x509Certs.Add(LoadCert(certs[i]));
+            }
+
+            x509Certs.Add(endCert);
+
+            for (int i = 0; i != crls.Length; i++)
+            {
+                x509Crls.Add(LoadCrl(crls[i]));
+            }
+
+            IX509Store x509CertStore = X509StoreFactory.Create(
+                "Certificate/Collection",
+                new X509CollectionStoreParameters(x509Certs));
+            IX509Store x509CrlStore = X509StoreFactory.Create(
+                "CRL/Collection",
+                new X509CollectionStoreParameters(x509Crls));
+
+            PkixCertPathBuilder builder = new PkixCertPathBuilder();
+
+            X509CertStoreSelector endSelector = new X509CertStoreSelector();
+
+            endSelector.Certificate = endCert;
+
+            PkixBuilderParameters builderParams = new PkixBuilderParameters(trustedSet, endSelector);
+
+            if (initialPolicies != null)
+            {
+                builderParams.SetInitialPolicies(initialPolicies);
+                builderParams.IsExplicitPolicyRequired = true;
+            }
+            if (policyMappingInhibited)
+            {
+                builderParams.IsPolicyMappingInhibited = policyMappingInhibited;
+            }
+            if (anyPolicyInhibited)
+            {
+                builderParams.IsAnyPolicyInhibited = anyPolicyInhibited;
+            }
+
+            builderParams.AddStore(x509CertStore);
+            builderParams.AddStore(x509CrlStore);
+
+            // Perform validation as of this date since test certs expired
+            builderParams.Date = new DateTimeObject(DateTime.Parse("1/1/2011"));
+
+            try
+            {
+                return (PkixCertPathBuilderResult)builder.Build(builderParams);
+            }
+            catch (PkixCertPathBuilderException e)
+            {
+                throw e.InnerException;
+            }
+        }
+
+        private X509Certificate LoadCert(string certName)
+		{
+			X509Certificate cert = (X509Certificate)certs[certName];
+			if (null != cert)
+				return cert;
+
+            Stream fs = null;
+
+			try
+			{
+				fs = SimpleTest.GetTestDataAsStream("PKITS.certs." + certName + ".crt");
+				cert = new X509CertificateParser().ReadCertificate(fs);
+				certs[certName] = cert;
+				return cert;
+			}
+			catch (Exception e)
+			{
+				throw new InvalidOperationException("exception loading certificate " + certName + ": " + e);
+			}
+			finally
+			{
+				fs.Close();
+			}
+		}
+
+		private X509Crl LoadCrl(string crlName)
+		{
+			X509Crl crl = (X509Crl)crls[crlName];
+			if (null != crl)
+				return crl;
+
+            Stream fs = null;
+
+			try
+			{
+				fs = SimpleTest.GetTestDataAsStream("PKITS.crls." + crlName + ".crl");
+				crl = new X509CrlParser().ReadCrl(fs);
+				crls[crlName] = crl;
+				return crl;
+			}
+			catch (Exception)
+			{
+				throw new InvalidOperationException("exception loading CRL: " + crlName);
+			}
+			finally
+			{
+				fs.Close();
+			}
+		}
+
+        private TrustAnchor GetTrustAnchor(string trustAnchorName)
+		{
+			X509Certificate cert = LoadCert(trustAnchorName);
+			Asn1OctetString extBytes = cert.GetExtensionValue(X509Extensions.NameConstraints);
+
+			if (extBytes != null)
+			{
+				Asn1Encodable extValue = X509ExtensionUtilities.FromExtensionValue(extBytes);
+
+				return new TrustAnchor(cert, extValue.GetDerEncoded());
+			}
+
+			return new TrustAnchor(cert, null);
+		}
+    }
+}