diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2019-06-01 19:11:34 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2019-06-01 19:11:34 +0700 |
commit | 5cc0a46f4dbaafd9e3104643ec1fa5bf69ac5a6c (patch) | |
tree | 0ac8a1bd4557e3682c2fb2cbf889bd992402b1db | |
parent | Remove extraneous output (diff) | |
download | BouncyCastle.NET-ed25519-5cc0a46f4dbaafd9e3104643ec1fa5bf69ac5a6c.tar.xz |
Name constraint validation updates from bc-java
-rw-r--r-- | crypto/BouncyCastle.Android.csproj | 3 | ||||
-rw-r--r-- | crypto/BouncyCastle.csproj | 3 | ||||
-rw-r--r-- | crypto/BouncyCastle.iOS.csproj | 3 | ||||
-rw-r--r-- | crypto/crypto.csproj | 20 | ||||
-rw-r--r-- | crypto/src/asn1/x500/AttributeTypeAndValue.cs | 60 | ||||
-rw-r--r-- | crypto/src/asn1/x500/Rdn.cs | 104 | ||||
-rw-r--r-- | crypto/src/asn1/x500/style/IetfUtilities.cs | 214 | ||||
-rw-r--r-- | crypto/src/pkix/PkixNameConstraintValidator.cs | 206 | ||||
-rw-r--r-- | crypto/src/pkix/Rfc3280CertPathUtilities.cs | 24 | ||||
-rw-r--r-- | crypto/test/UnitTests.csproj | 1 | ||||
-rw-r--r-- | crypto/test/src/test/PkixNameConstraintsTest.cs | 34 | ||||
-rw-r--r-- | crypto/test/src/test/nist/NistCertPathTest.cs | 139 | ||||
-rw-r--r-- | crypto/test/src/test/nist/NistCertPathTest2.cs | 499 |
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); + } + } +} |