public static void VerifyCrlCache() { string crlDirectory = PersistedFiles.GetUserFeatureDirectory("cryptography", "crls"); string crlFile = Path.Combine(crlDirectory, MicrosoftDotComRootCrlFilename); Directory.CreateDirectory(crlDirectory); File.Delete(crlFile); using (var microsoftDotComIssuer = new X509Certificate2(TestData.MicrosoftDotComIssuerBytes)) using (var microsoftDotComRoot = new X509Certificate2(TestData.MicrosoftDotComRootBytes)) using (var unrelated = new X509Certificate2(TestData.DssCer)) using (var chainHolder = new ChainHolder()) { X509Chain chain = chainHolder.Chain; chain.ChainPolicy.ExtraStore.Add(unrelated); chain.ChainPolicy.ExtraStore.Add(microsoftDotComRoot); // The very start of the CRL period. chain.ChainPolicy.VerificationTime = new DateTime(2015, 6, 17, 0, 0, 0, DateTimeKind.Utc); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EndCertificateOnly; chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.AllowUnknownCertificateAuthority; bool valid = chain.Build(microsoftDotComIssuer); Assert.True(valid, "Precondition: Chain builds with no revocation checks"); int initialErrorCount = chain.ChainStatus.Length; Assert.InRange(initialErrorCount, 0, 1); X509ChainStatusFlags initialFlags = chain.AllStatusFlags(); if (initialFlags != X509ChainStatusFlags.NoError) { Assert.Equal(X509ChainStatusFlags.UntrustedRoot, initialFlags); } chainHolder.DisposeChainElements(); chain.ChainPolicy.RevocationMode = X509RevocationMode.Offline; valid = chain.Build(microsoftDotComIssuer); Assert.False(valid, "Chain should not build validly"); const X509ChainStatusFlags UnknownOffline = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; Assert.Equal(initialFlags | UnknownOffline, chain.AllStatusFlags()); File.WriteAllText(crlFile, MicrosoftDotComRootCrlPem, Encoding.ASCII); chainHolder.DisposeChainElements(); valid = chain.Build(microsoftDotComIssuer); Assert.True(valid, "Chain should build validly now"); Assert.Equal(initialErrorCount, chain.ChainStatus.Length); } }
public static void CustomTrustModeWithNoCustomTrustCerts() { TestDataGenerator.MakeTestChain3( out X509Certificate2 endEntityCert, out X509Certificate2 intermediateCert, out X509Certificate2 rootCert); 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; if (PlatformDetection.IsAndroid) { // Android does not support an empty custom root trust Assert.Throws <PlatformNotSupportedException>(() => chain.Build(endEntityCert)); } else { Assert.False(chain.Build(endEntityCert)); Assert.Equal(1, chain.ChainElements.Count); Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); } } }
public static void CustomRootTrustDoesNotTrustIntermediates( bool saveAllInCustomTrustStore, X509ChainStatusFlags chainFlags) { TestDataGenerator.MakeTestChain3( out X509Certificate2 endEntityCert, out X509Certificate2 intermediateCert, out X509Certificate2 rootCert); 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(intermediateCert); if (saveAllInCustomTrustStore) { chain.ChainPolicy.CustomTrustStore.Add(rootCert); } else { chain.ChainPolicy.ExtraStore.Add(rootCert); } Assert.Equal(saveAllInCustomTrustStore, chain.Build(endEntityCert)); Assert.Equal(3, chain.ChainElements.Count); Assert.Equal(chainFlags, chain.AllStatusFlags()); } }
public static void EmptyAiaResponseIsIgnored() { CertificateAuthority.BuildPrivatePki( PkiOptions.AllRevocation, out RevocationResponder responder, out CertificateAuthority root, out CertificateAuthority intermediate, out X509Certificate2 endEntity, pkiOptionsInSubject: false); using (responder) using (root) using (intermediate) using (endEntity) using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 rootCert = root.CloneIssuerCert()) using (X509Certificate2 intermediateCert = intermediate.CloneIssuerCert()) { responder.RespondEmpty = true; X509Chain chain = holder.Chain; chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; Assert.False(chain.Build(endEntity)); X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); Assert.True(chainFlags.HasFlag(X509ChainStatusFlags.PartialChain), $"expected partial chain flags, got {chainFlags}"); } }
public static void MismatchKeyIdentifiers() { X509Extension[] intermediateExtensions = new [] { new X509BasicConstraintsExtension( certificateAuthority: true, hasPathLengthConstraint: false, pathLengthConstraint: 0, critical: true), new X509Extension( "2.5.29.14", "0414C7AC28EFB300F46F9406ED155628A123633E556F".HexToByteArray(), critical: false) }; X509Extension[] endEntityExtensions = new [] { new X509BasicConstraintsExtension( certificateAuthority: false, hasPathLengthConstraint: false, pathLengthConstraint: 0, critical: true), new X509Extension( "2.5.29.35", "30168014A84A6A63047DDDBAE6D139B7A64565EFF3A8ECA1".HexToByteArray(), critical: false) }; TestDataGenerator.MakeTestChain3( out X509Certificate2 endEntityCert, out X509Certificate2 intermediateCert, out X509Certificate2 rootCert, intermediateExtensions: intermediateExtensions, endEntityExtensions: endEntityExtensions); 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); if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { Assert.False(chain.Build(endEntityCert), "chain.Build"); Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); } else { Assert.True(chain.Build(endEntityCert), "chain.Build"); Assert.Equal(3, chain.ChainElements.Count); } } }
public static void BuildInvalidSignatureTwice() { byte[] bytes = (byte[])TestData.MsCertificate.Clone(); bytes[bytes.Length - 1] ^= 0xFF; using (X509Certificate2 cert = new X509Certificate2(bytes)) using (ChainHolder chainHolder = new ChainHolder()) { X509Chain chain = chainHolder.Chain; chain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; int iter = 0; void CheckChain() { iter++; bool valid = chain.Build(cert); X509ChainStatusFlags allFlags = chain.AllStatusFlags(); if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // OSX considers this to be valid because it doesn't report NotSignatureValid, // just PartialChain ("I couldn't find an issuer that made the signature work"), // and PartialChain + AllowUnknownCertificateAuthority == pass. Assert.True(valid, $"Chain is valid on execution {iter}"); Assert.Equal(1, chain.ChainElements.Count); Assert.Equal( X509ChainStatusFlags.PartialChain, allFlags); } else { Assert.False(valid, $"Chain is valid on execution {iter}"); Assert.Equal(3, chain.ChainElements.Count); // Clear UntrustedRoot, if it happened. allFlags &= ~X509ChainStatusFlags.UntrustedRoot; Assert.Equal( X509ChainStatusFlags.NotSignatureValid, allFlags); } chainHolder.DisposeChainElements(); } CheckChain(); CheckChain(); } }
public static void InvalidSelfSignedSignature() { X509ChainStatusFlags expectedFlags; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { expectedFlags = X509ChainStatusFlags.NotSignatureValid; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // For OSX alone expectedFlags here means OR instead of AND. // Because the error code changed in 10.13.4 from UntrustedRoot to PartialChain // and we handle that later in this test. expectedFlags = X509ChainStatusFlags.UntrustedRoot | X509ChainStatusFlags.PartialChain; } else { expectedFlags = X509ChainStatusFlags.NotSignatureValid | X509ChainStatusFlags.UntrustedRoot; } byte[] certBytes = (byte[])TestData.MicrosoftDotComRootBytes.Clone(); // The signature goes up to the very last byte, so flip some bits in it. certBytes[certBytes.Length - 1] ^= 0xFF; using (var cert = new X509Certificate2(certBytes)) using (ChainHolder holder = new ChainHolder()) { X509Chain chain = holder.Chain; X509ChainPolicy policy = chain.ChainPolicy; policy.VerificationTime = cert.NotBefore.AddDays(3); policy.RevocationMode = X509RevocationMode.NoCheck; chain.Build(cert); X509ChainStatusFlags allFlags = chain.AllStatusFlags(); if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // If we're on 10.13.3 or older we get UntrustedRoot. // If we're on 10.13.4 or newer we get PartialChain. // // So make the expectedValue be whichever of those two is set. expectedFlags = (expectedFlags & allFlags); // One of them has to be set. Assert.NotEqual(X509ChainStatusFlags.NoError, expectedFlags); // Continue executing now to ensure that no other unexpected flags were set. } Assert.Equal(expectedFlags, allFlags); } }
public static void RevocationCheckingDelayed(PkiOptions pkiOptions) { CertificateAuthority.BuildPrivatePki( pkiOptions, out RevocationResponder responder, out CertificateAuthority rootAuthority, out CertificateAuthority intermediateAuthority, out X509Certificate2 endEntityCert, nameof(RevocationCheckingDelayed)); using (responder) using (rootAuthority) using (intermediateAuthority) using (endEntityCert) using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 rootCert = rootAuthority.CloneIssuerCert()) using (X509Certificate2 intermediateCert = intermediateAuthority.CloneIssuerCert()) { TimeSpan delay = TimeSpan.FromSeconds(8); X509Chain chain = holder.Chain; responder.ResponseDelay = delay; responder.DelayedActions = RevocationResponder.DelayedActionsFlag.All; // This needs to be greater than delay, but less than 2x delay to ensure // that the time is a timeout for individual fetches, not a running total. chain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromSeconds(15); chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; chain.ChainPolicy.CustomTrustStore.Add(rootCert); chain.ChainPolicy.ExtraStore.Add(intermediateCert); chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; chain.ChainPolicy.DisableCertificateDownloads = true; Stopwatch watch = Stopwatch.StartNew(); Assert.True( Retry(() => { watch.Restart(); return(chain.Build(endEntityCert)); }), $"chain.Build; Chain status: {chain.AllStatusFlags()}"); watch.Stop(); // There should be two network fetches, OCSP/CRL to intermediate to get leaf status, // OCSP/CRL to root to get intermediate statuses. It should take at least 2x the delay // plus other non-network time, so we can at least ensure it took as long as // the delay for each fetch. Assert.True(watch.Elapsed >= delay * 2, $"watch.Elapsed: {watch.Elapsed}"); } }
public static void CustomRootTrustDoesNotTrustIntermediates( bool saveAllInCustomTrustStore, X509ChainStatusFlags chainFlags) { string testName = $"{nameof(CustomRootTrustDoesNotTrustIntermediates)} {saveAllInCustomTrustStore} {chainFlags}"; TestDataGenerator.MakeTestChain3( out X509Certificate2 endEntityCert, out X509Certificate2 intermediateCert, out X509Certificate2 rootCert, testName: testName); 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(intermediateCert); if (saveAllInCustomTrustStore) { chain.ChainPolicy.CustomTrustStore.Add(rootCert); } else { chain.ChainPolicy.ExtraStore.Add(rootCert); } if (PlatformDetection.IsAndroid && !saveAllInCustomTrustStore) { // Android does not support an empty custom root trust // Only self-issued certs are treated as trusted anchors, so building the chain // should through PNSE even though the intermediate cert is added to the store Assert.Throws <PlatformNotSupportedException>(() => chain.Build(endEntityCert)); } else { Assert.Equal(saveAllInCustomTrustStore, chain.Build(endEntityCert)); Assert.Equal(3, chain.ChainElements.Count); Assert.Equal(chainFlags, chain.AllStatusFlags()); } } }
public static void TestLeafCertificateWithUnknownCriticalExtension() { using (RSA key = RSA.Create()) { CertificateRequest certReq = new CertificateRequest( new X500DistinguishedName("CN=Cert"), key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); const string PrecertificatePoisonExtensionOid = "1.3.6.1.4.1.11129.2.4.3"; certReq.CertificateExtensions.Add(new X509Extension( new AsnEncodedData( new Oid(PrecertificatePoisonExtensionOid), new byte[] { 5, 0 }), critical: true)); DateTimeOffset notBefore = DateTimeOffset.UtcNow.AddDays(-1); DateTimeOffset notAfter = notBefore.AddDays(30); using (X509Certificate2 cert = certReq.CreateSelfSigned(notBefore, notAfter)) using (ChainHolder holder = new ChainHolder()) { X509Chain chain = holder.Chain; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; Assert.False(chain.Build(cert)); if (PlatformDetection.IsAndroid) { // Android always unsupported critical extensions as part of building a path, // so errors comes back as PartialChain with no elements Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); Assert.Equal(0, chain.ChainElements.Count); } else { X509ChainElement certElement = chain.ChainElements.OfType <X509ChainElement>().Single(); const X509ChainStatusFlags ExpectedFlag = X509ChainStatusFlags.HasNotSupportedCriticalExtension; X509ChainStatusFlags actualFlags = certElement.AllStatusFlags(); Assert.True((actualFlags & ExpectedFlag) == ExpectedFlag, $"Has expected flag {ExpectedFlag} but was {actualFlags}"); } } } }
public static void TestInvalidAia() { using (RSA key = RSA.Create()) { CertificateRequest rootReq = new CertificateRequest( "CN=Root", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); rootReq.CertificateExtensions.Add( new X509BasicConstraintsExtension(true, false, 0, true)); CertificateRequest certReq = new CertificateRequest( "CN=test", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); certReq.CertificateExtensions.Add( new X509BasicConstraintsExtension(false, false, 0, false)); certReq.CertificateExtensions.Add( new X509Extension( "1.3.6.1.5.5.7.1.1", new byte[] { 5 }, critical: false)); DateTimeOffset notBefore = DateTimeOffset.UtcNow.AddDays(-1); DateTimeOffset notAfter = notBefore.AddDays(30); using (X509Certificate2 root = rootReq.CreateSelfSigned(notBefore, notAfter)) using (X509Certificate2 ee = certReq.Create(root, notBefore, notAfter, root.GetSerialNumber())) { X509Chain chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; Assert.False(chain.Build(ee)); Assert.Equal(1, chain.ChainElements.Count); Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); } } }
public static void CustomTrustModeWithNoCustomTrustCerts() { TestDataGenerator.MakeTestChain3( out X509Certificate2 endEntityCert, out X509Certificate2 intermediateCert, out X509Certificate2 rootCert); 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; Assert.False(chain.Build(endEntityCert)); Assert.Equal(1, chain.ChainElements.Count); Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); } }
public static void BuildInvalidSignatureTwice( X509ChainStatusFlags endEntityErrors, X509ChainStatusFlags intermediateErrors, X509ChainStatusFlags rootErrors) { TestDataGenerator.MakeTestChain3( out X509Certificate2 endEntityCert, out X509Certificate2 intermediateCert, out X509Certificate2 rootCert); X509Certificate2 TamperIfNeeded(X509Certificate2 input, X509ChainStatusFlags flags) { if ((flags & X509ChainStatusFlags.NotSignatureValid) != 0) { X509Certificate2 tampered = TamperSignature(input); input.Dispose(); return(tampered); } return(input); } DateTime RewindIfNeeded(DateTime input, X509Certificate2 cert, X509ChainStatusFlags flags) { if ((flags & X509ChainStatusFlags.NotTimeValid) != 0) { return(cert.NotBefore.AddMinutes(-1)); } return(input); } int expectedCount = 3; DateTime verificationTime = endEntityCert.NotBefore.AddMinutes(1); verificationTime = RewindIfNeeded(verificationTime, endEntityCert, endEntityErrors); verificationTime = RewindIfNeeded(verificationTime, intermediateCert, intermediateErrors); verificationTime = RewindIfNeeded(verificationTime, rootCert, rootErrors); // Replace the certs for the scenario. endEntityCert = TamperIfNeeded(endEntityCert, endEntityErrors); intermediateCert = TamperIfNeeded(intermediateCert, intermediateErrors); rootCert = TamperIfNeeded(rootCert, rootErrors); if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { // For the lower levels, turn NotSignatureValid into PartialChain, // and clear all errors at higher levels. if ((endEntityErrors & X509ChainStatusFlags.NotSignatureValid) != 0) { expectedCount = 1; endEntityErrors &= ~X509ChainStatusFlags.NotSignatureValid; endEntityErrors |= X509ChainStatusFlags.PartialChain; intermediateErrors = X509ChainStatusFlags.NoError; rootErrors = X509ChainStatusFlags.NoError; } else if ((intermediateErrors & X509ChainStatusFlags.NotSignatureValid) != 0) { expectedCount = 2; intermediateErrors &= ~X509ChainStatusFlags.NotSignatureValid; intermediateErrors |= X509ChainStatusFlags.PartialChain; rootErrors = X509ChainStatusFlags.NoError; } else if ((rootErrors & X509ChainStatusFlags.NotSignatureValid) != 0) { rootErrors &= ~X509ChainStatusFlags.NotSignatureValid; // On 10.13+ it becomes PartialChain, and UntrustedRoot goes away. if (PlatformDetection.IsOSX) { rootErrors &= ~X509ChainStatusFlags.UntrustedRoot; rootErrors |= X509ChainStatusFlags.PartialChain; } } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Windows only reports NotTimeValid on the start-of-chain (end-entity in this case) // If it were possible in this suite to get only a higher-level cert as NotTimeValid // without the lower one, that would have resulted in NotTimeNested. intermediateErrors &= ~X509ChainStatusFlags.NotTimeValid; rootErrors &= ~X509ChainStatusFlags.NotTimeValid; } X509ChainStatusFlags expectedAllErrors = endEntityErrors | intermediateErrors | rootErrors; // If PartialChain or UntrustedRoot are the only remaining errors, the chain will succeed. const X509ChainStatusFlags SuccessCodes = X509ChainStatusFlags.UntrustedRoot | X509ChainStatusFlags.PartialChain; bool expectSuccess = (expectedAllErrors & ~SuccessCodes) == 0; using (endEntityCert) using (intermediateCert) using (rootCert) using (ChainHolder chainHolder = new ChainHolder()) { X509Chain chain = chainHolder.Chain; chain.ChainPolicy.VerificationTime = verificationTime; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.ExtraStore.Add(intermediateCert); chain.ChainPolicy.ExtraStore.Add(rootCert); chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.AllowUnknownCertificateAuthority; int i = 0; void CheckChain() { i++; bool valid = chain.Build(endEntityCert); if (expectSuccess) { Assert.True(valid, $"Chain build on iteration {i}"); } else { Assert.False(valid, $"Chain build on iteration {i}"); } Assert.Equal(expectedCount, chain.ChainElements.Count); Assert.Equal(expectedAllErrors, chain.AllStatusFlags()); Assert.Equal(endEntityErrors, chain.ChainElements[0].AllStatusFlags()); if (expectedCount > 2) { Assert.Equal(rootErrors, chain.ChainElements[2].AllStatusFlags()); } if (expectedCount > 1) { Assert.Equal(intermediateErrors, chain.ChainElements[1].AllStatusFlags()); } chainHolder.DisposeChainElements(); } CheckChain(); CheckChain(); } }
public static void RevocationCheckingNegativeTimeout(PkiOptions pkiOptions) { RetryHelper.Execute(() => { CertificateAuthority.BuildPrivatePki( pkiOptions, out RevocationResponder responder, out CertificateAuthority rootAuthority, out CertificateAuthority intermediateAuthority, out X509Certificate2 endEntityCert, nameof(RevocationCheckingNegativeTimeout)); using (responder) using (rootAuthority) using (intermediateAuthority) using (endEntityCert) using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 rootCert = rootAuthority.CloneIssuerCert()) using (X509Certificate2 intermediateCert = intermediateAuthority.CloneIssuerCert()) { // Delay is more than the 15 second default. TimeSpan delay = TimeSpan.FromSeconds(25); X509Chain chain = holder.Chain; responder.ResponseDelay = delay; responder.DelayedActions = DelayedActionsFlag.All; chain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromMinutes(-1); chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; chain.ChainPolicy.CustomTrustStore.Add(rootCert); chain.ChainPolicy.ExtraStore.Add(intermediateCert); chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EndCertificateOnly; chain.ChainPolicy.DisableCertificateDownloads = true; Assert.True(chain.Build(endEntityCert), $"chain.Build; Chain status: {chain.AllStatusFlags()}"); } }); }
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()); } }
public static void RevocationCheckingTimeoutFallbackToOther(DelayedActionsFlag delayFlags) { RetryHelper.Execute(() => { CertificateAuthority.BuildPrivatePki( PkiOptions.AllRevocation, out RevocationResponder responder, out CertificateAuthority rootAuthority, out CertificateAuthority intermediateAuthority, out X509Certificate2 endEntityCert, nameof(RevocationCheckingTimeoutFallbackToOther)); using (responder) using (rootAuthority) using (intermediateAuthority) using (endEntityCert) using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 rootCert = rootAuthority.CloneIssuerCert()) using (X509Certificate2 intermediateCert = intermediateAuthority.CloneIssuerCert()) { X509Chain chain = holder.Chain; responder.ResponseDelay = TimeSpan.FromSeconds(8); responder.DelayedActions = delayFlags; chain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromSeconds(4); chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; chain.ChainPolicy.CustomTrustStore.Add(rootCert); chain.ChainPolicy.ExtraStore.Add(intermediateCert); chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EndCertificateOnly; chain.ChainPolicy.DisableCertificateDownloads = true; Assert.True(chain.Build(endEntityCert), $"chain.Build; Chain status: {chain.AllStatusFlags()}"); } }); }
public static void ChainErrorsAtMultipleLayers() { // These certificates were generated for this test using CertificateRequest // but the netstandard(2.0) version of this test library doesn't have // CertificateRequest available. // // These certificates have been hard-coded to enable the scenario on // netstandard. byte[] endEntityBytes = Encoding.ASCII.GetBytes(@" -----BEGIN CERTIFICATE----- MIIC6DCCAdCgAwIBAgIQAKjmD7+TWUwQN2ucajn9kTANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQD EwxJbnRlcm1lZGlhdGUwHhcNMTkwMzAzMjM1NzA3WhcNMTkwNjAzMjM1NzA3WjAVMRMwEQYDVQQD EwpFbmQtRW50aXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxTybBkpMdQ8IeL1C jG755+ifQfqjNt4+Xhm3pbMi+nCRD68tym1xviUka1hQmx+I1mptswW0Laq1owur0r2KanKoIP2F i2h6orOOdslMFPMWqCuNTU4C7cUxokaWah0R7FihwW+aBeWgxG948Cvt+ByQeR1ns9yo7wa8f8kT IwzOUu0v1Yj5oW5bOn/cmIBE1C5CD5RivPMGUXX8mZ/myNh16dLQqJW5yQt/uvfr7lkNWC0qq+v7 Ely4+X27acwMTdtk4chcr5/bTS5FXV7HVqwhajOmm6WrzagPZBELWKRk2EaJkha/MLrBqNfHExs4 sx2ks+TTclrOrRzG+AUBuQIDAQABozIwMDAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIF4DATBgNV HSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAbrEbiw4gpgWi3SJ+sGrfcWCAldpx 0735hkkYz94OsJjIwWfgQ03pYZwjcnIE4Ln0PU2E52D2ldsJlAE376hpNxdO0X4RLpZVZPEjKGTF v2Rf+d0cpqha5J//mqcTTm7F58JRKyfEQn0pqfxx4VyXeLfEsqYbT3kY7ufK0km3Jst0DGw2AGue MPmiZicaNlXPVO9vyW4s6J23+kol6X8K2rnVht9jagfnOQ990Ux2xXGyDGM4I0pvW1Zo4vid/eli psHHsU9xg0o7L2WXD5qYhD2JCQIVWNRmRZCf1luWlKqUaqWWONMJ44hk8Md+ohxpyCRmbtLRZPzd wlkQzPsc9A== -----END CERTIFICATE-----"); byte[] intermediateBytes = Encoding.ASCII.GetBytes(@" -----BEGIN CERTIFICATE----- MIIC1DCCAbygAwIBAgIPRoY1rB2tMVJeYB4GILkNMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMT CVRlc3QgUm9vdDAeFw0xOTAyMTgyMzU3MDdaFw0yMDAyMTgyMzU3MDdaMBcxFTATBgNVBAMTDElu dGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALxYzEN6nYvQ0TOg/jOF wdBGRUYhTiJpYGFBh9826X5vKlbCS1UAcjFRXmKtJ4WZ8v3peCBPxvVe/1KR38+MWNVtO4B1GBvr qR2T9k1ewgn0lO3i6krnIAhJQ+F94xGcsRAfZjXBh7lOmTE9ZlDhDJWkehBIs5TteiBOfbGDml2S v7x81cmm2o/sDoP1oVGhezOkFtI2/NdZYKxRthnjDywN3W4KFataJFATVv/yq+QjcLEWrXFRpzDE rpVdYmj66kaAnu9D4sHhFqOk1SX3JvcB361stVuUPp2ri75MaaXakweH6X/Yb4nPNV6m1ENwMoDy HqrZrHSK8SpzfhY9aB0CAwEAAaMgMB4wDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAYYwDQYJ KoZIhvcNAQELBQADggEBAC4oJ2SH+Ov4QIMXo7mwGSrwONkdMuKyyM9shZiGEH+zIO9SVuPuvtQG cePR2bijSz2DtjySi+ST8y3Ql7A3isfbXYPDFmnkzKP6hGvLkctc8eO8U1x7ny+QW1max0gm3UA8 CY0IMP8pCHUZH9OX/K0N9L+GItqlBK8G4grJ4o43da2x9L0hIrdauPadaGcJalf8k1ymhJ4VDj7t ueuTl2qTtbBh015GuEld61EBXSBLIUqwOAeFYrNJbC4J2mXgnLTWC380cBf5KWeSdjLYgk2sZ1V4 FKKQecZIhxdlDGzMAbbmEV+2EqS+As2C7+y4dkpG4nnbQe/4AFr8vekHdrI= -----END CERTIFICATE-----"); byte[] rootBytes = Encoding.ASCII.GetBytes(@" -----BEGIN CERTIFICATE----- MIICyjCCAbKgAwIBAgIIKKt3K3rRbvQwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAxMJVGVzdCBS b290MB4XDTE5MDIwNDIzNTcwN1oXDTIxMDIwNDIzNTcwN1owFDESMBAGA1UEAxMJVGVzdCBSb290 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiM7tv4YvqmWYGF1vbeM2cQWV1NVBxKU4 ZK5XEJHZirzE2HCiA0+hI/UD7xnfBrzGQRLsHnp9vfhBi/0wenSIKTckxcGGpuM+JzNoVF97uFSd bKvfIwQZzbdRGyTF1eoQWCylsZsnZOXg8c/yoFhG2TJB38l09RYn+HkMkapQERFKSXPZ7taNVJNb Sedp3l9jO0aVmh9rmJ7taBXBfWDmSWqhkxjkEcbiRxB7z5K8YxZBlHQCLqf43JiCbKIMBHdzTg+N lEBkBGp6T2hoJ4/A1uwvhesjmyqagZrC2NnzOWOxUQ/WujIUfS62ii/yDkP4Jo3745lJ9XXoPbIw AwvWYQIDAQABoyAwHjAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBhjANBgkqhkiG9w0BAQsF AAOCAQEAA/pfswrUzcLP5UfmHgQDc1slJjh0btnkN+4dxCCTLcnteJCTumYw+/82qL+O4t1KlzlS 2Eqgyx0u48YmwDp/5jWAvT8RX8pvV3Prd7T8/dp/ucES7R9r3zF2Rmw5Me9iq1yaLAypGyBGqV1J HAwJjH/eKZ5iuOMhFljs2R5Gh5rRsQjNVUCRsolCds4d1f+76fi2SGaKqkAA4gzg1c71SPTAaUPR ythjxnoCBDVFmwV5opXZj9qIZoUdH92gCVFgMWkxWCYWzyH78uIUzV1oo+KNwK1SCTnfVHcfWRIL tHP28fj0LUop/QFojSZPsaPAW6JvoQ0t4hd6WoyX6z7FsA== -----END CERTIFICATE-----"); using (X509Certificate2 endEntityCert = new X509Certificate2(endEntityBytes)) using (X509Certificate2 intermediateCert = new X509Certificate2(intermediateBytes)) using (X509Certificate2 rootCert = new X509Certificate2(rootBytes)) using (ChainHolder chainHolder = new ChainHolder()) { X509Chain chain = chainHolder.Chain; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.AllowUnknownCertificateAuthority; chain.ChainPolicy.ExtraStore.Add(intermediateCert); chain.ChainPolicy.ExtraStore.Add(rootCert); chain.ChainPolicy.VerificationTime = endEntityCert.NotAfter.AddDays(1); Assert.Equal(false, chain.Build(endEntityCert)); Assert.Equal(3, chain.ChainElements.Count); Assert.Equal(X509ChainStatusFlags.NotTimeValid, chain.ChainElements[0].AllStatusFlags()); Assert.Equal(X509ChainStatusFlags.NoError, chain.ChainElements[1].AllStatusFlags()); Assert.Equal(X509ChainStatusFlags.UntrustedRoot, chain.ChainElements[2].AllStatusFlags()); Assert.Equal( X509ChainStatusFlags.NotTimeValid | X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); } }
public static void DisableAiaOptionWorks() { CertificateAuthority.BuildPrivatePki( PkiOptions.AllRevocation, out RevocationResponder responder, out CertificateAuthority root, out CertificateAuthority intermediate, out X509Certificate2 endEntity, pkiOptionsInSubject: false); using (responder) using (root) using (intermediate) using (endEntity) using (ChainHolder holder = new ChainHolder()) using (X509Certificate2 rootCert = root.CloneIssuerCert()) using (X509Certificate2 intermediateCert = intermediate.CloneIssuerCert()) using (var cuCaStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser)) { cuCaStore.Open(OpenFlags.ReadWrite); X509Chain chain = holder.Chain; chain.ChainPolicy.DisableCertificateDownloads = true; chain.ChainPolicy.CustomTrustStore.Add(rootCert); chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; Assert.False(chain.Build(endEntity), "Chain build with no intermediate, AIA disabled"); // If a previous run of this test leaves contamination in the CU\CA store on Windows // the Windows chain engine will match the bad issuer and report NotSignatureValid instead // of PartialChain. X509ChainStatusFlags chainFlags = chain.AllStatusFlags(); if (chainFlags.HasFlag(X509ChainStatusFlags.NotSignatureValid)) { Assert.Equal(3, chain.ChainElements.Count); foreach (X509Certificate2 storeCert in cuCaStore.Certificates) { if (storeCert.Subject.Equals(intermediateCert.Subject)) { cuCaStore.Remove(storeCert); } storeCert.Dispose(); } holder.DisposeChainElements(); // Try again, with no caching side effect. Assert.False(chain.Build(endEntity), "Chain build 2 with no intermediate, AIA disabled"); } Assert.Equal(1, chain.ChainElements.Count); Assert.Contains(X509ChainStatusFlags.PartialChain, chain.ChainStatus.Select(s => s.Status)); holder.DisposeChainElements(); // macOS doesn't like our revocation responder, so disable revocation checks there. if (PlatformDetection.IsOSX) { chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; } chain.ChainPolicy.ExtraStore.Add(intermediateCert); Assert.True(chain.Build(endEntity), "Chain build with intermediate, AIA disabled"); Assert.Equal(3, chain.ChainElements.Count); Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); holder.DisposeChainElements(); chain.ChainPolicy.DisableCertificateDownloads = false; chain.ChainPolicy.ExtraStore.Clear(); Assert.True(chain.Build(endEntity), "Chain build with no intermediate, AIA enabled"); Assert.Equal(3, chain.ChainElements.Count); Assert.Equal(X509ChainStatusFlags.NoError, chain.AllStatusFlags()); cuCaStore.Remove(intermediateCert); } }
public static void VerifyWithRevocation() { using (var cert = new X509Certificate2(Path.Combine("TestData", "MS.cer"))) using (var onlineChainHolder = new ChainHolder()) using (var offlineChainHolder = new ChainHolder()) { X509Chain onlineChain = onlineChainHolder.Chain; X509Chain offlineChain = offlineChainHolder.Chain; onlineChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; onlineChain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); onlineChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; onlineChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; // Attempt the online test a couple of times, in case there was just a CRL // download failure. const int RetryLimit = 3; bool valid = false; for (int i = 0; i < RetryLimit; i++) { valid = onlineChain.Build(cert); if (valid) { break; } for (int j = 0; j < onlineChain.ChainElements.Count; j++) { X509ChainStatusFlags chainFlags = onlineChain.AllStatusFlags(); const X509ChainStatusFlags WontCheck = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.UntrustedRoot; if (chainFlags == WontCheck) { Console.WriteLine($"{nameof(VerifyWithRevocation)}: online chain failed with {{{chainFlags}}}, skipping"); return; } X509ChainElement chainElement = onlineChain.ChainElements[j]; // Since `NoError` gets mapped as the empty array, just look for non-empty arrays if (chainElement.ChainElementStatus.Length > 0) { X509ChainStatusFlags allFlags = chainElement.AllStatusFlags(); Console.WriteLine( $"{nameof(VerifyWithRevocation)}: online attempt {i} - errors at depth {j}: {allFlags}"); } chainElement.Certificate.Dispose(); } Thread.Sleep(1000); // For network flakiness } if (TestEnvironmentConfiguration.RunManualTests) { Assert.True(valid, $"Online Chain Built Validly within {RetryLimit} tries"); } else if (!valid) { Console.WriteLine($"SKIP [{nameof(VerifyWithRevocation)}]: Chain failed to build within {RetryLimit} tries."); return; } // Since the network was enabled, we should get the whole chain. Assert.Equal(3, onlineChain.ChainElements.Count); Assert.Equal(0, onlineChain.ChainElements[0].ChainElementStatus.Length); Assert.Equal(0, onlineChain.ChainElements[1].ChainElementStatus.Length); // The root CA is not expected to be installed on everyone's machines, // so allow for it to report UntrustedRoot, but nothing else.. X509ChainStatus[] rootElementStatus = onlineChain.ChainElements[2].ChainElementStatus; if (rootElementStatus.Length != 0) { Assert.Equal(1, rootElementStatus.Length); Assert.Equal(X509ChainStatusFlags.UntrustedRoot, rootElementStatus[0].Status); } // Now that everything is cached, try again in Offline mode. offlineChain.ChainPolicy.VerificationFlags = onlineChain.ChainPolicy.VerificationFlags; offlineChain.ChainPolicy.VerificationTime = onlineChain.ChainPolicy.VerificationTime; offlineChain.ChainPolicy.RevocationMode = X509RevocationMode.Offline; offlineChain.ChainPolicy.RevocationFlag = onlineChain.ChainPolicy.RevocationFlag; valid = offlineChain.Build(cert); Assert.True(valid, "Offline Chain Built Validly"); // Everything should look just like the online chain: Assert.Equal(onlineChain.ChainElements.Count, offlineChain.ChainElements.Count); for (int i = 0; i < offlineChain.ChainElements.Count; i++) { X509ChainElement onlineElement = onlineChain.ChainElements[i]; X509ChainElement offlineElement = offlineChain.ChainElements[i]; Assert.Equal(onlineElement.ChainElementStatus, offlineElement.ChainElementStatus); Assert.Equal(onlineElement.Certificate, offlineElement.Certificate); } } }