fix certificate ctx on windows and macOS (#39818)
authorTomas Weinfurt <tweinfurt@yahoo.com>
Mon, 10 Aug 2020 19:02:08 +0000 (12:02 -0700)
committerGitHub <noreply@github.com>
Mon, 10 Aug 2020 19:02:08 +0000 (12:02 -0700)
* fix certificate ctx on windows

* fix linux

* fix osx pal

* feedback from review

* feedback from review

12 files changed:
src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs
src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeFreeSslCredentials.cs
src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Linux.cs
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.OSX.cs
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Windows.cs
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs
src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs

index ae52439d1629357b7fdfaa5d915e4b82a6de679a..06ecc8d9ed1d721a08e485feffd1772f0fec1820 100644 (file)
@@ -118,9 +118,9 @@ namespace System.Net
                     SetProtocols(sslContext, credential.Protocols);
                 }
 
-                if (credential.Certificate != null)
+                if (credential.CertificateContext != null)
                 {
-                    SetCertificate(sslContext, credential.Certificate);
+                    SetCertificate(sslContext, credential.CertificateContext);
                 }
 
                 Interop.AppleCrypto.SslBreakOnServerAuth(sslContext, true);
@@ -315,68 +315,34 @@ namespace System.Net
             Interop.AppleCrypto.SslSetMaxProtocolVersion(sslContext, maxProtocolId);
         }
 
-        private static void SetCertificate(SafeSslHandle sslContext, X509Certificate2 certificate)
+        private static void SetCertificate(SafeSslHandle sslContext, SslStreamCertificateContext context)
         {
             Debug.Assert(sslContext != null, "sslContext != null");
-            Debug.Assert(certificate != null, "certificate != null");
-            Debug.Assert(certificate.HasPrivateKey, "certificate.HasPrivateKey");
 
-            X509Chain chain = TLSCertificateExtensions.BuildNewChain(
-                certificate,
-                includeClientApplicationPolicy: false)!;
 
-            using (chain)
-            {
-                X509ChainElementCollection elements = chain.ChainElements;
-
-                // We need to leave off the EE (first) and root (last) certificate from the intermediates.
-                X509Certificate2[] intermediateCerts = elements.Count < 3
-                    ? Array.Empty<X509Certificate2>()
-                    : new X509Certificate2[elements.Count - 2];
-
-                // Build an array which is [
-                //   SecIdentityRef for EE cert,
-                //   SecCertificateRef for intermed0,
-                //   SecCertificateREf for intermed1,
-                //   ...
-                // ]
-                IntPtr[] ptrs = new IntPtr[intermediateCerts.Length + 1];
-
-                for (int i = 0; i < intermediateCerts.Length; i++)
-                {
-                    X509Certificate2 intermediateCert = elements[i + 1].Certificate!;
+            IntPtr[] ptrs = new IntPtr[context!.IntermediateCertificates!.Length + 1];
 
-                    if (intermediateCert.HasPrivateKey)
-                    {
-                        // In the unlikely event that we get a certificate with a private key from
-                        // a chain, clear it to the certificate.
-                        //
-                        // The current value of intermediateCert is still in elements, which will
-                        // get Disposed at the end of this method.  The new value will be
-                        // in the intermediate certs array, which also gets serially Disposed.
-                        intermediateCert = new X509Certificate2(intermediateCert.RawData);
-                    }
+            for (int i = 0; i < context.IntermediateCertificates.Length; i++)
+            {
+                X509Certificate2 intermediateCert = context.IntermediateCertificates[i];
 
-                    intermediateCerts[i] = intermediateCert;
-                    ptrs[i + 1] = intermediateCert.Handle;
+                if (intermediateCert.HasPrivateKey)
+                {
+                    // In the unlikely event that we get a certificate with a private key from
+                    // a chain, clear it to the certificate.
+                    //
+                    // The current value of intermediateCert is still in elements, which will
+                    // get Disposed at the end of this method.  The new value will be
+                    // in the intermediate certs array, which also gets serially Disposed.
+                    intermediateCert = new X509Certificate2(intermediateCert.RawData);
                 }
 
-                ptrs[0] = certificate.Handle;
-
-                Interop.AppleCrypto.SslSetCertificate(sslContext, ptrs);
+                ptrs[i + 1] = intermediateCert.Handle;
+            }
 
-                // The X509Chain created all new certs for us, so Dispose them.
-                // And since the intermediateCerts could have been new instances, Dispose them, too
-                for (int i = 0; i < elements.Count; i++)
-                {
-                    elements[i].Certificate!.Dispose();
+            ptrs[0] = context!.Certificate!.Handle;
 
-                    if (i < intermediateCerts.Length)
-                    {
-                        intermediateCerts[i].Dispose();
-                    }
-                }
-            }
+            Interop.AppleCrypto.SslSetCertificate(sslContext, ptrs);
         }
     }
 }
