diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/TimeoutTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/TimeoutTests.cs index b44e22d02980fc..bda193030e40a5 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/TimeoutTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/TimeoutTests.cs @@ -17,55 +17,51 @@ public static class TimeoutTests [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)] 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}"); - } + RetryHelper.Execute(() => { + 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(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}"); + } + }); } [Theory] @@ -182,85 +178,83 @@ public static void RevocationCheckingMaximum(PkiOptions pkiOptions) [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)] public static void RevocationCheckingNegativeTimeout(PkiOptions pkiOptions) { - 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); + 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 = RevocationResponder.DelayedActionsFlag.All; + X509Chain chain = holder.Chain; + responder.ResponseDelay = delay; + responder.DelayedActions = RevocationResponder.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.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; + chain.ChainPolicy.DisableCertificateDownloads = true; - Assert.True(Retry(() => chain.Build(endEntityCert)), $"chain.Build; Chain status: {chain.AllStatusFlags()}"); - } + Assert.True(chain.Build(endEntityCert), $"chain.Build; Chain status: {chain.AllStatusFlags()}"); + } + }); } [Fact] [PlatformSpecific(TestPlatforms.Linux)] public static void AiaFetchDelayed() { - CertificateAuthority.BuildPrivatePki( - PkiOptions.OcspEverywhere, - out RevocationResponder responder, - out CertificateAuthority rootAuthority, - out CertificateAuthority intermediateAuthority, - out X509Certificate2 endEntityCert, - nameof(AiaFetchDelayed)); + RetryHelper.Execute(() => { + CertificateAuthority.BuildPrivatePki( + PkiOptions.OcspEverywhere, + out RevocationResponder responder, + out CertificateAuthority rootAuthority, + out CertificateAuthority intermediateAuthority, + out X509Certificate2 endEntityCert, + nameof(AiaFetchDelayed)); + + 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(1); - 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(1); + X509Chain chain = holder.Chain; + responder.ResponseDelay = delay; + responder.DelayedActions = RevocationResponder.DelayedActionsFlag.All; - X509Chain chain = holder.Chain; - responder.ResponseDelay = delay; - responder.DelayedActions = RevocationResponder.DelayedActionsFlag.All; + chain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromSeconds(15); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.CustomTrustStore.Add(rootCert); + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - chain.ChainPolicy.UrlRetrievalTimeout = TimeSpan.FromSeconds(15); - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - chain.ChainPolicy.CustomTrustStore.Add(rootCert); - chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + Stopwatch watch = Stopwatch.StartNew(); + Assert.True(chain.Build(endEntityCert), GetFlags(chain, endEntityCert.Thumbprint).ToString()); + watch.Stop(); - Stopwatch watch = Stopwatch.StartNew(); - Assert.True( - Retry(() => - { - watch.Restart(); - return chain.Build(endEntityCert); - }), - GetFlags(chain, endEntityCert.Thumbprint).ToString()); - watch.Stop(); - - Assert.True(watch.Elapsed >= delay, $"watch.Elapsed: {watch.Elapsed}"); - } + Assert.True(watch.Elapsed >= delay, $"watch.Elapsed: {watch.Elapsed}"); + } + }); } [Fact] @@ -308,18 +302,5 @@ private static X509ChainStatusFlags GetFlags(X509Chain chain, string thumbprint) chain.ChainElements.OfType(). Single(e => e.Certificate.Thumbprint == thumbprint). ChainElementStatus.Aggregate((X509ChainStatusFlags)0, (a, e) => a | e.Status); - - private static bool Retry(Func body, int times = 3) - { - for (int attempt = 0; attempt < times; attempt++) - { - if (body()) - { - return true; - } - } - - return false; - } } }