diff --git a/src/libraries/Common/tests/System/Net/Configuration.Certificates.Dynamic.cs b/src/libraries/Common/tests/System/Net/Configuration.Certificates.Dynamic.cs index fa20d1f374ce36..185416759e725b 100644 --- a/src/libraries/Common/tests/System/Net/Configuration.Certificates.Dynamic.cs +++ b/src/libraries/Common/tests/System/Net/Configuration.Certificates.Dynamic.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Net.Security; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -37,29 +38,6 @@ public static partial class Certificates private static readonly X509BasicConstraintsExtension s_eeConstraints = new X509BasicConstraintsExtension(false, false, 0, false); - private static X509Certificate2 s_dynamicServerCertificate; - private static X509Certificate2Collection s_dynamicCaCertificates; - private static object certLock = new object(); - - - // These Get* methods make a copy of the certificates so that consumers own the lifetime of the - // certificates handed back. Consumers are expected to dispose of their certs when done with them. - - public static X509Certificate2 GetDynamicServerCerttificate(X509Certificate2Collection? chainCertificates) - { - lock (certLock) - { - if (s_dynamicServerCertificate == null) - { - CleanupCertificates(); - (s_dynamicServerCertificate, s_dynamicCaCertificates) = GenerateCertificates("localhost", nameof(Configuration) + nameof(Certificates)); - } - - chainCertificates?.AddRange(s_dynamicCaCertificates); - return new X509Certificate2(s_dynamicServerCertificate); - } - } - public static void CleanupCertificates([CallerMemberName] string? testName = null, StoreName storeName = StoreName.CertificateAuthority) { string caName = $"O={testName}"; @@ -78,7 +56,9 @@ public static void CleanupCertificates([CallerMemberName] string? testName = nul } } } - catch { }; + catch + { + } try { @@ -95,7 +75,9 @@ public static void CleanupCertificates([CallerMemberName] string? testName = nul } } } - catch { }; + catch + { + } } internal static X509ExtensionCollection BuildTlsServerCertExtensions(string serverName) @@ -119,7 +101,68 @@ private static X509ExtensionCollection BuildTlsCertExtensions(string targetName, return extensions; } - public static (X509Certificate2 certificate, X509Certificate2Collection) GenerateCertificates(string targetName, [CallerMemberName] string? testName = null, bool longChain = false, bool serverCertificate = true, bool ephemeralKey = false) + internal class PkiHolder : IDisposable + { + internal CertificateAuthority Root { get; } + internal CertificateAuthority[] Intermediates { get; } + public X509Certificate2 EndEntity { get; } + public X509Certificate2Collection IssuerChain { get; } + internal RevocationResponder Responder { get; } + + private readonly string? _testName; + + public PkiHolder(string? testName, CertificateAuthority root, CertificateAuthority[] intermediates, X509Certificate2 endEntity, RevocationResponder responder) + { + _testName = testName; + Root = root; + Intermediates = intermediates; + EndEntity = endEntity; + Responder = responder; + + // Walk the intermediates backwards so we build the chain collection as + // Issuer3 + // Issuer2 + // Issuer1 + // Root + IssuerChain = new X509Certificate2Collection(); + for (int i = intermediates.Length - 1; i >= 0; i--) + { + CertificateAuthority authority = intermediates[i]; + + IssuerChain.Add(authority.CloneIssuerCert()); + } + + IssuerChain.Add(root.CloneIssuerCert()); + } + + public SslStreamCertificateContext CreateSslStreamCertificateContext() + { + return SslStreamCertificateContext.Create(EndEntity, IssuerChain); + } + + public void Dispose() + { + foreach (CertificateAuthority authority in Intermediates) + { + authority.Dispose(); + } + Root.Dispose(); + EndEntity.Dispose(); + Responder.Dispose(); + + foreach (X509Certificate2 authority in IssuerChain) + { + authority.Dispose(); + } + + if (PlatformDetection.IsWindows && _testName != null) + { + CleanupCertificates(_testName); + } + } + } + + internal static PkiHolder GenerateCertificates(string targetName, [CallerMemberName] string? testName = null, bool longChain = false, bool serverCertificate = true, bool ephemeralKey = false) { const int keySize = 2048; if (PlatformDetection.IsWindows && testName != null) @@ -131,7 +174,7 @@ public static (X509Certificate2 certificate, X509Certificate2Collection) Generat X509ExtensionCollection extensions = BuildTlsCertExtensions(targetName, serverCertificate); CertificateAuthority.BuildPrivatePki( - PkiOptions.IssuerRevocationViaCrl, + PkiOptions.AllRevocation, out RevocationResponder responder, out CertificateAuthority root, out CertificateAuthority[] intermediates, @@ -142,24 +185,6 @@ public static (X509Certificate2 certificate, X509Certificate2Collection) Generat keyFactory: CertificateAuthority.KeyFactory.RSASize(keySize), extensions: extensions); - // Walk the intermediates backwards so we build the chain collection as - // Issuer3 - // Issuer2 - // Issuer1 - // Root - for (int i = intermediates.Length - 1; i >= 0; i--) - { - CertificateAuthority authority = intermediates[i]; - - chain.Add(authority.CloneIssuerCert()); - authority.Dispose(); - } - - chain.Add(root.CloneIssuerCert()); - - responder.Dispose(); - root.Dispose(); - if (!ephemeralKey && PlatformDetection.IsWindows) { X509Certificate2 ephemeral = endEntity; @@ -167,9 +192,8 @@ public static (X509Certificate2 certificate, X509Certificate2Collection) Generat ephemeral.Dispose(); } - return (endEntity, chain); + return new PkiHolder(testName, root, intermediates, endEntity, responder); } - } } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs index 4dc2eb3fcfdeaf..c8f581179ab042 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs @@ -48,7 +48,7 @@ public void Ctor_ExpectedDefaultValues() using (HttpClientHandler handler = CreateHttpClientHandler()) { Assert.Null(handler.ServerCertificateCustomValidationCallback); - Assert.False(handler.CheckCertificateRevocationList); + Assert.True(handler.CheckCertificateRevocationList); } } @@ -232,7 +232,9 @@ public async Task NoCallback_BadCertificate_ThrowsException(string url) [ActiveIssue("https://github.com/dotnet/runtime/issues/106634", typeof(PlatformDetection), nameof(PlatformDetection.IsAlpine))] public async Task NoCallback_RevokedCertificate_NoRevocationChecking_Succeeds() { - using (HttpClient client = CreateHttpClient()) + HttpClientHandler handler = CreateHttpClientHandler(); + handler.CheckCertificateRevocationList = false; + using (HttpClient client = CreateHttpClient(handler)) using (HttpResponseMessage response = await client.GetAsync(Configuration.Http.RevokedCertRemoteServer)) { Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -244,7 +246,6 @@ public async Task NoCallback_RevokedCertificate_NoRevocationChecking_Succeeds() public async Task NoCallback_RevokedCertificate_RevocationChecking_Fails() { HttpClientHandler handler = CreateHttpClientHandler(); - handler.CheckCertificateRevocationList = true; using (HttpClient client = CreateHttpClient(handler)) { await Assert.ThrowsAsync(() => client.GetAsync(Configuration.Http.RevokedCertRemoteServer)); diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 72f558e8a86885..582e4b8a1fd434 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -78,7 +78,7 @@ public void Ctor_ExpectedDefaultPropertyValues() Assert.True(handler.SupportsRedirectConfiguration); // Changes from .NET Framework. - Assert.False(handler.CheckCertificateRevocationList); + Assert.True(handler.CheckCertificateRevocationList); Assert.Equal(0, handler.MaxRequestContentBufferSize); Assert.Equal(SslProtocols.None, handler.SslProtocols); } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj index 7e6af9f6b954f9..2cba1a88684d98 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj @@ -96,6 +96,8 @@ System.Net.Http.WinHttpHandler + ? _serverCertificateValidationCallback; - private bool _checkCertificateRevocationList; + private bool _checkCertificateRevocationList = DefaultCertificateRevocationCheck; private ClientCertificateOption _clientCertificateOption = ClientCertificateOption.Manual; private X509Certificate2Collection? _clientCertificates; // Only create collection when required. private ICredentials? _serverCredentials; diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj index edabede4649558..e2553e9b42e680 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj @@ -24,6 +24,8 @@ Link="Common\Interop\Windows\WinHttp\Interop.SafeWinHttpHandle.cs" /> + _pkiHolder.EndEntity; + public X509Certificate2Collection ServerChain => _pkiHolder.IssuerChain; + + private readonly Configuration.Certificates.PkiHolder _pkiHolder; + + public CertificateSetup() + { + _pkiHolder = Configuration.Certificates.GenerateCertificates("localhost", nameof(HttpClientHandlerTestBase), longChain: true); + } + + public SslStreamCertificateContext CreateSslStreamCertificateContext() => _pkiHolder.CreateSslStreamCertificateContext(); + + public void Dispose() + { + _pkiHolder.Dispose(); + } + } + public sealed class SocketsHttpHandler_HttpClientHandler_Asynchrony_Test_Http11 : SocketsHttpHandler_HttpClientHandler_Asynchrony_Test { public SocketsHttpHandler_HttpClientHandler_Asynchrony_Test_Http11(ITestOutputHelper output) : base(output) { } @@ -2475,7 +2495,7 @@ public void SslOptions_GetSet_Roundtrips() Assert.True(options.AllowRenegotiation); Assert.Null(options.ApplicationProtocols); - Assert.Equal(X509RevocationMode.NoCheck, options.CertificateRevocationCheckMode); + Assert.Equal(X509RevocationMode.Online, options.CertificateRevocationCheckMode); Assert.Null(options.ClientCertificates); Assert.Equal(SslProtocols.None, options.EnabledSslProtocols); Assert.Equal(EncryptionPolicy.RequireEncryption, options.EncryptionPolicy); @@ -4339,15 +4359,17 @@ public SocketsHttpHandler_RequestContentLengthMismatchTest_Http3(ITestOutputHelp [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] public abstract class SocketsHttpHandler_SecurityTest : HttpClientHandlerTestBase { - public SocketsHttpHandler_SecurityTest(ITestOutputHelper output) : base(output) { } + private readonly CertificateSetup _certificateSetup; + + public SocketsHttpHandler_SecurityTest(ITestOutputHelper output, CertificateSetup certificateSetup) : base(output) + { + _certificateSetup = certificateSetup; + } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] public async Task SslOptions_CustomTrust_Ok() { - X509Certificate2Collection caCerts = new X509Certificate2Collection(); - X509Certificate2 certificate = Configuration.Certificates.GetDynamicServerCerttificate(caCerts); - - GenericLoopbackOptions options = new GenericLoopbackOptions() { UseSsl = true, Certificate = certificate }; + GenericLoopbackOptions options = new GenericLoopbackOptions() { UseSsl = true, Certificate = new X509Certificate2(_certificateSetup.ServerCert) }; await LoopbackServerFactory.CreateClientAndServerAsync( async uri => { @@ -4360,8 +4382,8 @@ await LoopbackServerFactory.CreateClientAndServerAsync( TrustMode = X509ChainTrustMode.CustomRootTrust, }; - policy.ExtraStore.AddRange(caCerts); - policy.CustomTrustStore.Add(caCerts[caCerts.Count - 1]); + policy.ExtraStore.AddRange(_certificateSetup.ServerChain); + policy.CustomTrustStore.Add(_certificateSetup.ServerChain[^1]); socketsHandler.SslOptions = new SslClientAuthenticationOptions() { CertificateChainPolicy = policy }; using HttpClient client = CreateHttpClient(handler); client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact; @@ -4380,15 +4402,22 @@ await LoopbackServerFactory.CreateClientAndServerAsync( [Fact] public async Task SslOptions_InvalidName_Throws() { - X509Certificate2Collection caCerts = new X509Certificate2Collection(); - using X509Certificate2 certificate = Configuration.Certificates.GetDynamicServerCerttificate(caCerts); - - GenericLoopbackOptions options = new GenericLoopbackOptions() { UseSsl = true, Certificate = certificate }; + GenericLoopbackOptions options = new GenericLoopbackOptions() { UseSsl = true, Certificate = new X509Certificate2(_certificateSetup.ServerCert) }; await LoopbackServerFactory.CreateClientAndServerAsync( async uri => { using HttpClientHandler handler = CreateHttpClientHandler(allowAllCertificates: false); var socketsHandler = (SocketsHttpHandler)GetUnderlyingSocketsHttpHandler(handler); + + var policy = new X509ChainPolicy() + { + RevocationMode = X509RevocationMode.NoCheck, + TrustMode = X509ChainTrustMode.CustomRootTrust, + }; + + policy.ExtraStore.AddRange(_certificateSetup.ServerChain); + policy.CustomTrustStore.Add(_certificateSetup.ServerChain[^1]); + socketsHandler.SslOptions = new SslClientAuthenticationOptions() { CertificateChainPolicy = policy }; using HttpClient client = CreateHttpClient(handler); client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion, VersionPolicy = HttpVersionPolicy.RequestVersionExact }; @@ -4403,17 +4432,15 @@ await LoopbackServerFactory.CreateClientAndServerAsync( { await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo"); } - catch { }; + catch { } + ; }, options: options); } [Fact] public async Task SslOptions_CustomPolicy_IgnoresNameMismatch() { - X509Certificate2Collection caCerts = new X509Certificate2Collection(); - X509Certificate2 certificate = Configuration.Certificates.GetDynamicServerCerttificate(caCerts); - - GenericLoopbackOptions options = new GenericLoopbackOptions() { UseSsl = true, Certificate = certificate }; + GenericLoopbackOptions options = new GenericLoopbackOptions() { UseSsl = true, Certificate = new X509Certificate2(_certificateSetup.ServerCert) }; await LoopbackServerFactory.CreateClientAndServerAsync( async uri => { @@ -4427,8 +4454,8 @@ await LoopbackServerFactory.CreateClientAndServerAsync( VerificationFlags = X509VerificationFlags.IgnoreInvalidName, }; - policy.ExtraStore.AddRange(caCerts); - policy.CustomTrustStore.Add(caCerts[caCerts.Count - 1]); + policy.ExtraStore.AddRange(_certificateSetup.ServerChain); + policy.CustomTrustStore.Add(_certificateSetup.ServerChain[^1]); socketsHandler.SslOptions = new SslClientAuthenticationOptions() { CertificateChainPolicy = policy }; using HttpClient client = CreateHttpClient(handler); @@ -4447,9 +4474,9 @@ await LoopbackServerFactory.CreateClientAndServerAsync( } } - public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http11 : SocketsHttpHandler_SecurityTest + public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http11 : SocketsHttpHandler_SecurityTest, IClassFixture { - public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http11(ITestOutputHelper output) : base(output) { } + public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http11(ITestOutputHelper output, CertificateSetup certificateSetup) : base(output, certificateSetup) { } protected override Version UseVersion => HttpVersion.Version11; #if DEBUG @@ -4499,16 +4526,17 @@ await server.AcceptConnectionAsync(async connection => } [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.SupportsAlpn))] - public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http2 : SocketsHttpHandler_SecurityTest + public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http2 : SocketsHttpHandler_SecurityTest, IClassFixture { - public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http2(ITestOutputHelper output) : base(output) { } + public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http2(ITestOutputHelper output, CertificateSetup certificateSetup) : base(output, certificateSetup) { } + protected override Version UseVersion => HttpVersion.Version20; } [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] - public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http3 : SocketsHttpHandler_SecurityTest + public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http3 : SocketsHttpHandler_SecurityTest, IClassFixture { - public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http3(ITestOutputHelper output) : base(output) { } + public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http3(ITestOutputHelper output, CertificateSetup certificateSetup) : base(output, certificateSetup) { } protected override Version UseVersion => HttpVersion.Version30; } diff --git a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTlsTest.cs b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTlsTest.cs index 794fc2e7bf45f5..2f46b2a0e3babf 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTlsTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTlsTest.cs @@ -13,26 +13,26 @@ namespace System.Net.Mail.Tests { + using Configuration = System.Net.Test.Common.Configuration; + // Common test setup to share across test cases. public class CertificateSetup : IDisposable { - public readonly X509Certificate2 serverCert; - public readonly X509Certificate2Collection serverChain; - public readonly SslStreamCertificateContext serverCertContext; + public X509Certificate2 ServerCert => _pkiHolder.EndEntity; + public X509Certificate2Collection ServerChain => _pkiHolder.IssuerChain; + + private readonly Configuration.Certificates.PkiHolder _pkiHolder; public CertificateSetup() { - (serverCert, serverChain) = System.Net.Test.Common.Configuration.Certificates.GenerateCertificates("localhost", nameof(SmtpClientTlsTest<>)); - serverCertContext = SslStreamCertificateContext.Create(serverCert, serverChain); + _pkiHolder = Configuration.Certificates.GenerateCertificates("localhost", nameof(SmtpClientTlsTest<>), longChain: true); } + public SslStreamCertificateContext CreateSslStreamCertificateContext() => _pkiHolder.CreateSslStreamCertificateContext(); + public void Dispose() { - serverCert.Dispose(); - foreach (var c in serverChain) - { - c.Dispose(); - } + _pkiHolder.Dispose(); } } @@ -47,7 +47,7 @@ public SmtpClientTlsTest(ITestOutputHelper output, CertificateSetup certificateS _certificateSetup = certificateSetup; Server.SslOptions = new SslServerAuthenticationOptions { - ServerCertificateContext = _certificateSetup.serverCertContext, + ServerCertificateContext = _certificateSetup.CreateSslStreamCertificateContext(), ClientCertificateRequired = false, }; @@ -158,7 +158,7 @@ public async Task AuthenticationException_Propagates() public async Task ClientCertificateRequired_Sent() { Server.SslOptions.ClientCertificateRequired = true; - X509Certificate2 clientCert = _certificateSetup.serverCert; // use the server cert as a client cert for testing + X509Certificate2 clientCert = _certificateSetup.ServerCert; // use the server cert as a client cert for testing X509Certificate2? receivedClientCert = null; Server.SslOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => { diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 7f9af662755d60..827841626541ab 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -27,22 +27,21 @@ namespace System.Net.Quic.Tests public class CertificateSetup : IDisposable { - public readonly X509Certificate2 serverCert; - public readonly X509Certificate2Collection serverChain; + public X509Certificate2 ServerCert => _pkiHolder.EndEntity; + public X509Certificate2Collection ServerChain => _pkiHolder.IssuerChain; + + Configuration.Certificates.PkiHolder _pkiHolder; public CertificateSetup() { - Configuration.Certificates.CleanupCertificates(nameof(MsQuicTests)); - (serverCert, serverChain) = Configuration.Certificates.GenerateCertificates("localhost", nameof(MsQuicTests), longChain: true); + _pkiHolder = Configuration.Certificates.GenerateCertificates("localhost", nameof(MsQuicTests), longChain: true); } + public SslStreamCertificateContext CreateSslStreamCertificateContext() => _pkiHolder.CreateSslStreamCertificateContext(); + public void Dispose() { - serverCert.Dispose(); - foreach (var c in serverChain) - { - c.Dispose(); - } + _pkiHolder.Dispose(); } } @@ -181,74 +180,63 @@ bool TestWeakReferences() [Fact] public async Task ConnectWithCertificateChain() { - (X509Certificate2 certificate, X509Certificate2Collection chain) = Configuration.Certificates.GenerateCertificates("localhost", longChain: true); - try - { - X509Certificate2 rootCA = chain[chain.Count - 1]; + X509Certificate2 certificate = _certificates.ServerCert; + X509Certificate2Collection chain = _certificates.ServerChain; - var listenerOptions = new QuicListenerOptions() - { - ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), - ApplicationProtocols = new List() { ApplicationProtocol }, - ConnectionOptionsCallback = (_, _, _) => - { - var serverOptions = CreateQuicServerOptions(); - serverOptions.ServerAuthenticationOptions.ServerCertificateContext = SslStreamCertificateContext.Create(certificate, chain); - serverOptions.ServerAuthenticationOptions.ServerCertificate = null; - return ValueTask.FromResult(serverOptions); - } - }; + X509Certificate2 rootCA = chain[chain.Count - 1]; - // Use whatever endpoint, it'll get overwritten in CreateConnectedQuicConnection. - QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listenerOptions.ListenEndPoint); - clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => + var listenerOptions = new QuicListenerOptions() + { + ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), + ApplicationProtocols = new List() { ApplicationProtocol }, + ConnectionOptionsCallback = (_, _, _) => { - Assert.Equal(certificate.Subject, cert.Subject); - Assert.Equal(certificate.Issuer, cert.Issuer); + var serverOptions = CreateQuicServerOptions(); + serverOptions.ServerAuthenticationOptions.ServerCertificateContext = SslStreamCertificateContext.Create(certificate, chain); + serverOptions.ServerAuthenticationOptions.ServerCertificate = null; + return ValueTask.FromResult(serverOptions); + } + }; - // Dispose of the chain's elements before calling Build, which will overwrite them. - for (int i = 0; i < chain.ChainElements.Count; i++) - { - chain.ChainElements[i].Certificate.Dispose(); - } + // Use whatever endpoint, it'll get overwritten in CreateConnectedQuicConnection. + QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listenerOptions.ListenEndPoint); + clientOptions.ClientAuthenticationOptions.CertificateChainPolicy = new X509ChainPolicy() + { + // We should get full chain without root CA. + // With trusted root, we should be able to build chain. + TrustMode = X509ChainTrustMode.CustomRootTrust, + CustomTrustStore = { rootCA }, + }; - // We should get full chain without root CA. - // With trusted root, we should be able to build chain. - chain.ChainPolicy.CustomTrustStore.Add(rootCA); - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - bool ret = chain.Build(certificate); - if (!ret) + clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => + { + Assert.Equal(certificate.Subject, cert.Subject); + Assert.Equal(certificate.Issuer, cert.Issuer); + + if (errors != SslPolicyErrors.None) + { + _output.WriteLine("Certificate validation failed with {0}", errors); + _output.WriteLine("Chain has {0} elements", chain.ChainElements.Count); + foreach (X509ChainElement element in chain.ChainElements) { - _output.WriteLine("Chain build failed with {0} elements", chain.ChainElements); - foreach (X509ChainElement element in chain.ChainElements) + _output.WriteLine("Element subject {0} and issuer {1}", element.Certificate.Subject, element.Certificate.Issuer); + _output.WriteLine("Element status len {0}", element.ChainElementStatus.Length); + foreach (X509ChainStatus status in element.ChainElementStatus) { - _output.WriteLine("Element subject {0} and issuer {1}", element.Certificate.Subject, element.Certificate.Issuer); - _output.WriteLine("Element status len {0}", element.ChainElementStatus.Length); - foreach (X509ChainStatus status in element.ChainElementStatus) - { - _output.WriteLine($"Status: {status.Status}: {status.StatusInformation}"); - } + _output.WriteLine($"Status: {status.Status}: {status.StatusInformation}"); } } + } - return ret; - }; + return errors == SslPolicyErrors.None; + }; - (QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection(clientOptions, listenerOptions); - using X509Certificate clientRemoteCertificate = clientConnection.RemoteCertificate; - Assert.Equal(certificate, clientRemoteCertificate); - Assert.Null(serverConnection.RemoteCertificate); - await serverConnection.DisposeAsync(); - await clientConnection.DisposeAsync(); - } - finally - { - foreach (X509Certificate2 cert in chain) - { - cert.Dispose(); - } - certificate.Dispose(); - } + (QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection(clientOptions, listenerOptions); + using X509Certificate clientRemoteCertificate = clientConnection.RemoteCertificate; + Assert.Equal(certificate, clientRemoteCertificate); + Assert.Null(serverConnection.RemoteCertificate); + await serverConnection.DisposeAsync(); + await clientConnection.DisposeAsync(); } [Theory] @@ -256,7 +244,7 @@ public async Task ConnectWithCertificateChain() [InlineData(false)] public async Task ConnectWithUntrustedCaWithCustomTrust_OK(bool usePartialChain) { - int split = Random.Shared.Next(0, _certificates.serverChain.Count - 1); + int split = Random.Shared.Next(0, _certificates.ServerChain.Count - 1); X509Certificate2Collection serverChain; if (usePartialChain) @@ -265,19 +253,19 @@ public async Task ConnectWithUntrustedCaWithCustomTrust_OK(bool usePartialChain) serverChain = new X509Certificate2Collection(); for (int i = 0; i < split; i++) { - serverChain.Add(_certificates.serverChain[i]); + serverChain.Add(_certificates.ServerChain[i]); } } else { - serverChain = _certificates.serverChain; + serverChain = _certificates.ServerChain; } var listenerOptions = CreateQuicListenerOptions(); listenerOptions.ConnectionOptionsCallback = (_, _, _) => { var serverOptions = CreateQuicServerOptions(); - serverOptions.ServerAuthenticationOptions.ServerCertificateContext = SslStreamCertificateContext.Create(_certificates.serverCert, serverChain); + serverOptions.ServerAuthenticationOptions.ServerCertificateContext = SslStreamCertificateContext.Create(_certificates.ServerCert, serverChain); serverOptions.ServerAuthenticationOptions.RemoteCertificateValidationCallback = null; return ValueTask.FromResult(serverOptions); }; @@ -293,14 +281,14 @@ public async Task ConnectWithUntrustedCaWithCustomTrust_OK(bool usePartialChain) RevocationMode = X509RevocationMode.NoCheck, TrustMode = X509ChainTrustMode.CustomRootTrust }; - clientSslOptions.CertificateChainPolicy.CustomTrustStore.Add(_certificates.serverChain[_certificates.serverChain.Count - 1]); + clientSslOptions.CertificateChainPolicy.CustomTrustStore.Add(_certificates.ServerChain[_certificates.ServerChain.Count - 1]); // Add only one CA to verify that peer did send intermediate CA cert. // In case of partial chain, we need to make missing certs available. if (usePartialChain) { - for (int i = split; i < _certificates.serverChain.Count - 1; i++) + for (int i = split; i < _certificates.ServerChain.Count - 1; i++) { - clientSslOptions.CertificateChainPolicy.ExtraStore.Add(_certificates.serverChain[i]); + clientSslOptions.CertificateChainPolicy.ExtraStore.Add(_certificates.ServerChain[i]); } } @@ -508,86 +496,66 @@ public async Task ConnectWithIpSetsSni(string destination) [Fact] public async Task ConnectWithCertificateForDifferentName_Throws() { - (X509Certificate2 certificate, X509Certificate2Collection chain) = Configuration.Certificates.GenerateCertificates("localhost"); - try - { - var quicOptions = new QuicListenerOptions() - { - ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), - ApplicationProtocols = new List() { ApplicationProtocol }, - ConnectionOptionsCallback = (_, _, _) => - { - var serverOptions = CreateQuicServerOptions(); - serverOptions.ServerAuthenticationOptions.ServerCertificate = certificate; - return ValueTask.FromResult(serverOptions); - } - }; - await using QuicListener listener = await CreateQuicListener(quicOptions); - - QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listener.LocalEndPoint); - // Use different target host on purpose to get RemoteCertificateNameMismatch ssl error. - clientOptions.ClientAuthenticationOptions.TargetHost = "loopback"; - clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => - { - Assert.Equal(certificate.Subject, cert.Subject); - Assert.Equal(certificate.Issuer, cert.Issuer); - Assert.Equal(SslPolicyErrors.RemoteCertificateNameMismatch, errors & SslPolicyErrors.RemoteCertificateNameMismatch); - return SslPolicyErrors.None == errors; - }; + X509Certificate2 certificate = _certificates.ServerCert; - await Assert.ThrowsAsync(async () => await CreateQuicConnection(clientOptions)); - } - finally + var quicOptions = new QuicListenerOptions() { - foreach (X509Certificate2 cert in chain) + ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), + ApplicationProtocols = new List() { ApplicationProtocol }, + ConnectionOptionsCallback = (_, _, _) => { - cert.Dispose(); + var serverOptions = CreateQuicServerOptions(); + serverOptions.ServerAuthenticationOptions.ServerCertificate = certificate; + return ValueTask.FromResult(serverOptions); } - certificate.Dispose(); - } + }; + await using QuicListener listener = await CreateQuicListener(quicOptions); + + QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listener.LocalEndPoint); + // Use different target host on purpose to get RemoteCertificateNameMismatch ssl error. + clientOptions.ClientAuthenticationOptions.TargetHost = "loopback"; + clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => + { + Assert.Equal(certificate.Subject, cert.Subject); + Assert.Equal(certificate.Issuer, cert.Issuer); + Assert.Equal(SslPolicyErrors.RemoteCertificateNameMismatch, errors & SslPolicyErrors.RemoteCertificateNameMismatch); + return SslPolicyErrors.None == errors; + }; + + await Assert.ThrowsAsync(async () => await CreateQuicConnection(clientOptions)); } [Fact] public async Task ConnectWithCertificate_MissingTargetHost_Succeeds() { - (X509Certificate2 certificate, X509Certificate2Collection chain) = Configuration.Certificates.GenerateCertificates("localhost"); - try - { - var quicOptions = new QuicListenerOptions() - { - // loopback may resolve to IPv6 - ListenEndPoint = new IPEndPoint(IPAddress.IPv6Any, 0), - ApplicationProtocols = new List() { ApplicationProtocol }, - ConnectionOptionsCallback = (_, _, _) => - { - var serverOptions = CreateQuicServerOptions(); - serverOptions.ServerAuthenticationOptions.ServerCertificate = certificate; - return ValueTask.FromResult(serverOptions); - } - }; - await using QuicListener listener = await CreateQuicListener(quicOptions); + X509Certificate2 certificate = _certificates.ServerCert; - QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(new DnsEndPoint("localhost", listener.LocalEndPoint.Port)); - // Do not set target host on client options, it should be taken from remote endpoint and used for both ClientHello SNI and Server cert validation - clientOptions.ClientAuthenticationOptions.TargetHost = null; - clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => - { - Assert.Equal(certificate.Subject, cert.Subject); - Assert.Equal(certificate.Issuer, cert.Issuer); - Assert.Equal(SslPolicyErrors.None, errors & SslPolicyErrors.RemoteCertificateNameMismatch); - return true; - }; - - await using QuicConnection connection = await CreateQuicConnection(clientOptions); - } - finally + var quicOptions = new QuicListenerOptions() { - foreach (X509Certificate2 cert in chain) + // loopback may resolve to IPv6 + ListenEndPoint = new IPEndPoint(IPAddress.IPv6Any, 0), + ApplicationProtocols = new List() { ApplicationProtocol }, + ConnectionOptionsCallback = (_, _, _) => { - cert.Dispose(); + var serverOptions = CreateQuicServerOptions(); + serverOptions.ServerAuthenticationOptions.ServerCertificate = certificate; + return ValueTask.FromResult(serverOptions); } - certificate.Dispose(); - } + }; + await using QuicListener listener = await CreateQuicListener(quicOptions); + + QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(new DnsEndPoint("localhost", listener.LocalEndPoint.Port)); + // Do not set target host on client options, it should be taken from remote endpoint and used for both ClientHello SNI and Server cert validation + clientOptions.ClientAuthenticationOptions.TargetHost = null; + clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => + { + Assert.Equal(certificate.Subject, cert.Subject); + Assert.Equal(certificate.Issuer, cert.Issuer); + Assert.Equal(SslPolicyErrors.None, errors & SslPolicyErrors.RemoteCertificateNameMismatch); + return true; + }; + + await using QuicConnection connection = await CreateQuicConnection(clientOptions); } [ConditionalTheory] @@ -603,43 +571,34 @@ public async Task ConnectWithCertificateForLoopbackIP_IndicatesExpectedError(str throw new SkipTestException("IPv6 is not available on this platform"); } - (X509Certificate2 certificate, X509Certificate2Collection chain) = Configuration.Certificates.GenerateCertificates(expectsError ? "badhost" : "localhost"); - try - { - var listenerOptions = new QuicListenerOptions() - { - ListenEndPoint = new IPEndPoint(ipAddress, 0), - ApplicationProtocols = new List() { ApplicationProtocol }, - ConnectionOptionsCallback = (_, _, _) => - { - var serverOptions = CreateQuicServerOptions(); - serverOptions.ServerAuthenticationOptions.ServerCertificate = certificate; - return ValueTask.FromResult(serverOptions); - } - }; - - // Use whatever endpoint, it'll get overwritten in CreateConnectedQuicConnection. - QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listenerOptions.ListenEndPoint); - clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => - { - Assert.Equal(certificate.Subject, cert.Subject); - Assert.Equal(certificate.Issuer, cert.Issuer); - Assert.Equal(expectsError ? SslPolicyErrors.RemoteCertificateNameMismatch : SslPolicyErrors.None, errors & SslPolicyErrors.RemoteCertificateNameMismatch); - return true; - }; + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(expectsError ? "badhost" : "localhost"); + X509Certificate2 certificate = pkiHolder.EndEntity; - (QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection(clientOptions, listenerOptions); - await clientConnection.DisposeAsync(); - await serverConnection.DisposeAsync(); - } - finally + var listenerOptions = new QuicListenerOptions() { - foreach (X509Certificate2 cert in chain) + ListenEndPoint = new IPEndPoint(ipAddress, 0), + ApplicationProtocols = new List() { ApplicationProtocol }, + ConnectionOptionsCallback = (_, _, _) => { - cert.Dispose(); + var serverOptions = CreateQuicServerOptions(); + serverOptions.ServerAuthenticationOptions.ServerCertificate = certificate; + return ValueTask.FromResult(serverOptions); } - certificate.Dispose(); - } + }; + + // Use whatever endpoint, it'll get overwritten in CreateConnectedQuicConnection. + QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listenerOptions.ListenEndPoint); + clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => + { + Assert.Equal(certificate.Subject, cert.Subject); + Assert.Equal(certificate.Issuer, cert.Issuer); + Assert.Equal(expectsError ? SslPolicyErrors.RemoteCertificateNameMismatch : SslPolicyErrors.None, errors & SslPolicyErrors.RemoteCertificateNameMismatch); + return true; + }; + + (QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection(clientOptions, listenerOptions); + await clientConnection.DisposeAsync(); + await serverConnection.DisposeAsync(); } public enum ClientCertSource @@ -724,85 +683,59 @@ public async Task ConnectWithClientCertificate(bool sendCertificate, ClientCertS [PlatformSpecific(TestPlatforms.Windows)] public async Task Server_CertificateWithEphemeralKey_Throws() { - (X509Certificate2 serverCertificate, X509Certificate2Collection chain) = Configuration.Certificates.GenerateCertificates(nameof(Server_CertificateWithEphemeralKey_Throws), ephemeralKey: true); - Configuration.Certificates.CleanupCertificates(nameof(Server_CertificateWithEphemeralKey_Throws)); + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(nameof(Server_CertificateWithEphemeralKey_Throws), ephemeralKey: true); - try + QuicListenerOptions listenerOptions = new QuicListenerOptions() { - QuicListenerOptions listenerOptions = new QuicListenerOptions() + ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), + ApplicationProtocols = new List() { ApplicationProtocol }, + ConnectionOptionsCallback = (_, _, _) => { - ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), - ApplicationProtocols = new List() { ApplicationProtocol }, - ConnectionOptionsCallback = (_, _, _) => - { - var serverOptions = CreateQuicServerOptions(); - serverOptions.ServerAuthenticationOptions.ServerCertificate = null; - serverOptions.ServerAuthenticationOptions.ServerCertificateContext = SslStreamCertificateContext.Create(serverCertificate, chain); - return ValueTask.FromResult(serverOptions); - } - }; - await using QuicListener listener = await CreateQuicListener(listenerOptions); + var serverOptions = CreateQuicServerOptions(); + serverOptions.ServerAuthenticationOptions.ServerCertificate = null; + serverOptions.ServerAuthenticationOptions.ServerCertificateContext = pkiHolder.CreateSslStreamCertificateContext(); + return ValueTask.FromResult(serverOptions); + } + }; + await using QuicListener listener = await CreateQuicListener(listenerOptions); - QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listener.LocalEndPoint); - clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = delegate { return true; }; + QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listener.LocalEndPoint); + clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = delegate { return true; }; - // client connection attempt will fail - await Assert.ThrowsAsync(async () => await CreateQuicConnection(clientOptions)); + // client connection attempt will fail + await Assert.ThrowsAsync(async () => await CreateQuicConnection(clientOptions)); - // server-side failure will be reported from AcceptConnectionAsync - AuthenticationException e = await Assert.ThrowsAsync(async () => await listener.AcceptConnectionAsync()); - Assert.Contains("ephemeral", e.Message); - } - finally - { - Configuration.Certificates.CleanupCertificates(nameof(Server_CertificateWithEphemeralKey_Throws)); - serverCertificate.Dispose(); - foreach (X509Certificate c in chain) - { - c.Dispose(); - } - } + // server-side failure will be reported from AcceptConnectionAsync + AuthenticationException e = await Assert.ThrowsAsync(async () => await listener.AcceptConnectionAsync()); + Assert.Contains("ephemeral", e.Message); } [ConditionalFact(typeof(QuicTestCollection), nameof(QuicTestCollection.IsUsingSchannelBackend))] [PlatformSpecific(TestPlatforms.Windows)] public async Task Client_CertificateWithEphemeralKey_Throws() { - (X509Certificate2 clientCertificate, X509Certificate2Collection chain) = Configuration.Certificates.GenerateCertificates(nameof(Client_CertificateWithEphemeralKey_Throws), ephemeralKey: true); - Configuration.Certificates.CleanupCertificates(nameof(Client_CertificateWithEphemeralKey_Throws)); + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(nameof(Client_CertificateWithEphemeralKey_Throws), ephemeralKey: true); - try + QuicListenerOptions listenerOptions = new QuicListenerOptions() { - QuicListenerOptions listenerOptions = new QuicListenerOptions() + ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), + ApplicationProtocols = new List() { ApplicationProtocol }, + ConnectionOptionsCallback = (_, _, _) => { - ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), - ApplicationProtocols = new List() { ApplicationProtocol }, - ConnectionOptionsCallback = (_, _, _) => - { - var serverOptions = CreateQuicServerOptions(); - serverOptions.ServerAuthenticationOptions.ClientCertificateRequired = true; - serverOptions.ServerAuthenticationOptions.RemoteCertificateValidationCallback = delegate { return true; }; - return ValueTask.FromResult(serverOptions); - } - }; - await using QuicListener listener = await CreateQuicListener(listenerOptions); + var serverOptions = CreateQuicServerOptions(); + serverOptions.ServerAuthenticationOptions.ClientCertificateRequired = true; + serverOptions.ServerAuthenticationOptions.RemoteCertificateValidationCallback = delegate { return true; }; + return ValueTask.FromResult(serverOptions); + } + }; + await using QuicListener listener = await CreateQuicListener(listenerOptions); - QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listener.LocalEndPoint); - clientOptions.ClientAuthenticationOptions.ClientCertificates = new X509CertificateCollection() { clientCertificate }; - clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = delegate { return true; }; + QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listener.LocalEndPoint); + clientOptions.ClientAuthenticationOptions.ClientCertificates = new X509CertificateCollection() { pkiHolder.EndEntity }; + clientOptions.ClientAuthenticationOptions.RemoteCertificateValidationCallback = delegate { return true; }; - AuthenticationException e = await Assert.ThrowsAsync(async () => await CreateQuicConnection(clientOptions)); - Assert.Contains("ephemeral", e.Message); - } - finally - { - Configuration.Certificates.CleanupCertificates(nameof(Client_CertificateWithEphemeralKey_Throws)); - clientCertificate.Dispose(); - foreach (X509Certificate c in chain) - { - c.Dispose(); - } - } + AuthenticationException e = await Assert.ThrowsAsync(async () => await CreateQuicConnection(clientOptions)); + Assert.Contains("ephemeral", e.Message); } [Theory] diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index 134419623e0c81..5ab3577f673e11 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -31,6 +31,8 @@ Link="Common\System\Obsoletions.cs" /> + diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs index 4c6d87735c8e24..0236ff306152bd 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs @@ -12,6 +12,13 @@ namespace System.Net.Security internal sealed class SslAuthenticationOptions { private const string EnableOcspStaplingContextSwitchName = "System.Net.Security.EnableServerOcspStaplingFromOnlyCertificateOnLinux"; + + internal static readonly X509RevocationMode DefaultRevocationMode = + AppContextSwitchHelper.GetBooleanConfig( + "System.Net.Security.NoRevocationCheckByDefault", + "DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT") + ? X509RevocationMode.NoCheck : X509RevocationMode.Online; + internal SslAuthenticationOptions() { TargetHost = string.Empty; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslClientAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslClientAuthenticationOptions.cs index 9f159207f93351..16700e0598dad1 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslClientAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslClientAuthenticationOptions.cs @@ -12,7 +12,7 @@ namespace System.Net.Security public class SslClientAuthenticationOptions { private EncryptionPolicy _encryptionPolicy = EncryptionPolicy.RequireEncryption; - private X509RevocationMode _checkCertificateRevocation = X509RevocationMode.NoCheck; + private X509RevocationMode _checkCertificateRevocation = SslAuthenticationOptions.DefaultRevocationMode; private SslProtocols _enabledSslProtocols = SslProtocols.None; private bool _allowRenegotiation = true; private bool _allowTlsResume = true; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs index 6ca64677652d50..940461b2632b2b 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslServerAuthenticationOptions.cs @@ -10,7 +10,7 @@ namespace System.Net.Security { public class SslServerAuthenticationOptions { - private X509RevocationMode _checkCertificateRevocation = X509RevocationMode.NoCheck; + private X509RevocationMode _checkCertificateRevocation = SslAuthenticationOptions.DefaultRevocationMode; private SslProtocols _enabledSslProtocols = SslProtocols.None; private EncryptionPolicy _encryptionPolicy = EncryptionPolicy.RequireEncryption; private bool _allowRenegotiation; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs index 6844e0d47189c7..eae09611396bca 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs @@ -185,7 +185,10 @@ public async Task CertificateValidationClientServer_EndToEnd_Ok(ClientCertSource TargetHost = Guid.NewGuid().ToString("N"), ClientCertificates = clientCerts, EnabledSslProtocols = SslProtocolSupport.DefaultSslProtocols, - CertificateChainPolicy = new X509ChainPolicy(), + CertificateChainPolicy = new X509ChainPolicy() + { + RevocationMode = X509RevocationMode.NoCheck, + } }; if (clientCertSource == ClientCertSource.CertificateContext) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateTrustTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateTrustTests.cs index e9b78bbc344862..ea3809f33f2e88 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateTrustTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateTrustTests.cs @@ -22,13 +22,13 @@ public class SslStreamCertificateTrustTest [SkipOnPlatform(TestPlatforms.Windows, "CertificateCollection-based SslCertificateTrust is not Supported on Windows")] public async Task SslStream_SendCertificateTrust_CertificateCollection() { - (X509Certificate2 certificate, X509Certificate2Collection caCerts) = Configuration.Certificates.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection)); + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection)); - SslCertificateTrust trust = SslCertificateTrust.CreateForX509Collection(caCerts, sendTrustInHandshake: true); + SslCertificateTrust trust = SslCertificateTrust.CreateForX509Collection(pkiHolder.IssuerChain, sendTrustInHandshake: true); string[] acceptableIssuers = await ConnectAndGatherAcceptableIssuers(trust); - Assert.Equal(caCerts.Count, acceptableIssuers.Length); - Assert.Equal(caCerts.Select(c => c.Subject), acceptableIssuers); + Assert.Equal(pkiHolder.IssuerChain.Count, acceptableIssuers.Length); + Assert.Equal(pkiHolder.IssuerChain.Select(c => c.Subject), acceptableIssuers); } [ConditionalFact(nameof(SupportsSendingCustomCANamesInTls))] @@ -94,20 +94,20 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( [PlatformSpecific(TestPlatforms.Windows)] public void SslStream_SendCertificateTrust_CertificateCollection_ThrowsOnWindows() { - (X509Certificate2 certificate, X509Certificate2Collection caCerts) = Configuration.Certificates.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection)); + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection)); - Assert.Throws(() => SslCertificateTrust.CreateForX509Collection(caCerts, sendTrustInHandshake: true)); + Assert.Throws(() => SslCertificateTrust.CreateForX509Collection(pkiHolder.IssuerChain, sendTrustInHandshake: true)); } [ConditionalFact(nameof(DoesNotSupportSendingCustomCANamesInTls))] [SkipOnPlatform(TestPlatforms.Windows, "Windows tested separately")] public void SslStream_SendCertificateTrust_ThrowsOnUnsupportedPlatform() { - (X509Certificate2 certificate, X509Certificate2Collection caCerts) = Configuration.Certificates.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection)); + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection)); using X509Store store = new X509Store("Root", StoreLocation.LocalMachine); - Assert.Throws(() => SslCertificateTrust.CreateForX509Collection(caCerts, sendTrustInHandshake: true)); + Assert.Throws(() => SslCertificateTrust.CreateForX509Collection(pkiHolder.IssuerChain, sendTrustInHandshake: true)); Assert.Throws(() => SslCertificateTrust.CreateForX509Store(store, sendTrustInHandshake: true)); } } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamFramingTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamFramingTest.cs index f83e1b7027ebba..e6946670f76904 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamFramingTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamFramingTest.cs @@ -91,7 +91,7 @@ public async Task Handshake_Success(FramingType framingType, SslProtocols sslPro { EnabledSslProtocols = sslProtocol, CertificateRevocationCheckMode = X509RevocationMode.NoCheck, - ServerCertificateContext = SslStreamCertificateContext.Create(_certificates.serverCert, _certificates.serverChain), + ServerCertificateContext = _certificates.CreateSslStreamCertificateContext(), RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true, ClientCertificateRequired = clientCertScenario == ClientCertScenario.InHandshake, }; @@ -102,7 +102,7 @@ public async Task Handshake_Success(FramingType framingType, SslProtocols sslPro EnabledSslProtocols = sslProtocol, CertificateRevocationCheckMode = X509RevocationMode.NoCheck, ClientCertificates = clientCertScenario != ClientCertScenario.None - ? new X509CertificateCollection { _certificates.serverCert } + ? new X509CertificateCollection { _certificates.ServerCert } : new X509CertificateCollection(), RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true, }; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index d5486ef526aa1a..aadf36b19d114f 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -22,22 +22,21 @@ namespace System.Net.Security.Tests public class CertificateSetup : IDisposable { - public readonly X509Certificate2 serverCert; - public readonly X509Certificate2Collection serverChain; + public X509Certificate2 ServerCert => _pkiHolder.EndEntity; + public X509Certificate2Collection ServerChain => _pkiHolder.IssuerChain; + + private readonly Configuration.Certificates.PkiHolder _pkiHolder; public CertificateSetup() { - TestHelper.CleanupCertificates(nameof(SslStreamNetworkStreamTest)); - (serverCert, serverChain) = Configuration.Certificates.GenerateCertificates("localhost", nameof(SslStreamNetworkStreamTest), longChain: true); + _pkiHolder = Configuration.Certificates.GenerateCertificates("localhost", nameof(SslStreamNetworkStreamTest), longChain: true); } + public SslStreamCertificateContext CreateSslStreamCertificateContext() => _pkiHolder.CreateSslStreamCertificateContext(); + public void Dispose() { - serverCert.Dispose(); - foreach (var c in serverChain) - { - c.Dispose(); - } + _pkiHolder.Dispose(); } } @@ -760,7 +759,7 @@ public async Task SslStream_ServerUntrustedCaWithCustomTrust_OK(bool usePartialC throw new SkipTestException("Android does not support partial chain validation."); } - int split = Random.Shared.Next(0, _certificates.serverChain.Count - 1); + int split = Random.Shared.Next(0, _certificates.ServerChain.Count - 1); var clientOptions = new SslClientAuthenticationOptions() { TargetHost = "localhost" }; clientOptions.CertificateChainPolicy = new X509ChainPolicy() @@ -768,14 +767,14 @@ public async Task SslStream_ServerUntrustedCaWithCustomTrust_OK(bool usePartialC RevocationMode = X509RevocationMode.NoCheck, TrustMode = X509ChainTrustMode.CustomRootTrust }; - clientOptions.CertificateChainPolicy.CustomTrustStore.Add(_certificates.serverChain[_certificates.serverChain.Count - 1]); + clientOptions.CertificateChainPolicy.CustomTrustStore.Add(_certificates.ServerChain[_certificates.ServerChain.Count - 1]); // Add only one CA to verify that peer did send intermediate CA cert. // In case of partial chain, we need to make missing certs available. if (usePartialChain) { - for (int i = split; i < _certificates.serverChain.Count - 1; i++) + for (int i = split; i < _certificates.ServerChain.Count - 1; i++) { - clientOptions.CertificateChainPolicy.ExtraStore.Add(_certificates.serverChain[i]); + clientOptions.CertificateChainPolicy.ExtraStore.Add(_certificates.ServerChain[i]); } } @@ -787,15 +786,15 @@ public async Task SslStream_ServerUntrustedCaWithCustomTrust_OK(bool usePartialC serverChain = new X509Certificate2Collection(); for (int i = 0; i < split; i++) { - serverChain.Add(_certificates.serverChain[i]); + serverChain.Add(_certificates.ServerChain[i]); } } else { - serverChain = _certificates.serverChain; + serverChain = _certificates.ServerChain; } - serverOptions.ServerCertificateContext = SslStreamCertificateContext.Create(_certificates.serverCert, serverChain); + serverOptions.ServerCertificateContext = SslStreamCertificateContext.Create(_certificates.ServerCert, serverChain); (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams(); using (clientStream) @@ -866,13 +865,15 @@ public async Task SslStream_ClientCertificate_SendsChain() StoreName storeName = OperatingSystem.IsMacOS() ? StoreName.My : StoreName.CertificateAuthority; List streams = new List(); TestHelper.CleanupCertificates(nameof(SslStream_ClientCertificate_SendsChain), storeName); - (X509Certificate2 clientCertificate, X509Certificate2Collection clientChain) = Configuration.Certificates.GenerateCertificates(nameof(SslStream_ClientCertificate_SendsChain), serverCertificate: false); + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(nameof(SslStream_ClientCertificate_SendsChain), serverCertificate: false); + X509Certificate2 clientCertificate = pkiHolder.EndEntity; + X509Certificate2Collection clientChain = pkiHolder.IssuerChain; using (X509Store store = new X509Store(storeName, 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.AddRange(clientChain); + store.AddRange(pkiHolder.IssuerChain); store.Close(); } @@ -908,13 +909,6 @@ public async Task SslStream_ClientCertificate_SendsChain() clientOptions.LocalCertificateSelectionCallback = (sender, target, certificates, remoteCertificate, issuers) => clientCertificate; await SslStream_ClientSendsChain_Core(clientOptions, clientChain); - - TestHelper.CleanupCertificates(nameof(SslStream_ClientCertificate_SendsChain), storeName); - clientCertificate.Dispose(); - foreach (X509Certificate c in clientChain) - { - c.Dispose(); - } } [Theory] @@ -922,7 +916,7 @@ public async Task SslStream_ClientCertificate_SendsChain() [InlineData(false)] public async Task SslStream_ClientCertificateContext_SendsChain(bool useTrust) { - (X509Certificate2 clientCertificate, X509Certificate2Collection clientChain) = Configuration.Certificates.GenerateCertificates(nameof(SslStream_ClientCertificateContext_SendsChain), serverCertificate: false); + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(nameof(SslStream_ClientCertificateContext_SendsChain), serverCertificate: false); TestHelper.CleanupCertificates(nameof(SslStream_ClientCertificateContext_SendsChain)); SslCertificateTrust? trust = null; @@ -930,32 +924,24 @@ public async Task SslStream_ClientCertificateContext_SendsChain(bool useTrust) { // This is simplification. We make all the intermediates trusted, // normally just the root would go here. - trust = SslCertificateTrust.CreateForX509Collection(clientChain, false); + trust = SslCertificateTrust.CreateForX509Collection(pkiHolder.IssuerChain, false); } var clientOptions = new SslClientAuthenticationOptions() { TargetHost = "localhost", + RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true, + ClientCertificateContext = SslStreamCertificateContext.Create(pkiHolder.EndEntity, useTrust ? null : pkiHolder.IssuerChain, offline: true, trust), }; - clientOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; - clientOptions.ClientCertificateContext = SslStreamCertificateContext.Create(clientCertificate, useTrust ? null : clientChain, offline: true, trust); - await SslStream_ClientSendsChain_Core(clientOptions, clientChain); - - TestHelper.CleanupCertificates(nameof(SslStream_ClientCertificateContext_SendsChain)); - clientCertificate.Dispose(); - foreach (X509Certificate c in clientChain) - { - c.Dispose(); - } + await SslStream_ClientSendsChain_Core(clientOptions, pkiHolder.IssuerChain); } [Fact] [PlatformSpecific(TestPlatforms.Windows)] public async Task SslStream_EphemeralKey_Throws() { - (X509Certificate2 serverCertificate, X509Certificate2Collection chain) = Configuration.Certificates.GenerateCertificates(nameof(SslStream_EphemeralKey_Throws), ephemeralKey: true); - TestHelper.CleanupCertificates(nameof(SslStream_EphemeralKey_Throws)); + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(nameof(SslStream_EphemeralKey_Throws), ephemeralKey: true); var clientOptions = new SslClientAuthenticationOptions() { @@ -965,7 +951,7 @@ public async Task SslStream_EphemeralKey_Throws() var serverOptions = new SslServerAuthenticationOptions() { - ServerCertificate = serverCertificate + ServerCertificate = pkiHolder.EndEntity }; (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(); @@ -978,13 +964,6 @@ public async Task SslStream_EphemeralKey_Throws() server.Dispose(); await Assert.ThrowsAsync(() => t1); client.Dispose(); - - TestHelper.CleanupCertificates(nameof(SslStream_EphemeralKey_Throws)); - serverCertificate.Dispose(); - foreach (X509Certificate c in chain) - { - c.Dispose(); - } } [Theory] diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamRemoteExecutorTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamRemoteExecutorTests.cs index 87ec6605e37aad..62cb061f6390d3 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamRemoteExecutorTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamRemoteExecutorTests.cs @@ -75,5 +75,31 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( Assert.True(File.ReadAllText(tempFile).Length == 0); } } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void DefaultRevocationMode_OfflineRevocationByDefault_True_UsesNoCheck(bool useEnvVar) + { + var psi = new ProcessStartInfo(); + if (useEnvVar) + { + psi.Environment.Add("DOTNET_SYSTEM_NET_SECURITY_NOREVOCATIONCHECKBYDEFAULT", "true"); + } + + Assert.Equal(X509RevocationMode.Online, new SslClientAuthenticationOptions().CertificateRevocationCheckMode); + Assert.Equal(X509RevocationMode.Online, new SslServerAuthenticationOptions().CertificateRevocationCheckMode); + + RemoteExecutor.Invoke(useEnvVar => + { + if (!bool.Parse(useEnvVar)) + { + AppContext.SetSwitch("System.Net.Security.NoRevocationCheckByDefault", true); + } + + Assert.Equal(X509RevocationMode.NoCheck, new SslClientAuthenticationOptions().CertificateRevocationCheckMode); + Assert.Equal(X509RevocationMode.NoCheck, new SslServerAuthenticationOptions().CertificateRevocationCheckMode); + }, useEnvVar.ToString(), new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + } } } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs index 72587240202979..a510fedaf47fd6 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs @@ -27,7 +27,8 @@ public async Task SslStream_ClientSendsSNIServerReceives_Ok(string hostName) await WithVirtualConnection(async (server, client) => { - Task clientJob = Task.Run(() => { + Task clientJob = Task.Run(() => + { client.AuthenticateAsClient(hostName); }); @@ -79,7 +80,8 @@ public async Task SslStream_ServerCallbackAndLocalCertificateSelectionSet_Throws using (SslStream server = new SslStream(stream1, false, null, selectionCallback), client = new SslStream(stream2, leaveInnerStreamOpen: false, validationCallback)) { - Task clientJob = Task.Run(() => { + Task clientJob = Task.Run(() => + { client.AuthenticateAsClient(hostName); Assert.Fail("RemoteCertificateValidationCallback called when AuthenticateAsServerAsync was expected to fail."); }); @@ -127,7 +129,8 @@ public async Task SslStream_ServerCallbackNotSet_UsesLocalCertificateSelection(s using (SslStream server = new SslStream(stream1, false, null, selectionCallback), client = new SslStream(stream2, leaveInnerStreamOpen: false, validationCallback)) { - Task clientJob = Task.Run(() => { + Task clientJob = Task.Run(() => + { client.AuthenticateAsClient(hostName); }); @@ -147,7 +150,8 @@ public async Task SslStream_NoSniFromClient_CallbackReturnsNull() { await WithVirtualConnection(async (server, client) => { - Task clientJob = Task.Run(() => { + Task clientJob = Task.Run(() => + { Assert.Throws(() => client.AuthenticateAsClient("test") ); @@ -189,8 +193,8 @@ public async Task SslStream_IpLiteral_NotSend(string target) (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(); SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions() { - TargetHost = target, - RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true, + TargetHost = target, + RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true, }; SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions() { @@ -241,41 +245,33 @@ public async Task UnencodedHostName_ValidatesCertificate() string rawHostname = "räksmörgås.josefsson.org"; string punycodeHostname = "xn--rksmrgs-5wao1o.josefsson.org"; - var (serverCert, serverChain) = Configuration.Certificates.GenerateCertificates(punycodeHostname); - try + using Configuration.Certificates.PkiHolder pkiHolder = Configuration.Certificates.GenerateCertificates(punycodeHostname); + + SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions() { - SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions() - { - ServerCertificateContext = SslStreamCertificateContext.Create(serverCert, serverChain), - }; + ServerCertificateContext = pkiHolder.CreateSslStreamCertificateContext(), + }; - SslClientAuthenticationOptions clientOptions = new () + SslClientAuthenticationOptions clientOptions = new() + { + TargetHost = rawHostname, + CertificateChainPolicy = new X509ChainPolicy() { - TargetHost = rawHostname, - CertificateChainPolicy = new X509ChainPolicy() - { - RevocationMode = X509RevocationMode.NoCheck, - TrustMode = X509ChainTrustMode.CustomRootTrust, - CustomTrustStore = { serverChain[serverChain.Count - 1] } - } - }; + RevocationMode = X509RevocationMode.NoCheck, + TrustMode = X509ChainTrustMode.CustomRootTrust, + CustomTrustStore = { pkiHolder.IssuerChain[^1] } + } + }; - (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(); + (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(); - await TestConfiguration.WhenAllOrAnyFailedWithTimeout( - client.AuthenticateAsClientAsync(clientOptions, default), - server.AuthenticateAsServerAsync(serverOptions, default)); + await TestConfiguration.WhenAllOrAnyFailedWithTimeout( + client.AuthenticateAsClientAsync(clientOptions, default), + server.AuthenticateAsServerAsync(serverOptions, default)); - await TestHelper.PingPong(client, server, default); - Assert.Equal(rawHostname, server.TargetHostName); - Assert.Equal(rawHostname, client.TargetHostName); - } - finally - { - serverCert.Dispose(); - foreach (var c in serverChain) c.Dispose(); - TestHelper.CleanupCertificates(rawHostname); - } + await TestHelper.PingPong(client, server, default); + Assert.Equal(rawHostname, server.TargetHostName); + Assert.Equal(rawHostname, client.TargetHostName); } [Theory] @@ -333,7 +329,8 @@ public async Task SslStream_UnsafeInvalidIdn_Throws(string name) private static Func WithAggregateExceptionUnwrapping(Func a) { - return async () => { + return async () => + { try { await a(); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs index 4ff9eb1835ef63..d495f9dfc268b7 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs @@ -461,7 +461,7 @@ public sealed class SslStreamStreamToStreamTest_Async : SslStreamStreamToStreamT protected override async Task DoHandshake(SslStream clientSslStream, SslStream serverSslStream, X509Certificate serverCertificate = null, X509Certificate clientCertificate = null) { X509CertificateCollection clientCerts = clientCertificate != null ? new X509CertificateCollection() { clientCertificate } : null; - await WithServerCertificate(serverCertificate, async(certificate, name) => + await WithServerCertificate(serverCertificate, async (certificate, name) => { Task t1 = clientSslStream.AuthenticateAsClientAsync(name, clientCerts, SslProtocols.None, checkCertificateRevocation: false); Task t2 = serverSslStream.AuthenticateAsServerAsync(certificate, clientCertificateRequired: clientCertificate != null, checkCertificateRevocation: false); @@ -626,10 +626,13 @@ await WithServerCertificate(serverCertificate, async (certificate, name) => TargetHost = name, ClientCertificates = clientCerts, EnabledSslProtocols = SslProtocols.None, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck }; SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions() { - ServerCertificate = certificate, ClientCertificateRequired = clientCertificate != null, + ServerCertificate = certificate, + ClientCertificateRequired = clientCertificate != null, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck }; Task t1 = Task.Run(() => clientSslStream.AuthenticateAsClient(clientOptions)); Task t2 = Task.Run(() => serverSslStream.AuthenticateAsServer(serverOptions)); @@ -643,10 +646,20 @@ public sealed class SslStreamStreamToStreamTest_MemoryAsync : SslStreamStreamToS protected override async Task DoHandshake(SslStream clientSslStream, SslStream serverSslStream, X509Certificate serverCertificate = null, X509Certificate clientCertificate = null) { X509CertificateCollection clientCerts = clientCertificate != null ? new X509CertificateCollection() { clientCertificate } : null; - await WithServerCertificate(serverCertificate, async(certificate, name) => + await WithServerCertificate(serverCertificate, async (certificate, name) => { - Task t1 = clientSslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions() { TargetHost = name, ClientCertificates = clientCerts }, CancellationToken.None); - Task t2 = serverSslStream.AuthenticateAsServerAsync(new SslServerAuthenticationOptions() { ServerCertificate = certificate, ClientCertificateRequired = clientCertificate != null }, CancellationToken.None); + Task t1 = clientSslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions() + { + TargetHost = name, + ClientCertificates = clientCerts, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck + }, CancellationToken.None); + Task t2 = serverSslStream.AuthenticateAsServerAsync(new SslServerAuthenticationOptions() + { + ServerCertificate = certificate, + ClientCertificateRequired = clientCertificate != null, + CertificateRevocationCheckMode = X509RevocationMode.NoCheck + }, CancellationToken.None); await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); }); } diff --git a/src/libraries/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs b/src/libraries/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs index b7e9d29d9dda98..22c8bcfffae93d 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs +++ b/src/libraries/System.Net.Security/tests/UnitTests/SslAuthenticationOptionsTests.cs @@ -133,13 +133,13 @@ public void EnabledSslProtocols_Get_Set_Succeeds() [Fact] public void CheckCertificateRevocation_Get_Set_Succeeds() { - Assert.Equal(X509RevocationMode.NoCheck, _clientOptions.CertificateRevocationCheckMode); - Assert.Equal(X509RevocationMode.NoCheck, _serverOptions.CertificateRevocationCheckMode); + Assert.Equal(X509RevocationMode.Online, _clientOptions.CertificateRevocationCheckMode); + Assert.Equal(X509RevocationMode.Online, _serverOptions.CertificateRevocationCheckMode); - _clientOptions.CertificateRevocationCheckMode = X509RevocationMode.Online; + _clientOptions.CertificateRevocationCheckMode = X509RevocationMode.NoCheck; _serverOptions.CertificateRevocationCheckMode = X509RevocationMode.Offline; - Assert.Equal(X509RevocationMode.Online, _clientOptions.CertificateRevocationCheckMode); + Assert.Equal(X509RevocationMode.NoCheck, _clientOptions.CertificateRevocationCheckMode); Assert.Equal(X509RevocationMode.Offline, _serverOptions.CertificateRevocationCheckMode); Assert.Throws(() => _clientOptions.CertificateRevocationCheckMode = (X509RevocationMode)3); diff --git a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj index efa3cd671c53dd..42381dbab6c6b8 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj @@ -34,6 +34,8 @@ +