index 9a894cb811221e7b4fc7d6e871ad101bb1ced7f3..24a4e7be7843a6e02e20176f778f1222e0342e59 100644 (file)
@@ -10,30 +10,22 @@ namespace System.Net
 {
     internal sealed class SafeFreeSslCredentials : SafeFreeCredentials
     {
-        public SafeFreeSslCredentials(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy)
+        public SafeFreeSslCredentials(SslStreamCertificateContext? certificateContext, SslProtocols protocols, EncryptionPolicy policy)
             : base(IntPtr.Zero, true)
         {
-            Debug.Assert(
-                certificate == null || certificate is X509Certificate2,
-                "Only X509Certificate2 certificates are supported at this time");
-
-            X509Certificate2? cert = (X509Certificate2?)certificate;
-
-            if (cert != null)
+            if (certificateContext != null)
             {
-                Debug.Assert(cert.HasPrivateKey, "cert.HasPrivateKey");
-
                 // Make a defensive copy of the certificate. In some async cases the
                 // certificate can have been disposed before being provided to the handshake.
                 //
                 // This meshes with the Unix (OpenSSL) PAL, because it extracts the private key
                 // and cert handle (which get up-reffed) to match the API expectations.
-                cert = new X509Certificate2(cert);
+                certificateContext = certificateContext.Duplicate();
 
-                Debug.Assert(cert.HasPrivateKey, "cert clone.HasPrivateKey");
+                Debug.Assert(certificateContext.Certificate.HasPrivateKey, "cert clone.HasPrivateKey");
             }
 
-            Certificate = cert;
+            CertificateContext = certificateContext;
             Protocols = protocols;
             Policy = policy;
         }
@@ -42,13 +34,13 @@ namespace System.Net
 
         public SslProtocols Protocols { get; }
 
-        public X509Certificate2? Certificate { get; }
+        public SslStreamCertificateContext? CertificateContext { get; }
 
         public override bool IsInvalid => false;
 
         protected override bool ReleaseHandle()
         {
-            Certificate?.Dispose();
+            CertificateContext?.Certificate.Dispose();
             return true;
         }
     }
index 4d20cea9c251b5fedd5e7b41b62f02ae02afc4c6..c8cdcb780a26b34a0199acf31c29172464998bcc 100644 (file)
@@ -596,21 +596,27 @@ namespace System.Net.Security
                 {
                     if (NetEventSource.Log.IsEnabled())
                         NetEventSource.Log.UsingCachedCredential(this);
-
                     _credentialsHandle = cachedCredentialHandle;
                     _selectedClientCertificate = clientCertificate;
                     cachedCred = true;
                 }
                 else
                 {
-                    _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(selectedCert!, _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.IsServer);
+                    if (selectedCert != null)
+                    {
+                        _sslAuthenticationOptions.CertificateContext = SslStreamCertificateContext.Create(selectedCert!);
+                    }
+
+                    _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(_sslAuthenticationOptions.CertificateContext,
+                            _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.IsServer);
+
                     thumbPrint = guessedThumbPrint; // Delay until here in case something above threw.
                     _selectedClientCertificate = clientCertificate;
                 }
             }
             finally
             {
-                if (selectedCert != null)
+                if (selectedCert != null && _sslAuthenticationOptions.CertificateContext != null)
                 {
                     _sslAuthenticationOptions.CertificateContext = SslStreamCertificateContext.Create(selectedCert);
                 }
@@ -710,7 +716,8 @@ namespace System.Net.Security
             }
             else
             {
-                _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(selectedCert, _sslAuthenticationOptions.EnabledSslProtocols, _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.IsServer);
+                _credentialsHandle = SslStreamPal.AcquireCredentialsHandle(_sslAuthenticationOptions.CertificateContext, _sslAuthenticationOptions.EnabledSslProtocols,
+                        _sslAuthenticationOptions.EncryptionPolicy, _sslAuthenticationOptions.IsServer);
                 thumbPrint = guessedThumbPrint;
             }
 
