-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
I did ran into this while working on #47729 and #46837.
related to #47680 and #47580 where users reported issues with certificate chain in SslStream.
Essentially I have private chain of certificates, I add CAs to to StoreName.CertificateAuthority and subsequent X509Chain.Build() may or may not see them. At this point I'm not sure if the issue is with the store or chain. When I add Sleep(5000) between store.Add() and chain.Build() I usually get expected result. That makes it very difficult to write reliable code. For the test I was working on I added retry loop but that is not great solution either IMHO.
This is essential for SslStream's ability to send correct chain from server or when client certificate authentication is used.
I have test in #48014 to demonstrate the behavior.
System.Security.Cryptography.X509Certificates.Tests.X509StoreTests.CertificatesUsableAfterAdd [STARTING]
System.Security.Cryptography.X509Certificates.Tests.X509StoreTests.CertificatesUsableAfterAdd [FAIL]
Assert.Equal() Failure
Expected: 3
Actual: 1
Stack Trace:
/home/furt/github/wfurt-runtime/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs(617,0): at System.Security.Cryptography.X509Certificates.Tests.X509StoreTests.CertificatesUsableAfterAdd()
Output:
Test result after sleep is True and 3
System.Security.Cryptography.X509Certificates.Tests.X509StoreTests.CertificatesUsableAfterAdd [FINISHED] Time: 5.23547s
here is relevant fragment from #48014
using (X509Store store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser))
{
// add chain certificate so we can construct chain since there is no way how to pass intermediates directly.
store.Open(OpenFlags.ReadWrite);
store.Add(intermediate.CloneIssuerCert());
store.Add(root.CloneIssuerCert());
store.Close();
}
using(....)
{
X509Chain chain = new X509Chain();
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.DisableCertificateDownloads = true;
bool result = chain.Build(endEntity);
int count = result ? chain.ChainElements.Count : -1;
if (!result || count != 3)
{
Thread.Sleep(5000);
bool result2 = chain.Build(endEntity);
_output.WriteLine("Test result after sleep is {0} and {1}", result2, chain.ChainElements?.Count);
}
CleanupCertificates(MethodBase.GetCurrentMethod().Name);
Assert.True(result, "chain was not built");
Assert.Equal(3, count);one more note, that on macOS this seems to always fail. I don't know at this point if this is just timing e.g. needs more time or if the StoreName.CertificateAuthority is ignored or if it is something else. It would be great if this works consistent on all platforms.