diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.c index fa3055efb3d224..76dfb3e088efdf 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.c @@ -166,7 +166,8 @@ static void MergeStatusCodes(CFTypeRef key, CFTypeRef value, void* context) *pStatus |= PAL_X509ChainNotValidForUsage; else if (CFEqual(keyString, CFSTR("AnchorTrusted"))) *pStatus |= PAL_X509ChainUntrustedRoot; - else if (CFEqual(keyString, CFSTR("BasicConstraints"))) + else if (CFEqual(keyString, CFSTR("BasicConstraints")) || CFEqual(keyString, CFSTR("BasicConstraintsCA")) || + CFEqual(keyString, CFSTR("BasicConstraintsPathLen"))) *pStatus |= PAL_X509ChainInvalidBasicConstraints; else if (CFEqual(keyString, CFSTR("UsageConstraints"))) *pStatus |= PAL_X509ChainExplicitDistrust; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs index d6acb4c035691f..2fb3882674524b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs @@ -201,6 +201,86 @@ void CheckChain() } } + [Fact] + public static void BasicConstraints_ExceedMaximumPathLength() + { + X509Extension[] rootExtensions = new [] { + new X509BasicConstraintsExtension( + certificateAuthority: true, + hasPathLengthConstraint: true, + pathLengthConstraint: 0, + critical: true) + }; + + X509Extension[] intermediateExtensions = new [] { + new X509BasicConstraintsExtension( + certificateAuthority: true, + hasPathLengthConstraint: true, + pathLengthConstraint: 0, + critical: true) + }; + + TestDataGenerator.MakeTestChain4( + out X509Certificate2 endEntityCert, + out X509Certificate2 intermediateCert1, + out X509Certificate2 intermediateCert2, + out X509Certificate2 rootCert, + rootExtensions: rootExtensions, + intermediateExtensions: intermediateExtensions); + + using (endEntityCert) + using (intermediateCert1) + using (intermediateCert2) + using (rootCert) + using (ChainHolder chainHolder = new ChainHolder()) + { + X509Chain chain = chainHolder.Chain; + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chain.ChainPolicy.VerificationTime = endEntityCert.NotBefore.AddSeconds(1); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.CustomTrustStore.Add(rootCert); + chain.ChainPolicy.ExtraStore.Add(intermediateCert1); + chain.ChainPolicy.ExtraStore.Add(intermediateCert2); + + Assert.False(chain.Build(endEntityCert)); + Assert.Equal(X509ChainStatusFlags.InvalidBasicConstraints, chain.AllStatusFlags()); + } + } + + [Fact] + public static void BasicConstraints_ViolatesCaFalse() + { + X509Extension[] intermediateExtensions = new [] { + new X509BasicConstraintsExtension( + certificateAuthority: false, + hasPathLengthConstraint: false, + pathLengthConstraint: 0, + critical: true) + }; + + TestDataGenerator.MakeTestChain3( + out X509Certificate2 endEntityCert, + out X509Certificate2 intermediateCert, + out X509Certificate2 rootCert, + intermediateExtensions: intermediateExtensions); + + using (endEntityCert) + using (intermediateCert) + using (rootCert) + using (ChainHolder chainHolder = new ChainHolder()) + { + X509Chain chain = chainHolder.Chain; + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chain.ChainPolicy.VerificationTime = endEntityCert.NotBefore.AddSeconds(1); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.CustomTrustStore.Add(rootCert); + chain.ChainPolicy.ExtraStore.Add(intermediateCert); + + Assert.False(chain.Build(endEntityCert)); + Assert.Equal(X509ChainStatusFlags.InvalidBasicConstraints, chain.AllStatusFlags()); + } + } + [Fact] public static void TestInvalidAia() { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestDataGenerator.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestDataGenerator.cs index 40412515dc7bc7..4e972ae3ea5b8f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestDataGenerator.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestDataGenerator.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; + namespace System.Security.Cryptography.X509Certificates.Tests { internal static class TestDataGenerator @@ -9,7 +11,10 @@ internal static class TestDataGenerator internal static void MakeTestChain3( out X509Certificate2 endEntityCert, out X509Certificate2 intermediateCert, - out X509Certificate2 rootCert) + out X509Certificate2 rootCert, + IEnumerable endEntityExtensions = null, + IEnumerable intermediateExtensions = null, + IEnumerable rootExtensions = null) { using (RSA rootKey = RSA.Create()) using (RSA intermediateKey = RSA.Create()) @@ -23,7 +28,12 @@ internal static void MakeTestChain3( }; Span certs = new X509Certificate2[keys.Length]; - MakeTestChain(keys, certs); + MakeTestChain( + keys, + certs, + endEntityExtensions, + intermediateExtensions, + rootExtensions); endEntityCert = certs[0]; intermediateCert = certs[1]; @@ -31,33 +41,81 @@ internal static void MakeTestChain3( } } + + internal static void MakeTestChain4( + out X509Certificate2 endEntityCert, + out X509Certificate2 intermediateCert1, + out X509Certificate2 intermediateCert2, + out X509Certificate2 rootCert, + IEnumerable endEntityExtensions = null, + IEnumerable intermediateExtensions = null, + IEnumerable rootExtensions = null) + { + using (RSA rootKey = RSA.Create()) + using (RSA intermediateKey = RSA.Create()) + using (RSA endEntityKey = RSA.Create()) + { + ReadOnlySpan keys = new[] + { + rootKey, + intermediateKey, + intermediateKey, + endEntityKey, + }; + + Span certs = new X509Certificate2[keys.Length]; + MakeTestChain( + keys, + certs, + endEntityExtensions, + intermediateExtensions, + rootExtensions); + + endEntityCert = certs[0]; + intermediateCert1 = certs[1]; + intermediateCert2 = certs[2]; + rootCert = certs[3]; + } + } + internal static void MakeTestChain( ReadOnlySpan keys, Span certs, - string eeName = "CN=End-Entity", - OidCollection eeEku = null) + IEnumerable endEntityExtensions, + IEnumerable intermediateExtensions, + IEnumerable rootExtensions) { if (keys.Length < 2) throw new ArgumentException(nameof(keys)); if (keys.Length != certs.Length) throw new ArgumentException(nameof(certs)); - if (string.IsNullOrEmpty(eeName)) - throw new ArgumentOutOfRangeException(nameof(eeName)); - - var caUnlimited = new X509BasicConstraintsExtension(true, false, 0, true); - var eeConstraint = new X509BasicConstraintsExtension(false, false, 0, true); - var caUsage = new X509KeyUsageExtension( - X509KeyUsageFlags.CrlSign | - X509KeyUsageFlags.KeyCertSign | - X509KeyUsageFlags.DigitalSignature, - false); - - var eeUsage = new X509KeyUsageExtension( + rootExtensions ??= new X509Extension[] { + new X509BasicConstraintsExtension(true, false, 0, true), + new X509KeyUsageExtension( + X509KeyUsageFlags.CrlSign | + X509KeyUsageFlags.KeyCertSign | + X509KeyUsageFlags.DigitalSignature, + false) + }; + + intermediateExtensions ??= new X509Extension[] { + new X509BasicConstraintsExtension(true, false, 0, true), + new X509KeyUsageExtension( + X509KeyUsageFlags.CrlSign | + X509KeyUsageFlags.KeyCertSign | + X509KeyUsageFlags.DigitalSignature, + false) + }; + + endEntityExtensions ??= new X509Extension[] { + new X509BasicConstraintsExtension(false, false, 0, true), + new X509KeyUsageExtension( X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation | X509KeyUsageFlags.KeyEncipherment, - false); + false) + }; TimeSpan notBeforeInterval = TimeSpan.FromDays(30); TimeSpan notAfterInterval = TimeSpan.FromDays(90); @@ -76,14 +134,20 @@ internal static void MakeTestChain( hashAlgorithm, signaturePadding); - rootReq.CertificateExtensions.Add(caUnlimited); - rootReq.CertificateExtensions.Add(caUsage); + foreach (X509Extension extension in rootExtensions) + { + rootReq.CertificateExtensions.Add(extension); + } + + X509SignatureGenerator lastGenerator = X509SignatureGenerator.CreateForRSA(keys[rootIndex], RSASignaturePadding.Pkcs1); + X500DistinguishedName lastSubject = rootReq.SubjectName; - X509Certificate2 lastWithKey = rootReq.CreateSelfSigned( + certs[rootIndex] = rootReq.Create( + lastSubject, + lastGenerator, eeStart - (notBeforeInterval * rootIndex), - eeEnd + (notAfterInterval * rootIndex)); - - certs[rootIndex] = new X509Certificate2(lastWithKey.RawData); + eeEnd + (notAfterInterval * rootIndex), + CreateSerial()); int presentationNumber = 0; @@ -97,41 +161,41 @@ internal static void MakeTestChain( hashAlgorithm, signaturePadding); - intermediateReq.CertificateExtensions.Add(caUnlimited); - intermediateReq.CertificateExtensions.Add(caUsage); - - // Leave serialBuf[0] as 0 to avoid a realloc in the signer - RandomNumberGenerator.Fill(serialBuf.AsSpan(1)); + foreach (X509Extension extension in intermediateExtensions) + { + intermediateReq.CertificateExtensions.Add(extension); + } certs[i] = intermediateReq.Create( - lastWithKey, + lastSubject, + lastGenerator, eeStart - (notBeforeInterval * i), eeEnd + (notAfterInterval * i), - serialBuf); + CreateSerial()); - lastWithKey.Dispose(); - lastWithKey = certs[i].CopyWithPrivateKey(keys[i]); + lastSubject = intermediateReq.SubjectName; + lastGenerator = X509SignatureGenerator.CreateForRSA(keys[i], RSASignaturePadding.Pkcs1); } CertificateRequest eeReq = new CertificateRequest( - eeName, + "CN=End-Entity", keys[0], hashAlgorithm, signaturePadding); - eeReq.CertificateExtensions.Add(eeConstraint); - eeReq.CertificateExtensions.Add(eeUsage); - - if (eeEku != null) + foreach (X509Extension extension in endEntityExtensions) { - eeReq.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(eeEku, false)); + eeReq.CertificateExtensions.Add(extension); } - // Leave serialBuf[0] as 0 to avoid a realloc in the signer - RandomNumberGenerator.Fill(serialBuf.AsSpan(1)); + certs[0] = eeReq.Create(lastSubject, lastGenerator, eeStart, eeEnd, CreateSerial()); + } - certs[0] = eeReq.Create(lastWithKey, eeStart, eeEnd, serialBuf); - lastWithKey.Dispose(); + private static byte[] CreateSerial() + { + byte[] bytes = new byte[8]; + RandomNumberGenerator.Fill(bytes); + return bytes; } } }