From a229987cb3d2383c2c563929f684919f0f2bb279 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Wed, 22 Jul 2026 16:26:08 -0700 Subject: [PATCH 1/4] Make AiaCompletionHasLimits handle Windows variance --- .../RevocationTests/AiaTests.cs | 69 ++++++++++++++----- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs index 8e1a66a564a975..28f9b4e5b708f3 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs @@ -220,7 +220,7 @@ public static void AiaIgnoresCertOverLimit() [Fact] public static void AiaCompletionHasLimits() { - const int IntermediateCount = 6; + const int IntermediateCount = 8; CertificateAuthority.BuildPrivatePki( PkiOptions.AllRevocation, @@ -260,25 +260,60 @@ public static void AiaCompletionHasLimits() // EE, intermediate0 (AIA), intermediate1 (ExtraStore), intermediate2 (AIA). AssertExtensions.TrueExpression(chain.Build(endEntity)); - Assert.Equal(4, chain.ChainElements.Count); - AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); - CloneIntoExtraStore(chain, 1); - CloneIntoExtraStore(chain, 3); - holder.DisposeChainElements(); - - // Previous 4 plus intermediate3 and intermediate4. - AssertExtensions.TrueExpression(chain.Build(endEntity)); - Assert.Equal(6, chain.ChainElements.Count); - AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); - - CloneIntoExtraStore(chain, 4); - CloneIntoExtraStore(chain, 5); - holder.DisposeChainElements(); + // Current Windows only allows 2, by black box testing, but 3 does seem to happen + // on some builds. So, variant test for it being over-sized + if (chain.ChainElements.Count == 5) + { + // The ones described above, plus intermediate3(AIA). + AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + + CloneIntoExtraStore(chain, 1); + CloneIntoExtraStore(chain, 3); + CloneIntoExtraStore(chain, 4); + holder.DisposeChainElements(); + + // Previous 5 plus intermediate4, intermediate5, and intermediate6. + AssertExtensions.TrueExpression(chain.Build(endEntity)); + Assert.Equal(8, chain.ChainElements.Count); + AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + + CloneIntoExtraStore(chain, 5); + CloneIntoExtraStore(chain, 6); + CloneIntoExtraStore(chain, 7); + holder.DisposeChainElements(); + } + else + { + Assert.Equal(4, chain.ChainElements.Count); + AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + + CloneIntoExtraStore(chain, 1); + CloneIntoExtraStore(chain, 3); + holder.DisposeChainElements(); + + // Previous 4 plus intermediate3 and intermediate4. + AssertExtensions.TrueExpression(chain.Build(endEntity)); + Assert.Equal(6, chain.ChainElements.Count); + AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + + CloneIntoExtraStore(chain, 4); + CloneIntoExtraStore(chain, 5); + holder.DisposeChainElements(); + + // Previous 6 plus intermediate5 and intermediate6. + AssertExtensions.TrueExpression(chain.Build(endEntity)); + Assert.Equal(8, chain.ChainElements.Count); + AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + + CloneIntoExtraStore(chain, 6); + CloneIntoExtraStore(chain, 7); + holder.DisposeChainElements(); + } - // AIA fetches intermediate5 and root, chain finishes. + // AIA fetches intermediate7 and root, chain finishes. AssertExtensions.TrueExpression(chain.Build(endEntity)); - Assert.Equal(8, chain.ChainElements.Count); + Assert.Equal(10, chain.ChainElements.Count); Assert.Equal(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); } finally From 907ef98d5618e6ed5a0a13f8aa5d8d6208cf9911 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 23 Jul 2026 09:46:03 -0700 Subject: [PATCH 2/4] On retry, generate a whole new PKI --- .../RevocationTests/AiaTests.cs | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs index 28f9b4e5b708f3..f76738247a549a 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs @@ -222,24 +222,24 @@ public static void AiaCompletionHasLimits() { const int IntermediateCount = 8; - CertificateAuthority.BuildPrivatePki( - PkiOptions.AllRevocation, - out RevocationResponder responder, - out CertificateAuthority root, - out CertificateAuthority[] intermediates, - out X509Certificate2 endEntity, - intermediateAuthorityCount: IntermediateCount, - pkiOptionsInSubject: false, - testName: nameof(AiaCompletionHasLimits)); - - using (responder) - using (root) - using (endEntity) - { - try + RetryHelper.Execute( + () => { - RetryHelper.Execute( - () => + CertificateAuthority.BuildPrivatePki( + PkiOptions.AllRevocation, + out RevocationResponder responder, + out CertificateAuthority root, + out CertificateAuthority[] intermediates, + out X509Certificate2 endEntity, + intermediateAuthorityCount: IntermediateCount, + pkiOptionsInSubject: false, + testName: nameof(AiaCompletionHasLimits)); + + using (responder) + using (root) + using (endEntity) + { + try { using (ChainHolder holder = new ChainHolder()) { @@ -324,22 +324,22 @@ public static void AiaCompletionHasLimits() } } } - }); - } - finally - { - foreach (CertificateAuthority intermediate in intermediates) - { - intermediate.Dispose(); + } + finally + { + foreach (CertificateAuthority intermediate in intermediates) + { + intermediate.Dispose(); + } + } } - } + }); - static void CloneIntoExtraStore(X509Chain chain, int index) - { - ReadOnlySpan source = chain.ChainElements[index].Certificate.RawDataMemory.Span; - X509Certificate2 cert = X509CertificateLoader.LoadCertificate(source); - chain.ChainPolicy.ExtraStore.Add(cert); - } + static void CloneIntoExtraStore(X509Chain chain, int index) + { + ReadOnlySpan source = chain.ChainElements[index].Certificate.RawDataMemory.Span; + X509Certificate2 cert = X509CertificateLoader.LoadCertificate(source); + chain.ChainPolicy.ExtraStore.Add(cert); } } } From 8f29219cee7dfc7195aaf1740efd0413e2d20089 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 23 Jul 2026 09:54:32 -0700 Subject: [PATCH 3/4] Conditionalize Dispose --- .../tests/X509Certificates/RevocationTests/AiaTests.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs index f76738247a549a..549bba71291ee4 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs @@ -327,9 +327,12 @@ public static void AiaCompletionHasLimits() } finally { - foreach (CertificateAuthority intermediate in intermediates) + if (intermediates is not null) { - intermediate.Dispose(); + foreach (CertificateAuthority intermediate in intermediates) + { + intermediate.Dispose(); + } } } } From 2ddcc9287e78ee44a48f9c0295b44bbb11a75f0d Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 23 Jul 2026 10:23:27 -0700 Subject: [PATCH 4/4] Ensure different PKI-name on retry --- .../tests/X509Certificates/RevocationTests/AiaTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs index 549bba71291ee4..32a48fb77b8d4f 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs @@ -221,10 +221,13 @@ public static void AiaIgnoresCertOverLimit() public static void AiaCompletionHasLimits() { const int IntermediateCount = 8; + int iteration = 0; RetryHelper.Execute( () => { + iteration++; + CertificateAuthority.BuildPrivatePki( PkiOptions.AllRevocation, out RevocationResponder responder, @@ -233,7 +236,7 @@ public static void AiaCompletionHasLimits() out X509Certificate2 endEntity, intermediateAuthorityCount: IntermediateCount, pkiOptionsInSubject: false, - testName: nameof(AiaCompletionHasLimits)); + testName: $"{nameof(AiaCompletionHasLimits)}_{iteration}"); using (responder) using (root)