namespace System.Net.Security
{
- internal delegate X509Certificate2? SelectClientCertificate(out bool sessionRestartAttempt);
-
public partial class SslStream
{
private SafeFreeCredentials? _credentialsHandle;
return issuers;
}
- internal X509Certificate2? SelectClientCertificate(out bool sessionRestartAttempt)
+ internal X509Certificate2? SelectClientCertificate()
{
- sessionRestartAttempt = false;
-
X509Certificate? clientCertificate = null; // candidate certificate that can come from the user callback or be guessed when targeting a session restart.
X509Certificate2? selectedCert = null; // final selected cert (ensured that it does have private key with it).
List<X509Certificate>? filteredCerts = null; // This is an intermediate client certs collection that try to use if no selectedCert is available yet.
// private key, so we don't have to do any further processing.
//
- sessionRestartAttempt = _credentialsHandle == null;
_selectedClientCertificate = _sslAuthenticationOptions.CertificateContext.Certificate;
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Selected cert = {_selectedClientCertificate}");
return _sslAuthenticationOptions.CertificateContext.Certificate;
if (clientCertificate != null)
{
- if (_credentialsHandle == null)
- {
- sessionRestartAttempt = true;
- }
-
EnsureInitialized(ref filteredCerts).Add(clientCertificate);
if (NetEventSource.Log.IsEnabled())
NetEventSource.Log.CertificateFromDelegate(this);
{
if (NetEventSource.Log.IsEnabled())
NetEventSource.Log.NoDelegateNoClientCert(this);
-
- sessionRestartAttempt = true;
}
else
{
// This is where we attempt to restart a session by picking the FIRST cert from the collection.
// Otherwise it is either server sending a client cert request or the session is renegotiated.
clientCertificate = _sslAuthenticationOptions.ClientCertificates[0];
- sessionRestartAttempt = true;
if (clientCertificate != null)
{
EnsureInitialized(ref filteredCerts).Add(clientCertificate);
private bool AcquireClientCredentials(ref byte[]? thumbPrint, bool newCredentialsRequested = false)
{
// Acquire possible Client Certificate information and set it on the handle.
-
- bool sessionRestartAttempt; // If true and no cached creds we will use anonymous creds.
bool cachedCred = false; // this is a return result from this method.
- X509Certificate2? selectedCert = SelectClientCertificate(out sessionRestartAttempt);
+ X509Certificate2? selectedCert = SelectClientCertificate();
+
+ if (newCredentialsRequested)
+ {
+ if (selectedCert != null)
+ {
+ // build the cert context only if it was not provided by the user
+ _sslAuthenticationOptions.CertificateContext ??= SslStreamCertificateContext.Create(selectedCert);
+ }
+
+ if (SslStreamPal.TryUpdateClintCertificate(_credentialsHandle, _securityContext, _sslAuthenticationOptions))
+ {
+ // If the certificate was updated we do not need to deal with the credential handle.
+ return false;
+ }
+ }
+
try
{
// Try to locate cached creds first.
// We can probably do some optimization here. If the selectedCert is returned by the delegate
// we can always go ahead and use the certificate to create our credential
// (instead of going anonymous as we do here).
- if (sessionRestartAttempt &&
+ if (!newCredentialsRequested &&
cachedCredentialHandle == null &&
selectedCert != null &&
SslStreamPal.StartMutualAuthAsAnonymous)
_sslAuthenticationOptions.TargetHost,
inputBuffer,
ref result,
- _sslAuthenticationOptions,
- SelectClientCertificate
- );
+ _sslAuthenticationOptions);
if (status.ErrorCode == SecurityStatusPalErrorCode.CredentialsNeeded)
{
ref _credentialsHandle!,
ref _securityContext,
_sslAuthenticationOptions.TargetHost,
- inputBuffer,
+ ReadOnlySpan<byte>.Empty,
ref result,
- _sslAuthenticationOptions,
- SelectClientCertificate);
+ _sslAuthenticationOptions);
}
}
} while (cachedCreds && _credentialsHandle == null);
string? targetName,
ReadOnlySpan<byte> inputBuffer,
ref byte[]? outputBuffer,
- SslAuthenticationOptions sslAuthenticationOptions,
- SelectClientCertificate? clientCertificateSelectionCallback)
+ SslAuthenticationOptions sslAuthenticationOptions)
{
return HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions);
}
connectionInfo.UpdateSslConnectionInfo(securityContext.SslContext);
}
+ public static bool TryUpdateClintCertificate(
+ SafeFreeCredentials? _1,
+ SafeDeleteSslContext? _2,
+ SslAuthenticationOptions _3)
+ {
+ return false;
+ }
+
private static SecurityStatusPal HandshakeInternal(
SafeFreeCredentials credential,
ref SafeDeleteSslContext? context,
ref byte[]? outputBuffer,
SslAuthenticationOptions sslAuthenticationOptions)
{
- return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions, null);
+ return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions);
}
public static SecurityStatusPal InitializeSecurityContext(
string? _ /*targetName*/,
ReadOnlySpan<byte> inputBuffer,
ref byte[]? outputBuffer,
- SslAuthenticationOptions sslAuthenticationOptions,
- SelectClientCertificate clientCertificateSelectionCallback)
+ SslAuthenticationOptions sslAuthenticationOptions)
{
- return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions, clientCertificateSelectionCallback);
+ return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions);
}
public static SecurityStatusPal Renegotiate(
connectionInfo.UpdateSslConnectionInfo(securityContext);
}
+ public static bool TryUpdateClintCertificate(
+ SafeFreeCredentials? _,
+ SafeDeleteSslContext? context,
+ SslAuthenticationOptions sslAuthenticationOptions)
+ {
+ SafeDeleteSslContext? sslContext = ((SafeDeleteSslContext?)context);
+
+ if (sslAuthenticationOptions.CertificateContext != null)
+ {
+ SafeDeleteSslContext.SetCertificate(sslContext!.SslContext, sslAuthenticationOptions.CertificateContext);
+ }
+
+ return true;
+ }
+
private static SecurityStatusPal HandshakeInternal(
ref SafeDeleteSslContext? context,
ReadOnlySpan<byte> inputBuffer,
ref byte[]? outputBuffer,
- SslAuthenticationOptions sslAuthenticationOptions,
- SelectClientCertificate? clientCertificateSelectionCallback)
+ SslAuthenticationOptions sslAuthenticationOptions)
{
try
{
if (status.ErrorCode == SecurityStatusPalErrorCode.CredentialsNeeded)
{
// this should happen only for clients
- Debug.Assert(clientCertificateSelectionCallback != null);
-
- // The callback also saves the selected cert in SslStream.LocalCertificate
- X509Certificate2? clientCertificate = clientCertificateSelectionCallback(out bool _);
-
- if (clientCertificate != null)
- {
- // build the cert context only if it was not provided by the user
- sslAuthenticationOptions.CertificateContext ??= SslStreamCertificateContext.Create(clientCertificate);
- }
-
- if (sslAuthenticationOptions.CertificateContext != null)
- {
- SafeDeleteSslContext.SetCertificate(sslContext.SslContext, sslAuthenticationOptions.CertificateContext);
- }
-
- // We either got certificate or we can proceed without it. It is up to the server to decide if either is OK.
- status = PerformHandshake(sslHandle);
+ Debug.Assert(sslAuthenticationOptions.IsClient);
+ return status;
}
outputBuffer = sslContext.ReadPendingWrites();
ref byte[]? outputBuffer,
SslAuthenticationOptions sslAuthenticationOptions)
{
- return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions, null);
+ return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions);
}
public static SecurityStatusPal InitializeSecurityContext(
string? _ /*targetName*/,
ReadOnlySpan<byte> inputBuffer,
ref byte[]? outputBuffer,
- SslAuthenticationOptions sslAuthenticationOptions,
- SelectClientCertificate? clientCertificateSelectionCallback)
+ SslAuthenticationOptions sslAuthenticationOptions)
{
- return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions, clientCertificateSelectionCallback);
+ return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions);
}
public static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions _1, bool _2)
{
return status;
}
- return HandshakeInternal(ref context!, null, ref outputBuffer, sslAuthenticationOptions, null);
+ return HandshakeInternal(ref context!, null, ref outputBuffer, sslAuthenticationOptions);
}
public static void QueryContextStreamSizes(SafeDeleteContext? _ /*securityContext*/, out StreamSizes streamSizes)
connectionInfo.UpdateSslConnectionInfo((SafeSslHandle)securityContext);
}
- private static SecurityStatusPal HandshakeInternal(ref SafeDeleteSslContext? context,
- ReadOnlySpan<byte> inputBuffer, ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions, SelectClientCertificate? clientCertificateSelectionCallback)
+ public static bool TryUpdateClintCertificate(
+ SafeFreeCredentials? _,
+ SafeDeleteSslContext? context,
+ SslAuthenticationOptions sslAuthenticationOptions)
+ {
+ Interop.OpenSsl.UpdateClientCertificate((SafeSslHandle)context!, sslAuthenticationOptions);
+
+ return true;
+ }
+
+ private static SecurityStatusPal HandshakeInternal(ref SafeDeleteSslContext? context,
+ ReadOnlySpan<byte> inputBuffer, ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
{
byte[]? output = null;
int outputSize = 0;
if (errorCode == SecurityStatusPalErrorCode.CredentialsNeeded)
{
// this should happen only for clients
- Debug.Assert(clientCertificateSelectionCallback != null);
-
- // The callback also saves the selected cert in SslStream.LocalCertificate
- X509Certificate2? clientCertificate = clientCertificateSelectionCallback(out bool _);
-
- if (clientCertificate != null)
- {
- // build the cert context only if it was not provided by the user
- sslAuthenticationOptions.CertificateContext ??= SslStreamCertificateContext.Create(clientCertificate);
- }
-
- Interop.OpenSsl.UpdateClientCertificate((SafeSslHandle)context, sslAuthenticationOptions);
- errorCode = Interop.OpenSsl.DoSslHandshake((SafeSslHandle)context, null, out output, out outputSize);
+ Debug.Assert(sslAuthenticationOptions.IsClient);
+ return new SecurityStatusPal(errorCode);
}
// sometimes during renegotiation processing message does not yield new output.
return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode);
}
+ public static bool TryUpdateClintCertificate(
+ SafeFreeCredentials? _1,
+ SafeDeleteSslContext? _2,
+ SslAuthenticationOptions _3)
+ {
+ // We will need to allocate new credential handle
+ return false;
+ }
+
public static unsafe SecurityStatusPal InitializeSecurityContext(
ref SafeFreeCredentials? credentialsHandle,
ref SafeDeleteSslContext? context,
string? targetName,
ReadOnlySpan<byte> inputBuffer,
ref byte[]? outputBuffer,
- SslAuthenticationOptions sslAuthenticationOptions,
- SelectClientCertificate? _ /*clientCertificateSelectionCallback*/)
+ SslAuthenticationOptions sslAuthenticationOptions)
{
Interop.SspiCli.ContextFlags unusedAttributes = default;