Skip to content

Commit

Permalink
fix TLS resume with client certificates (#81795)
Browse files Browse the repository at this point in the history
  • Loading branch information
wfurt authored Feb 8, 2023
1 parent 2508b20 commit 6cad173
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,8 @@ internal static SafeSslHandle AllocateSslHandle(SslAuthenticationOptions sslAuth
if (!Interop.Ssl.Capabilities.Tls13Supported ||
string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) ||
sslAuthenticationOptions.CertificateContext != null ||
sslAuthenticationOptions.CertSelectionDelegate != null)
sslAuthenticationOptions.ClientCertificates?.Count > 0 ||
sslAuthenticationOptions.CertSelectionDelegate != null)
{
cacheSslContext = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.IO;
using System.Threading.Tasks;
using System.Net.Test.Common;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

using Xunit;
Expand All @@ -16,11 +17,13 @@ public class SslStreamMutualAuthenticationTest : IDisposable
{
private readonly X509Certificate2 _clientCertificate;
private readonly X509Certificate2 _serverCertificate;
private readonly X509Certificate2 _selfSignedCertificate;

public SslStreamMutualAuthenticationTest()
{
_serverCertificate = Configuration.Certificates.GetServerCertificate();
_clientCertificate = Configuration.Certificates.GetClientCertificate();
_selfSignedCertificate = Configuration.Certificates.GetSelfSignedServerCertificate();
}

public void Dispose()
Expand Down Expand Up @@ -80,6 +83,171 @@ public async Task SslStream_RequireClientCert_IsMutuallyAuthenticated_ReturnsTru
}
}

[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Linux)] // https://github.com/dotnet/runtime/issues/65563
[Theory]
public async Task SslStream_ResumedSessionsClientCollection_IsMutuallyAuthenticatedCorrect(
SslProtocols protocol)
{
var clientOptions = new SslClientAuthenticationOptions
{
EnabledSslProtocols = protocol,
RemoteCertificateValidationCallback = delegate { return true; },
TargetHost = Guid.NewGuid().ToString("N")
};

// Create options with certificate context so TLS resume is possible on Linux
var serverOptions = new SslServerAuthenticationOptions
{
ClientCertificateRequired = true,
ServerCertificateContext = SslStreamCertificateContext.Create(_serverCertificate, null),
RemoteCertificateValidationCallback = delegate { return true; },
EnabledSslProtocols = protocol
};

for (int i = 0; i < 5; i++)
{
(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
using (client)
using (server)
{
bool expectMutualAuthentication = (i % 2) == 0;

clientOptions.ClientCertificates = expectMutualAuthentication ? new X509CertificateCollection() { _clientCertificate } : null;
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions),
server.AuthenticateAsServerAsync(serverOptions));

// mutual authentication should only be set if client set certificate
Assert.Equal(expectMutualAuthentication, server.IsMutuallyAuthenticated);
Assert.Equal(expectMutualAuthentication, client.IsMutuallyAuthenticated);

if (expectMutualAuthentication)
{
Assert.NotNull(server.RemoteCertificate);
}
else
{
Assert.Null(server.RemoteCertificate);
}
};
}
}

[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Linux)] // https://github.com/dotnet/runtime/issues/65563
[Theory]
public async Task SslStream_ResumedSessionsCallbackSet_IsMutuallyAuthenticatedCorrect(
SslProtocols protocol)
{
var clientOptions = new SslClientAuthenticationOptions
{
EnabledSslProtocols = protocol,
RemoteCertificateValidationCallback = delegate { return true; },
TargetHost = Guid.NewGuid().ToString("N")
};

// Create options with certificate context so TLS resume is possible on Linux
var serverOptions = new SslServerAuthenticationOptions
{
ClientCertificateRequired = true,
ServerCertificateContext = SslStreamCertificateContext.Create(_serverCertificate, null),
RemoteCertificateValidationCallback = delegate { return true; },
EnabledSslProtocols = protocol
};

for (int i = 0; i < 5; i++)
{
(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
using (client)
using (server)
{
bool expectMutualAuthentication = (i % 2) == 0;

clientOptions.LocalCertificateSelectionCallback = (s, t, l, r, a) =>
{
return expectMutualAuthentication ? _clientCertificate : null;
};

await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions),
server.AuthenticateAsServerAsync(serverOptions));

// mutual authentication should only be set if client set certificate
Assert.Equal(expectMutualAuthentication, server.IsMutuallyAuthenticated);
Assert.Equal(expectMutualAuthentication, client.IsMutuallyAuthenticated);

if (expectMutualAuthentication)
{
Assert.NotNull(server.RemoteCertificate);
}
else
{
Assert.Null(server.RemoteCertificate);
}
};
}
}

[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
[PlatformSpecific(TestPlatforms.Linux)] // https://github.com/dotnet/runtime/issues/65563
[Theory]
public async Task SslStream_ResumedSessionsCallbackMaybeSet_IsMutuallyAuthenticatedCorrect(
SslProtocols protocol)
{
var clientOptions = new SslClientAuthenticationOptions
{
EnabledSslProtocols = protocol,
RemoteCertificateValidationCallback = delegate { return true; },
TargetHost = Guid.NewGuid().ToString("N")
};

// Create options with certificate context so TLS resume is possible on Linux
var serverOptions = new SslServerAuthenticationOptions
{
ClientCertificateRequired = true,
ServerCertificateContext = SslStreamCertificateContext.Create(_serverCertificate, null),
RemoteCertificateValidationCallback = delegate { return true; },
EnabledSslProtocols = protocol
};

for (int i = 0; i < 5; i++)
{
(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
using (client)
using (server)
{
bool expectMutualAuthentication = (i % 2) == 0;

if (expectMutualAuthentication)
{
clientOptions.LocalCertificateSelectionCallback = (s, t, l, r, a) => _clientCertificate;
}
else
{
clientOptions.LocalCertificateSelectionCallback = null;
}

await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions),
server.AuthenticateAsServerAsync(serverOptions));

// mutual authentication should only be set if client set certificate
Assert.Equal(expectMutualAuthentication, server.IsMutuallyAuthenticated);
Assert.Equal(expectMutualAuthentication, client.IsMutuallyAuthenticated);

if (expectMutualAuthentication)
{
Assert.NotNull(server.RemoteCertificate);
}
else
{
Assert.Null(server.RemoteCertificate);
}
};
}
}

private static bool AllowAnyCertificate(
object sender,
X509Certificate certificate,
Expand Down

0 comments on commit 6cad173

Please sign in to comment.