Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -298,6 +298,10 @@ private static bool QueryCertContextAttribute(ISSPIInterface secModule, SafeDele
public static bool QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CONTEXT(ISSPIInterface secModule, SafeDeleteContext securityContext, out SafeFreeCertContext? certContext)
=> QueryCertContextAttribute(secModule, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CONTEXT, out certContext);

public static bool QueryContextAttributes_SECPKG_ATTR_LOCAL_CERT_CONTEXT(ISSPIInterface secModule, SafeDeleteContext securityContext, out SafeFreeCertContext? certContext)
=> QueryCertContextAttribute(secModule, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_LOCAL_CERT_CONTEXT, out certContext);


public static bool QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CHAIN(ISSPIInterface secModule, SafeDeleteContext securityContext, out SafeFreeCertContext? certContext)
=> QueryCertContextAttribute(secModule, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CHAIN, out certContext);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ internal static SslPolicyErrors VerifyCertificateProperties(
return cert;
}

// This is only called when we selected local client certificate.
// Currently this is only when Java crypto asked for it.
internal static bool IsLocalCertificateUsed(SafeDeleteContext? _) => true;

//
// Used only by client SSL code, never returns null.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ internal static SslPolicyErrors VerifyCertificateProperties(
return result;
}

// This is only called when we selected local client certificate.
// Currently this is only when Apple crypto asked for it.
internal static bool IsLocalCertificateUsed(SafeDeleteContext? _) => true;

//
// Used only by client SSL code, never returns null.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ internal static SslPolicyErrors VerifyCertificateProperties(
return result;
}

// This is only called when we selected local client certificate.
// Currently this is only when OpenSSL needs it becaseu peer asked.
internal static bool IsLocalCertificateUsed(SafeDeleteContext? _) => true;

//
// Used only by client SSL code, never returns null.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,28 @@ internal static SslPolicyErrors VerifyCertificateProperties(
return result;
}

// Check that local certificate was used by schannel.
internal static bool IsLocalCertificateUsed(SafeDeleteContext securityContext)
{
SafeFreeCertContext? localContext = null;
try
{
if (SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_LOCAL_CERT_CONTEXT(GlobalSSPI.SSPISecureChannel, securityContext, out localContext) &&
localContext != null)
{
return !localContext.IsInvalid;
}
}
finally
{
localContext?.Dispose();
}

// Some older Windows do not support that. This is only called when client certificate was provided
// so assume it was for a reason.
return true;
}

//
// Used only by client SSL code, never returns null.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,6 @@ private bool AcquireClientCredentials(ref byte[]? thumbPrint)
bool cachedCred = false; // this is a return result from this method.

X509Certificate2? selectedCert = SelectClientCertificate(out sessionRestartAttempt);

try
{
// Try to locate cached creds first.
Expand Down Expand Up @@ -977,6 +976,12 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot
}

_remoteCertificate = certificate;
if (_selectedClientCertificate != null && !CertificateValidationPal.IsLocalCertificateUsed(_securityContext!))
{
// We may slect client cert but it may not be used.
// This is primarily issue on Windows with credential caching
_selectedClientCertificate = null;
}

if (_remoteCertificate == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
using System.IO;
using System.Threading.Tasks;
using System.Net.Test.Common;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

using Xunit;
using System.Runtime.InteropServices;

namespace System.Net.Security.Tests
{
Expand All @@ -16,11 +18,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 +84,46 @@ public async Task SslStream_RequireClientCert_IsMutuallyAuthenticated_ReturnsTru
}
}

[Theory]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
public async Task SslStream_CachedCredentials_IsMutuallyAuthenticatedCorrect(
SslProtocols protocol)
{
var clientOptions = new SslClientAuthenticationOptions
{
ClientCertificates = new X509CertificateCollection() { _clientCertificate },
EnabledSslProtocols = protocol,
RemoteCertificateValidationCallback = delegate { return true; },
TargetHost = Guid.NewGuid().ToString("N")
};

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

var serverOptions = new SslServerAuthenticationOptions
{
ClientCertificateRequired = expectMutualAuthentication,
ServerCertificate = expectMutualAuthentication ? _serverCertificate : _selfSignedCertificate,
RemoteCertificateValidationCallback = delegate { return true; },
EnabledSslProtocols = protocol
};

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

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

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