Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -936,10 +936,23 @@ private static unsafe int NewSessionCallback(IntPtr ssl, IntPtr session)
Debug.Assert(ssl != IntPtr.Zero);
Debug.Assert(session != IntPtr.Zero);

// remember if the session used a certificate, this information is used after
// session resumption, the pointer is not being dereferenced and the refcount
// Remember if the session used a certificate, this information is used after
// session resumption. The pointer is not being dereferenced and the refcount
// is not going to be manipulated.
IntPtr cert = Interop.Ssl.SslGetCertificate(ssl);

// In TLS 1.3, new session tickets can be issued on resumed connections.
// When resuming, no certificate is set on the SSL object, so inherit
// the cert info from the current (resuming) session.
if (cert == IntPtr.Zero && Interop.Ssl.SslSessionReused(ssl))
{
IntPtr currentSession = Interop.Ssl.SslGetSession(ssl);
if (currentSession != IntPtr.Zero)
{
cert = Interop.Ssl.SslSessionGetData(currentSession);
}
}

Interop.Ssl.SslSessionSetData(session, cert);

IntPtr ptr = Ssl.SslGetData(ssl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,16 @@ internal static SafeSharedX509StackHandle SslGetPeerCertChain(SafeSslHandle ssl)
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool SslSessionReused(SafeSslHandle ssl);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslSessionReused")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool SslSessionReused(IntPtr ssl);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGetSession")]
internal static partial IntPtr SslGetSession(SafeSslHandle ssl);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGetSession")]
internal static partial IntPtr SslGetSession(IntPtr ssl);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGetClientCAList")]
private static partial SafeSharedX509NameStackHandle SslGetClientCAList_private(SafeSslHandle ssl);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.IO;
Expand Down Expand Up @@ -392,6 +392,54 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
}
}

[Theory]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
public async Task SslStream_Tls13ResumptionWithClientCert_IsMutuallyAuthenticatedTrue(
SslProtocols protocol)
{
string targetHost = Guid.NewGuid().ToString("N");

var clientOptions = new SslClientAuthenticationOptions
{
TargetHost = targetHost,
ClientCertificates = new X509CertificateCollection { _clientCertificate },
EnabledSslProtocols = protocol,
RemoteCertificateValidationCallback = AllowAnyCertificate,
};

var serverOptions = new SslServerAuthenticationOptions
{
ServerCertificate = _serverCertificate,
ClientCertificateRequired = true,
EnabledSslProtocols = protocol,
RemoteCertificateValidationCallback = AllowAnyCertificate,
};

for (int i = 0; i < 3; i++)
{
(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
using (client)
using (server)
{
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions),
server.AuthenticateAsServerAsync(serverOptions));

// PingPong triggers new session ticket delivery (TLS 1.3)
await TestHelper.PingPong(client, server);

// Regression test: all connections (including resumed ones) must report mutual auth
Assert.True(client.IsMutuallyAuthenticated, $"Client connection {i}: IsMutuallyAuthenticated should be true");
Assert.True(server.IsMutuallyAuthenticated, $"Server connection {i}: IsMutuallyAuthenticated should be true");
Assert.NotNull(client.LocalCertificate);
Assert.NotNull(server.RemoteCertificate);

await client.ShutdownAsync();
await server.ShutdownAsync();
}
}
}

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