index e4260aeaac7885311f531badf3fc9ca3792ec50c..f5e9dd2114d615e940b53a10f6595222d679f29e 100644 (file)
@@ -7,6 +7,14 @@ namespace System.Net.Security
 {
     public partial class SslStreamCertificateContext
     {
+        private const bool TrimRootCertificate = true;
+
+        private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates)
+        {
+            Certificate = target;
+            IntermediateCertificates = intermediates;
+        }
+
         internal static SslStreamCertificateContext Create(X509Certificate2 target) => Create(target, null);
     }
 }
index d616c9e8e6b9b8b6d23077eaff24e9f645dad04f..4579f15407fdea88e783671a9c605acc65033e22 100644 (file)
@@ -7,6 +7,15 @@ namespace System.Net.Security
 {
     public partial class SslStreamCertificateContext
     {
+        // No leaf, no root.
+        private const bool TrimRootCertificate = true;
+
+        private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates)
+        {
+            Certificate = target;
+            IntermediateCertificates = intermediates;
+        }
+
         internal static SslStreamCertificateContext Create(X509Certificate2 target)
         {
             // On OSX we do not need to build chain unless we are asked for it.
index 5906a13a68b7343abaa093440429b748829f392d..0827eca16d572e0f55b25caad8a5dd5529a91248 100644 (file)
@@ -7,10 +7,102 @@ namespace System.Net.Security
 {
     public partial class SslStreamCertificateContext
     {
+        // No leaf, include root.
+        private const bool TrimRootCertificate = false;
+
         internal static SslStreamCertificateContext Create(X509Certificate2 target)
         {
             // On Windows we do not need to build chain unless we are asked for it.
             return new SslStreamCertificateContext(target, Array.Empty<X509Certificate2>());
         }
+
+        private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates)
+        {
+            if (intermediates.Length > 0)
+            {
+                using (X509Chain chain = new X509Chain())
+                {
+                    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
+                    chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
+                    chain.ChainPolicy.DisableCertificateDownloads = true;
+                    bool osCanBuildChain = chain.Build(target);
+
+                    int count = 0;
+                    foreach (X509ChainStatus status in chain.ChainStatus)
+                    {
+                        if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain) || status.Status.HasFlag(X509ChainStatusFlags.NotSignatureValid))
+                        {
+                            osCanBuildChain = false;
+                            break;
+                        }
+
+                        count++;
+                    }
+
+                    // OS failed to build the chain but we have at least some intermediates.
+                    // We will try to add them to "Intermediate Certification Authorities" store.
+                    if (!osCanBuildChain)
+                    {
+                        X509Store? store = new X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine);
+
+                        try
+                        {
+                            store.Open(OpenFlags.ReadWrite);
+                        }
+                        catch
+                        {
+                            // If using system store fails, try to fall-back to user store.
+                            store.Dispose();
+                            store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser);
+                            try
+                            {
+                                store.Open(OpenFlags.ReadWrite);
+                            }
+                            catch
+                            {
+                                store.Dispose();
+                                store = null;
+                                if (NetEventSource.IsEnabled)
+                                {
+                                    NetEventSource.Error(this, $"Failed to open certificate store for intermediates.");
+                                }
+                            }
+                        }
+
+                        if (store != null)
+                        {
+                            using (store)
+                            {
+                                // Add everything except the root
+                                for (int index = count; index < intermediates.Length - 1; index++)
+                                {
+                                    store.Add(intermediates[index]);
+                                }
+
+                                osCanBuildChain = chain.Build(target);
+                                foreach (X509ChainStatus status in chain.ChainStatus)
+                                {
+                                    if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain) || status.Status.HasFlag(X509ChainStatusFlags.NotSignatureValid))
+                                    {
+                                        osCanBuildChain = false;
+                                        break;
+                                    }
+                                }
+
+                                if (!osCanBuildChain)
+                                {
+                                    // Add also root to Intermediate CA store so OS can complete building chain.
+                                    // (This does not make it trusted.
+                                    store.Add(intermediates[intermediates.Length - 1]);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            Certificate = target;
+            IntermediateCertificates = intermediates;
+        }
     }
 }
index 621ab20079d514b05aec768eacf8128cc4de5fb8..4cba3232be530fdae1158d86ad7a6204d6a95931 100644 (file)
@@ -18,7 +18,6 @@ namespace System.Net.Security
             }
 
             X509Certificate2[] intermediates = Array.Empty<X509Certificate2>();
-
             using (X509Chain chain = new X509Chain())
             {
                 if (additionalCertificates != null)
@@ -32,11 +31,14 @@ namespace System.Net.Security
                 chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
                 chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
                 chain.ChainPolicy.DisableCertificateDownloads = offline;
-                chain.Build(target);
+                bool chainStatus = chain.Build(target);
 
-                // No leaf, no root.
-                int count = chain.ChainElements.Count - 2;
+                if (!chainStatus && NetEventSource.IsEnabled)
+                {
+                    NetEventSource.Error(null, $"Failed to build chain for {target.Subject}");
+                }
 
+                int count = chain.ChainElements.Count - (TrimRootCertificate ? 1 : 2);
                 foreach (X509ChainStatus status in chain.ChainStatus)
                 {
                     if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain))
@@ -48,7 +50,7 @@ namespace System.Net.Security
                 }
 
                 // Count can be zero for a self-signed certificate, or a cert issued directly from a root.
-                if (count > 0)
+                if (count > 0 && chain.ChainElements.Count > 1)
                 {
                     intermediates = new X509Certificate2[count];
                     for (int i = 0; i < count; i++)
@@ -70,10 +72,10 @@ namespace System.Net.Security
             return new SslStreamCertificateContext(target, intermediates);
         }
 
-        private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates)
+        internal SslStreamCertificateContext Duplicate()
         {
-            Certificate = target;
-            IntermediateCertificates = intermediates;
+            return new SslStreamCertificateContext(new X509Certificate2(Certificate), IntermediateCertificates);
+
         }
     }
 }
index 4f5a990698515031a086874c2a92a7b376695dfa..5f1c1bb346676e843fd8fd5036e682815ba5be7c 100644 (file)
@@ -53,12 +53,12 @@ namespace System.Net.Security
         }
 
         public static SafeFreeCredentials AcquireCredentialsHandle(
-            X509Certificate certificate,
+            SslStreamCertificateContext? certificateContext,
             SslProtocols protocols,
             EncryptionPolicy policy,
             bool isServer)
         {
-            return new SafeFreeSslCredentials(certificate, protocols, policy);
+            return new SafeFreeSslCredentials(certificateContext, protocols, policy);
         }
 
         internal static byte[]? GetNegotiatedApplicationProtocol(SafeDeleteContext? context)
index 4864d32b63c6edcf5f22aa0e72f8d50cff91b4bb..aeae1edc9fe94611afb3fae8e1b1ea44db01eb25 100644 (file)
@@ -36,10 +36,10 @@ namespace System.Net.Security
             return HandshakeInternal(credential!, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions);
         }
 
-        public static SafeFreeCredentials AcquireCredentialsHandle(X509Certificate? certificate,
+        public static SafeFreeCredentials AcquireCredentialsHandle(SslStreamCertificateContext? certificateContext,
             SslProtocols protocols, EncryptionPolicy policy, bool isServer)
         {
-            return new SafeFreeSslCredentials(certificate, protocols, policy);
+            return new SafeFreeSslCredentials(certificateContext?.Certificate, protocols, policy);
         }
 
         public static SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, ReadOnlyMemory<byte> input, int headerSize, int trailerSize, ref byte[] output, out int resultSize)
index 5e72e6b553b0d95e12c1156a189d77175b0bc076..4c3c2c22db42e0833a328a77be9b2c2958f55862 100644 (file)
@@ -110,12 +110,12 @@ namespace System.Net.Security
             return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode);
         }
 
-        public static SafeFreeCredentials AcquireCredentialsHandle(X509Certificate? certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer)
+        public static SafeFreeCredentials AcquireCredentialsHandle(SslStreamCertificateContext? certificateContext, SslProtocols protocols, EncryptionPolicy policy, bool isServer)
         {
             // New crypto API supports TLS1.3 but it does not allow to force NULL encryption.
             return !UseNewCryptoApi || policy == EncryptionPolicy.NoEncryption ?
-                        AcquireCredentialsHandleSchannelCred(certificate, protocols, policy, isServer) :
-                        AcquireCredentialsHandleSchCredentials(certificate, protocols, policy, isServer);
+                        AcquireCredentialsHandleSchannelCred(certificateContext?.Certificate, protocols, policy, isServer) :
+                        AcquireCredentialsHandleSchCredentials(certificateContext?.Certificate, protocols, policy, isServer);
         }
 
         // This is legacy crypto API used on .NET Framework and older Windows versions.
index 28cb9233b638496378f8afddb28e9d8be9fdffff..14d61a3d7407bd054abf343d6d73f422f5b6a4d0 100644 (file)
@@ -7,6 +7,7 @@ using System.Net.Test.Common;
 using System.Security.Authentication;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.DotNet.XUnitExtensions;
 using Xunit;
@@ -15,14 +16,19 @@ namespace System.Net.Security.Tests
 {
     using Configuration = System.Net.Test.Common.Configuration;
 
-    public class SslStreamNetworkStreamTest
+    public class SslStreamNetworkStreamTest : IDisposable
     {
         private readonly X509Certificate2 _serverCert;
-        private readonly X509CertificateCollection _serverChain;
+        private readonly X509Certificate2Collection _serverChain;
 
         public SslStreamNetworkStreamTest()
         {
-            (_serverCert, _serverChain) = TestHelper.GenerateCertificates("localhost");
+            (_serverCert, _serverChain) = TestHelper.GenerateCertificates("localhost", this.GetType().Name);
+        }
+
+        public void Dispose()
+        {
+            TestHelper.CleanupCertificates(this.GetType().Name);
         }
 
         [Fact]
@@ -269,14 +275,12 @@ namespace System.Net.Security.Tests
         }
 
         [Fact]
-        [PlatformSpecific(TestPlatforms.AnyUnix)]
         public async Task SslStream_UntrustedCaWithCustomCallback_OK()
         {
-            var options = new  SslClientAuthenticationOptions() { TargetHost = "localhost" };
-            options.RemoteCertificateValidationCallback =
+            var clientOptions = new  SslClientAuthenticationOptions() { TargetHost = "localhost" };
+            clientOptions.RemoteCertificateValidationCallback =
                 (sender, certificate, chain, sslPolicyErrors) =>
                 {
-                    chain.ChainPolicy.ExtraStore.AddRange(_serverChain);
                     chain.ChainPolicy.CustomTrustStore.Add(_serverChain[_serverChain.Count -1]);
                     chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
 
@@ -286,14 +290,17 @@ namespace System.Net.Security.Tests
                     return result;
                 };
 
+            var serverOptions = new SslServerAuthenticationOptions();
+            serverOptions.ServerCertificateContext = SslStreamCertificateContext.Create(_serverCert, _serverChain);
+
             (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams();
             using (clientStream)
             using (serverStream)
             using (SslStream client = new SslStream(clientStream))
             using (SslStream server = new SslStream(serverStream))
             {
-                Task t1 = client.AuthenticateAsClientAsync(options, default);
-                Task t2 = server.AuthenticateAsServerAsync(_serverCert);
+                Task t1 = client.AuthenticateAsClientAsync(clientOptions, CancellationToken.None);
+                Task t2 = server.AuthenticateAsServerAsync(serverOptions, CancellationToken.None);
 
                 await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2);
             }
@@ -306,13 +313,12 @@ namespace System.Net.Security.Tests
         public async Task SslStream_UntrustedCaWithCustomCallback_Throws(bool customCallback)
         {
             string errorMessage;
-            var options = new  SslClientAuthenticationOptions() { TargetHost = "localhost" };
+            var clientOptions = new  SslClientAuthenticationOptions() { TargetHost = "localhost" };
             if (customCallback)
             {
-                options.RemoteCertificateValidationCallback =
+                clientOptions.RemoteCertificateValidationCallback =
                     (sender, certificate, chain, sslPolicyErrors) =>
                     {
-                        chain.ChainPolicy.ExtraStore.AddRange(_serverChain);
                         chain.ChainPolicy.CustomTrustStore.Add(_serverChain[_serverChain.Count -1]);
                         chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
                         // This should work and we should be able to trust the chain.
@@ -325,17 +331,20 @@ namespace System.Net.Security.Tests
             }
             else
             {
-                errorMessage = "PartialChain";
+                errorMessage = "UntrustedRoot";
             }
 
+            var serverOptions = new SslServerAuthenticationOptions();
+            serverOptions.ServerCertificateContext = SslStreamCertificateContext.Create(_serverCert, _serverChain);
+
             (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams();
             using (clientStream)
             using (serverStream)
             using (SslStream client = new SslStream(clientStream))
             using (SslStream server = new SslStream(serverStream))
             {
-                Task t1 = client.AuthenticateAsClientAsync(options, default);
-                Task t2 = server.AuthenticateAsServerAsync(_serverCert);
+                Task t1 = client.AuthenticateAsClientAsync(clientOptions, CancellationToken.None);
+                Task t2 = server.AuthenticateAsServerAsync(serverOptions, CancellationToken.None);
 
                 var e = await Assert.ThrowsAsync<AuthenticationException>(() => t1);
                 Assert.Contains(errorMessage, e.Message);
index 4e9a9a8ea37a7e0308e1d64059fc56b6c1abc9d6..b16bcac29825ded4121024fb2499c6c4d4ce11cb 100644 (file)
@@ -72,14 +72,53 @@ namespace System.Net.Security.Tests
             return (new VirtualNetworkStream(vn, isServer: false), new VirtualNetworkStream(vn, isServer: true));
         }
 
-        internal static (X509Certificate2 certificate, X509Certificate2Collection) GenerateCertificates(string name, string? testName = null)
+        internal static void CleanupCertificates(string testName)
         {
-            X509Certificate2Collection chain = new X509Certificate2Collection();
+            string caName = $"O={testName}";
+            try
+            {
+                using (X509Store store = new X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine))
+                {
+                    store.Open(OpenFlags.ReadWrite);
+                    foreach (X509Certificate2 cert in store.Certificates)
+                    {
+                        if (cert.Subject.Contains(caName))
+                        {
+                            store.Remove(cert);
+                        }
+                    }
+                }
+            }
+            catch { };
+
+            try
+            {
+                using (X509Store store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser))
+                {
+                    store.Open(OpenFlags.ReadWrite);
+                    foreach (X509Certificate2 cert in store.Certificates)
+                    {
+                        if (cert.Subject.Contains(caName))
+                        {
+                            store.Remove(cert);
+                        }
+                    }
+                }
+            }
+            catch { };
+        }
+        internal static (X509Certificate2 certificate, X509Certificate2Collection) GenerateCertificates(string targetName, string? testName = null)
+        {
+            if (PlatformDetection.IsWindows && testName != null)
+            {
+                CleanupCertificates(testName);
+            }
 
+            X509Certificate2Collection chain = new X509Certificate2Collection();
             X509ExtensionCollection extensions = new X509ExtensionCollection();
 
             SubjectAlternativeNameBuilder builder = new SubjectAlternativeNameBuilder();
-            builder.AddDnsName(name);
+            builder.AddDnsName(targetName);
             extensions.Add(builder.Build());
             extensions.Add(s_eeConstraints);
             extensions.Add(s_eeKeyUsage);
@@ -91,7 +130,7 @@ namespace System.Net.Security.Tests
                 out CertificateAuthority root,
                 out CertificateAuthority intermediate,
                 out X509Certificate2 endEntity,
-                subjectName: name,
+                subjectName: targetName,
                 testName: testName,
                 keySize: 2048,
                 extensions: extensions);
@@ -103,6 +142,11 @@ namespace System.Net.Security.Tests
             root.Dispose();
             intermediate.Dispose();
 
+            if (PlatformDetection.IsWindows)
+            {
+                endEntity = new X509Certificate2(endEntity.Export(X509ContentType.Pfx));
+            }
+
             return (endEntity, chain);
         }
     }