未验证 提交 1815306c 编写于 作者: T Tomas Weinfurt 提交者: GitHub

add AllowTlsResume to SslStream options (#86047)

* add AllowTlsResume to SslStream options

* debug

* test

* dwSessionLifespan

* feedback

* update

* feedback
上级 5ec1f155
......@@ -288,7 +288,7 @@ internal static SafeSslHandle AllocateSslHandle(SslAuthenticationOptions sslAuth
SafeSslContextHandle? newCtxHandle = null;
SslProtocols protocols = CalculateEffectiveProtocols(sslAuthenticationOptions);
bool hasAlpn = sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0;
bool cacheSslContext = !DisableTlsResume && sslAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.RequireEncryption && sslAuthenticationOptions.CipherSuitesPolicy == null;
bool cacheSslContext = sslAuthenticationOptions.AllowTlsResume && !DisableTlsResume && sslAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.RequireEncryption && sslAuthenticationOptions.CipherSuitesPolicy == null;
if (cacheSslContext)
{
......
......@@ -17,11 +17,8 @@ internal static partial class SChannel
// to be passed into ApplyControlToken
// through a PkgParams buffer.
public const int SCHANNEL_RENEGOTIATE = 0; // renegotiate a connection
public const int SCHANNEL_SHUTDOWN = 1; // gracefully close down a connection
public const int SCHANNEL_ALERT = 2; // build an error message
public const int SCHANNEL_SESSION = 3; // session control
// Alert token structure.
[StructLayout(LayoutKind.Sequential)]
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.InteropServices;
internal static partial class Interop
{
internal static partial class SChannel
{
// schannel.h;
public const int SCHANNEL_SESSION = 3; // session control
// Session structure.
[StructLayout(LayoutKind.Sequential)]
public struct SCHANNEL_SESSION_TOKEN
{
public uint dwTokenType; // SCHANNEL_SESSION
public uint dwFlags;
}
public const int SSL_SESSION_ENABLE_RECONNECTS = 1;
public const int SSL_SESSION_DISABLE_RECONNECTS = 2;
}
}
......@@ -24,6 +24,6 @@ internal interface ISSPIInterface
int QueryContextAttributes(SafeDeleteContext phContext, Interop.SspiCli.ContextAttribute attribute, Span<byte> buffer, Type? handleType, out SafeHandle? refHandle);
int QuerySecurityContextToken(SafeDeleteContext phContext, out SecurityContextTokenHandle phToken);
int CompleteAuthToken(ref SafeDeleteSslContext? refContext, in InputSecurityBuffer inputBuffer);
int ApplyControlToken(ref SafeDeleteContext? refContext, in SecurityBuffer inputBuffer);
int ApplyControlToken(ref SafeDeleteSslContext? refContext, in SecurityBuffer inputBuffer);
}
}
......@@ -201,6 +201,7 @@ public enum Flags
SCH_CRED_MANUAL_CRED_VALIDATION = 0x08,
SCH_CRED_NO_DEFAULT_CREDS = 0x10,
SCH_CRED_AUTO_CRED_VALIDATION = 0x20,
SCH_CRED_DISABLE_RECONNECTS = 0x80,
SCH_CRED_REVOCATION_CHECK_END_CERT = 0x100,
SCH_CRED_IGNORE_NO_REVOCATION_CHECK = 0x800,
SCH_CRED_IGNORE_REVOCATION_OFFLINE = 0x1000,
......@@ -239,7 +240,7 @@ public enum Flags
SCH_CRED_NO_DEFAULT_CREDS = 0x10,
SCH_CRED_AUTO_CRED_VALIDATION = 0x20,
SCH_CRED_USE_DEFAULT_CREDS = 0x40,
SCH_DISABLE_RECONNECTS = 0x80,
SCH_CRED_DISABLE_RECONNECTS = 0x80,
SCH_CRED_REVOCATION_CHECK_END_CERT = 0x100,
SCH_CRED_REVOCATION_CHECK_CHAIN = 0x200,
SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT = 0x400,
......
......@@ -150,7 +150,7 @@ private static int GetSecurityContextToken(SafeDeleteContext phContext, out Secu
}
}
public int ApplyControlToken(ref SafeDeleteContext? refContext, in SecurityBuffer inputBuffers)
public int ApplyControlToken(ref SafeDeleteSslContext? refContext, in SecurityBuffer inputBuffers)
{
throw new NotSupportedException();
}
......
......@@ -136,7 +136,7 @@ public int CompleteAuthToken(ref SafeDeleteSslContext? refContext, in InputSecur
throw new NotSupportedException();
}
public int ApplyControlToken(ref SafeDeleteContext? refContext, in SecurityBuffer inputBuffer)
public int ApplyControlToken(ref SafeDeleteSslContext? refContext, in SecurityBuffer inputBuffer)
{
return SafeDeleteContext.ApplyControlToken(ref refContext, in inputBuffer);
}
......
......@@ -172,7 +172,7 @@ internal static int CompleteAuthToken(ISSPIInterface secModule, ref SafeDeleteSs
return errorCode;
}
internal static int ApplyControlToken(ISSPIInterface secModule, ref SafeDeleteContext? context, in SecurityBuffer inputBuffer)
internal static int ApplyControlToken(ISSPIInterface secModule, ref SafeDeleteSslContext? context, in SecurityBuffer inputBuffer)
{
int errorCode = secModule.ApplyControlToken(ref context, in inputBuffer);
......
......@@ -1009,7 +1009,7 @@ internal abstract partial class SafeDeleteContext : SafeHandle
}
internal static unsafe int ApplyControlToken(
ref SafeDeleteContext? refContext,
ref SafeDeleteSslContext? refContext,
in SecurityBuffer inSecBuffer)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, $"refContext = {refContext}, inSecBuffer = {inSecBuffer}");
......
......@@ -199,6 +199,7 @@ public partial class SslClientAuthenticationOptions
{
public SslClientAuthenticationOptions() { }
public bool AllowRenegotiation { get { throw null; } set { } }
public bool AllowTlsResume { get { throw null; } set { } }
public System.Collections.Generic.List<System.Net.Security.SslApplicationProtocol>? ApplicationProtocols { get { throw null; } set { } }
public System.Security.Cryptography.X509Certificates.X509ChainPolicy? CertificateChainPolicy { get { throw null; } set { } }
public System.Security.Cryptography.X509Certificates.X509RevocationMode CertificateRevocationCheckMode { get { throw null; } set { } }
......@@ -223,6 +224,7 @@ public partial class SslServerAuthenticationOptions
{
public SslServerAuthenticationOptions() { }
public bool AllowRenegotiation { get { throw null; } set { } }
public bool AllowTlsResume { get { throw null; } set { } }
public System.Collections.Generic.List<System.Net.Security.SslApplicationProtocol>? ApplicationProtocols { get { throw null; } set { } }
public System.Security.Cryptography.X509Certificates.X509ChainPolicy? CertificateChainPolicy { get { throw null; } set { } }
public System.Security.Cryptography.X509Certificates.X509RevocationMode CertificateRevocationCheckMode { get { throw null; } set { } }
......
......@@ -214,6 +214,8 @@
Link="Common\Interop\Windows\Kernel32\Interop.CloseHandle.cs" />
<Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.Alerts.cs"
Link="Common\Interop\Windows\SChannel\Interop.Alerts.cs" />
<Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.Session.cs"
Link="Common\Interop\Windows\SChannel\Interop.Session.cs" />
<Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.SchProtocols.cs"
Link="Common\Interop\Windows\SChannel\Interop.SchProtocols.cs" />
<Compile Include="$(CommonPath)Interop\Windows\SChannel\SecPkgContext_ConnectionInfo.cs"
......
......@@ -41,6 +41,7 @@ internal void UpdateOptions(SslClientAuthenticationOptions sslClientAuthenticati
// Common options.
AllowRenegotiation = sslClientAuthenticationOptions.AllowRenegotiation;
AllowTlsResume = sslClientAuthenticationOptions.AllowTlsResume;
ApplicationProtocols = sslClientAuthenticationOptions.ApplicationProtocols;
CheckCertName = !(sslClientAuthenticationOptions.CertificateChainPolicy?.VerificationFlags.HasFlag(X509VerificationFlags.IgnoreInvalidName) == true);
EnabledSslProtocols = FilterOutIncompatibleSslProtocols(sslClientAuthenticationOptions.EnabledSslProtocols);
......@@ -105,6 +106,7 @@ internal void UpdateOptions(SslServerAuthenticationOptions sslServerAuthenticati
IsServer = true;
AllowRenegotiation = sslServerAuthenticationOptions.AllowRenegotiation;
AllowTlsResume = sslServerAuthenticationOptions.AllowTlsResume;
ApplicationProtocols = sslServerAuthenticationOptions.ApplicationProtocols;
EnabledSslProtocols = FilterOutIncompatibleSslProtocols(sslServerAuthenticationOptions.EnabledSslProtocols);
EncryptionPolicy = sslServerAuthenticationOptions.EncryptionPolicy;
......@@ -183,6 +185,7 @@ private static SslProtocols FilterOutIncompatibleSslProtocols(SslProtocols proto
internal object? UserState { get; set; }
internal ServerOptionsSelectionCallback? ServerOptionDelegate { get; set; }
internal X509ChainPolicy? CertificateChainPolicy { get; set; }
internal bool AllowTlsResume { get; set; }
#if TARGET_ANDROID
internal SslStream.JavaProxy? SslStreamProxy { get; set; }
......
......@@ -14,6 +14,7 @@ public class SslClientAuthenticationOptions
private X509RevocationMode _checkCertificateRevocation = X509RevocationMode.NoCheck;
private SslProtocols _enabledSslProtocols = SecurityProtocol.SystemDefaultSecurityProtocols;
private bool _allowRenegotiation = true;
private bool _allowTlsResume = true;
public bool AllowRenegotiation
{
......@@ -21,6 +22,15 @@ public bool AllowRenegotiation
set => _allowRenegotiation = value;
}
/// <summary>
/// Gets or sets a value that indicates whether the SslStream should allow TLS resumption.
/// </summary>
public bool AllowTlsResume
{
get => _allowTlsResume;
set => _allowTlsResume = value;
}
public LocalCertificateSelectionCallback? LocalCertificateSelectionCallback { get; set; }
public RemoteCertificateValidationCallback? RemoteCertificateValidationCallback { get; set; }
......
......@@ -28,7 +28,9 @@ public void UpdateSslConnectionInfo(SafeSslHandle sslContext)
{
ApplicationProtocol = alpn.ToArray();
}
#if DEBUG
TlsResumed = Interop.Ssl.SslSessionReused(sslContext);
#endif
MapCipherSuite(SslGetCurrentCipherSuite(sslContext));
}
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using static Interop.SspiCli;
namespace System.Net.Security
{
......@@ -66,6 +67,16 @@ public void UpdateSslConnectionInfo(SafeDeleteContext securityContext)
TlsCipherSuite = cipherSuite;
ApplicationProtocol = GetNegotiatedApplicationProtocol(securityContext);
#if DEBUG
SecPkgContext_SessionInfo info = default;
TlsResumed = SSPIWrapper.QueryBlittableContextAttributes(
GlobalSSPI.SSPISecureChannel,
securityContext,
Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SESSION_INFO,
ref info) &&
((SecPkgContext_SessionInfo.Flags)info.dwFlags).HasFlag(SecPkgContext_SessionInfo.Flags.SSL_SESSION_RECONNECT);
#endif
}
}
}
......@@ -21,5 +21,8 @@ internal partial struct SslConnectionInfo
public int KeyExchKeySize { get; private set; }
public byte[]? ApplicationProtocol { get; internal set; }
#if DEBUG
public bool TlsResumed { get; private set; }
#endif
}
}
......@@ -13,6 +13,7 @@ public class SslServerAuthenticationOptions
private SslProtocols _enabledSslProtocols = SecurityProtocol.SystemDefaultSecurityProtocols;
private EncryptionPolicy _encryptionPolicy = EncryptionPolicy.RequireEncryption;
private bool _allowRenegotiation;
private bool _allowTlsResume = true;
public bool AllowRenegotiation
{
......@@ -20,6 +21,15 @@ public bool AllowRenegotiation
set => _allowRenegotiation = value;
}
/// <summary>
/// Gets or sets a value that indicates whether the SslStream should allow TLS resumption.
/// </summary>
public bool AllowTlsResume
{
get => _allowTlsResume;
set => _allowTlsResume = value;
}
public bool ClientCertificateRequired { get; set; }
public List<SslApplicationProtocol>? ApplicationProtocols { get; set; }
......
......@@ -28,6 +28,7 @@ internal static class SslSessionsCache
private readonly bool _isServerMode;
private readonly bool _sendTrustList;
private readonly bool _checkRevocation;
private readonly bool _allowTlsResume;
//
// SECURITY: X509Certificate.GetCertHash() is virtual hence before going here,
......@@ -40,7 +41,8 @@ internal static class SslSessionsCache
bool isServerMode,
EncryptionPolicy encryptionPolicy,
bool sendTrustList,
bool checkRevocation)
bool checkRevocation,
bool allowTlsResume)
{
_thumbPrint = thumbPrint ?? Array.Empty<byte>();
_allowedProtocols = allowedProtocols;
......@@ -48,38 +50,24 @@ internal static class SslSessionsCache
_isServerMode = isServerMode;
_checkRevocation = checkRevocation;
_sendTrustList = sendTrustList;
_allowTlsResume = allowTlsResume;
}
public override int GetHashCode()
{
int hashCode = 0;
if (_thumbPrint.Length > 0)
if (_thumbPrint.Length > 3)
{
hashCode ^= _thumbPrint[0];
if (1 < _thumbPrint.Length)
{
hashCode ^= (_thumbPrint[1] << 8);
}
if (2 < _thumbPrint.Length)
{
hashCode ^= (_thumbPrint[2] << 16);
}
if (3 < _thumbPrint.Length)
{
hashCode ^= (_thumbPrint[3] << 24);
}
hashCode ^= _thumbPrint[0] | (_thumbPrint[1] << 8) | (_thumbPrint[2] << 16) | (_thumbPrint[3] << 24);
}
hashCode ^= _allowedProtocols;
hashCode ^= (int)_encryptionPolicy;
hashCode ^= _isServerMode ? 0x10000 : 0x20000;
hashCode ^= _sendTrustList ? 0x40000 : 0x80000;
hashCode ^= _checkRevocation ? 0x100000 : 0x200000;
return hashCode;
return HashCode.Combine(_allowedProtocols,
(int)_encryptionPolicy,
_isServerMode,
_sendTrustList,
_checkRevocation,
_allowedProtocols,
hashCode);
}
public override bool Equals([NotNullWhen(true)] object? obj) =>
......@@ -97,6 +85,7 @@ public bool Equals(SslCredKey other)
_isServerMode == other._isServerMode &&
_sendTrustList == other._sendTrustList &&
_checkRevocation == other._checkRevocation &&
_allowTlsResume == other._allowTlsResume &&
thumbPrint.AsSpan().SequenceEqual(otherThumbPrint);
}
}
......@@ -113,7 +102,8 @@ public bool Equals(SslCredKey other)
bool isServer,
EncryptionPolicy encryptionPolicy,
bool checkRevocation,
bool sendTrustList = false)
bool allowTlsResume,
bool sendTrustList)
{
if (s_cachedCreds.IsEmpty)
{
......@@ -121,7 +111,7 @@ public bool Equals(SslCredKey other)
return null;
}
var key = new SslCredKey(thumbPrint, (int)sslProtocols, isServer, encryptionPolicy, sendTrustList, checkRevocation);
var key = new SslCredKey(thumbPrint, (int)sslProtocols, isServer, encryptionPolicy, sendTrustList, checkRevocation, allowTlsResume);
//SafeCredentialReference? cached;
SafeFreeCredentials? credentials = GetCachedCredential(key);
......@@ -153,7 +143,8 @@ public bool Equals(SslCredKey other)
bool isServer,
EncryptionPolicy encryptionPolicy,
bool checkRevocation,
bool sendTrustList = false)
bool allowTlsResume,
bool sendTrustList)
{
Debug.Assert(creds != null, "creds == null");
......@@ -163,7 +154,7 @@ public bool Equals(SslCredKey other)
return;
}
SslCredKey key = new SslCredKey(thumbPrint, (int)sslProtocols, isServer, encryptionPolicy, sendTrustList, checkRevocation);
SslCredKey key = new SslCredKey(thumbPrint, (int)sslProtocols, isServer, encryptionPolicy, sendTrustList, checkRevocation, allowTlsResume);
SafeFreeCredentials? credentials = GetCachedCredential(key);
......
......@@ -544,7 +544,9 @@ private bool AcquireClientCredentials(ref byte[]? thumbPrint, bool newCredential
_sslAuthenticationOptions.EnabledSslProtocols,
_sslAuthenticationOptions.IsServer,
_sslAuthenticationOptions.EncryptionPolicy,
_sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck);
_sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck,
_sslAuthenticationOptions.AllowTlsResume,
sendTrustList: false);
// 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
......@@ -688,6 +690,8 @@ private bool AcquireServerCredentials(ref byte[]? thumbPrint)
_sslAuthenticationOptions.EnabledSslProtocols,
_sslAuthenticationOptions.IsServer,
_sslAuthenticationOptions.EncryptionPolicy,
_sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck,
_sslAuthenticationOptions.AllowTlsResume,
sendTrustedList);
if (cachedCredentialHandle != null)
{
......@@ -895,6 +899,7 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan<byte> inputBuffer, ref byte
_sslAuthenticationOptions.IsServer,
_sslAuthenticationOptions.EncryptionPolicy,
_sslAuthenticationOptions.CertificateRevocationCheckMode != X509RevocationMode.NoCheck,
_sslAuthenticationOptions.AllowTlsResume,
sendTrustList);
}
}
......@@ -933,6 +938,14 @@ internal void ProcessHandshakeSuccess()
Debug.Assert(_maxDataSize > 0, "_maxDataSize > 0");
SslStreamPal.QueryContextConnectionInfo(_securityContext!, ref _connectionInfo);
#if DEBUG
if (NetEventSource.Log.IsEnabled())
{
// This keeps the property alive only for tests via reflection
// Otherwise it could be optimized out as it is not used by production code.
NetEventSource.Info(this, $"TLS resumed {_connectionInfo.TlsResumed}");
}
#endif
}
/*++
......
......@@ -45,6 +45,14 @@ public static Exception GetException(SecurityStatusPal status)
internal const bool StartMutualAuthAsAnonymous = true;
internal const bool CanEncryptEmptyMessage = true;
private static readonly byte[] s_sessionTokenBuffer = MemoryMarshal.AsBytes(new ReadOnlySpan<Interop.SChannel.SCHANNEL_SESSION_TOKEN>(
new Interop.SChannel.SCHANNEL_SESSION_TOKEN()
{
dwTokenType = Interop.SChannel.SCHANNEL_SESSION,
dwFlags = Interop.SChannel.SSL_SESSION_DISABLE_RECONNECTS,
}
)).ToArray();
public static void VerifyPackageInfo()
{
SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPISecureChannel, SecurityPackage, true);
......@@ -139,6 +147,7 @@ private static unsafe void SetAlpn(ref InputSecurityBuffers inputBuffers, List<S
ref byte[]? outputBuffer,
SslAuthenticationOptions sslAuthenticationOptions)
{
bool newContext = context == null;
Interop.SspiCli.ContextFlags unusedAttributes = default;
scoped InputSecurityBuffers inputBuffers = default;
......@@ -163,6 +172,20 @@ private static unsafe void SetAlpn(ref InputSecurityBuffers inputBuffers, List<S
ref resultBuffer,
ref unusedAttributes);
if (!sslAuthenticationOptions.AllowTlsResume && newContext && context != null)
{
var securityBuffer = new SecurityBuffer(s_sessionTokenBuffer, SecurityBufferType.SECBUFFER_TOKEN);
SecurityStatusPal result = SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(SSPIWrapper.ApplyControlToken(
GlobalSSPI.SSPISecureChannel,
ref context,
in securityBuffer));
if (result.ErrorCode != SecurityStatusPalErrorCode.OK)
{
return result;
}
}
outputBuffer = resultBuffer.token;
return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode);
}
......@@ -278,6 +301,11 @@ public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchannelCred(Ss
flags =
Interop.SspiCli.SCHANNEL_CRED.Flags.SCH_SEND_AUX_RECORD |
Interop.SspiCli.SCHANNEL_CRED.Flags.SCH_CRED_NO_SYSTEM_MAPPER;
if (!authOptions.AllowTlsResume)
{
// Works only on server
flags |= Interop.SspiCli.SCHANNEL_CRED.Flags.SCH_CRED_DISABLE_RECONNECTS;
}
}
EncryptionPolicy policy = authOptions.EncryptionPolicy;
......@@ -298,6 +326,11 @@ public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchannelCred(Ss
protocolFlags,
policy);
if (!isServer && !authOptions.AllowTlsResume)
{
secureCredential.dwSessionLifespan = -1;
}
if (certificate != null)
{
secureCredential.cCreds = 1;
......@@ -325,6 +358,11 @@ public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchCredentials(
{
flags |= Interop.SspiCli.SCH_CREDENTIALS.Flags.SCH_CRED_NO_SYSTEM_MAPPER;
}
if (!authOptions.AllowTlsResume)
{
// Works only on server
flags |= Interop.SspiCli.SCH_CREDENTIALS.Flags.SCH_CRED_DISABLE_RECONNECTS;
}
}
else
{
......@@ -369,6 +407,11 @@ public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchCredentials(
Interop.SspiCli.SCH_CREDENTIALS credential = default;
credential.dwVersion = Interop.SspiCli.SCH_CREDENTIALS.CurrentVersion;
credential.dwFlags = flags;
if (!isServer && !authOptions.AllowTlsResume)
{
credential.dwSessionLifespan = -1;
}
if (certificate != null)
{
credential.cCreds = 1;
......@@ -500,7 +543,7 @@ public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteSslContext? secu
}
}
public static SecurityStatusPal ApplyAlertToken(SafeDeleteContext? securityContext, TlsAlertType alertType, TlsAlertMessage alertMessage)
public static SecurityStatusPal ApplyAlertToken(SafeDeleteSslContext? securityContext, TlsAlertType alertType, TlsAlertMessage alertMessage)
{
var alertToken = new Interop.SChannel.SCHANNEL_ALERT_TOKEN
{
......@@ -521,7 +564,7 @@ public static SecurityStatusPal ApplyAlertToken(SafeDeleteContext? securityConte
private static readonly byte[] s_schannelShutdownBytes = BitConverter.GetBytes(Interop.SChannel.SCHANNEL_SHUTDOWN);
public static SecurityStatusPal ApplyShutdownToken(SafeDeleteContext? securityContext)
public static SecurityStatusPal ApplyShutdownToken(SafeDeleteSslContext? securityContext)
{
var securityBuffer = new SecurityBuffer(s_schannelShutdownBytes, SecurityBufferType.SECBUFFER_TOKEN);
......
......@@ -26,6 +26,7 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
{
// Values used to populate client options
bool clientAllowRenegotiation = false;
bool clientAllowTlsResume = false;
List<SslApplicationProtocol> clientAppProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http11 };
X509RevocationMode clientRevocation = X509RevocationMode.NoCheck;
X509CertificateCollection clientCertificates = new X509CertificateCollection() { clientCert };
......@@ -37,6 +38,7 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
// Values used to populate server options
bool serverAllowRenegotiation = true;
bool serverAllowTlsResume = false;
List<SslApplicationProtocol> serverAppProtocols = new List<SslApplicationProtocol> { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 };
X509RevocationMode serverRevocation = X509RevocationMode.NoCheck;
bool serverCertRequired = false;
......@@ -58,6 +60,7 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
var clientOptions = new SslClientAuthenticationOptions
{
AllowRenegotiation = clientAllowRenegotiation,
AllowTlsResume = clientAllowTlsResume,
ApplicationProtocols = clientAppProtocols,
CertificateRevocationCheckMode = clientRevocation,
ClientCertificates = clientCertificates,
......@@ -73,6 +76,7 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
var serverOptions = new SslServerAuthenticationOptions
{
AllowRenegotiation = serverAllowRenegotiation,
AllowTlsResume = serverAllowTlsResume,
ApplicationProtocols = serverAppProtocols,
CertificateRevocationCheckMode = serverRevocation,
ClientCertificateRequired = serverCertRequired,
......@@ -91,6 +95,7 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
// Validate that client options are unchanged
Assert.Equal(clientAllowRenegotiation, clientOptions.AllowRenegotiation);
Assert.Equal(clientAllowTlsResume, clientOptions.AllowTlsResume);
Assert.Same(clientAppProtocols, clientOptions.ApplicationProtocols);
Assert.Equal(1, clientOptions.ApplicationProtocols.Count);
Assert.Equal(clientRevocation, clientOptions.CertificateRevocationCheckMode);
......@@ -105,6 +110,7 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
// Validate that server options are unchanged
Assert.Equal(serverAllowRenegotiation, serverOptions.AllowRenegotiation);
Assert.Equal(serverAllowTlsResume, serverOptions.AllowTlsResume);
Assert.Same(serverAppProtocols, serverOptions.ApplicationProtocols);
Assert.Equal(2, serverOptions.ApplicationProtocols.Count);
Assert.Equal(clientRevocation, serverOptions.CertificateRevocationCheckMode);
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Reflection;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Xunit;
using Microsoft.DotNet.XUnitExtensions;
#if DEBUG
namespace System.Net.Security.Tests
{
using Configuration = System.Net.Test.Common.Configuration;
public class SslStreamTlsResumeTests
{
private static FieldInfo connectionInfo = typeof(SslStream).GetField(
"_connectionInfo",
BindingFlags.Instance | BindingFlags.NonPublic);
private bool CheckResumeFlag(SslStream ssl)
{
// This works only on Debug build where SslStream has extra property so we can validate.
object info = connectionInfo.GetValue(ssl);
return (bool)info.GetType().GetProperty("TlsResumed").GetValue(info);
}
[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
[PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)]
public async Task SslStream_ClientDisableTlsResume_Succeeds(bool testClient)
{
SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions
{
ServerCertificateContext = SslStreamCertificateContext.Create(Configuration.Certificates.GetServerCertificate(), null, false)
};
SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions
{
TargetHost = Guid.NewGuid().ToString("N"),
EnabledSslProtocols = SslProtocols.Tls12,
CertificateRevocationCheckMode = X509RevocationMode.NoCheck,
RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true,
};
(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions),
server.AuthenticateAsServerAsync(serverOptions));
Assert.True(client.IsAuthenticated);
Assert.True(client.IsEncrypted);
await client.ShutdownAsync();
await server.ShutdownAsync();
client.Dispose();
server.Dispose();
// create new TLS to the same server. This should resume TLS.
(client, server) = TestHelper.GetConnectedSslStreams();
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions),
server.AuthenticateAsServerAsync(serverOptions));
//Assert.True(CheckResumeFlag(client));
if (!CheckResumeFlag(client))
{
throw new SkipTestException("Unable to resume test session");
}
Assert.True(CheckResumeFlag(server));
await client.ShutdownAsync();
await server.ShutdownAsync();
client.Dispose();
server.Dispose();
// Disable TLS resumption and try it again.
if (testClient)
{
clientOptions.AllowTlsResume = false;
}
else
{
serverOptions.AllowTlsResume = false;
}
// We do multiple loops to also cover credential cache.
for (int i=0; i < 3; i++)
{
(client, server) = TestHelper.GetConnectedSslStreams();
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions),
server.AuthenticateAsServerAsync(serverOptions));
Assert.False(CheckResumeFlag(client), $"TLS session resumed in round ${i}");
Assert.False(CheckResumeFlag(server), $"TLS session resumed in round ${i}");
await client.ShutdownAsync();
await server.ShutdownAsync();
client.Dispose();
server.Dispose();
}
// TLS resume still should be possible
if (testClient)
{
clientOptions.AllowTlsResume = true;
}
else
{
serverOptions.AllowTlsResume = true;
}
// On Windows it may take extra round to refresh the session cache.
(client, server) = TestHelper.GetConnectedSslStreams();
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions),
server.AuthenticateAsServerAsync(serverOptions));
client.Dispose();
server.Dispose();
(client, server) = TestHelper.GetConnectedSslStreams();
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
client.AuthenticateAsClientAsync(clientOptions),
server.AuthenticateAsServerAsync(serverOptions));
Assert.True(CheckResumeFlag(client));
Assert.True(CheckResumeFlag(server));
client.Dispose();
server.Dispose();
}
}
}
#endif
......@@ -105,6 +105,7 @@
<Compile Include="SslAuthenticationOptionsTest.cs" />
<Compile Include="SslStreamAlertsTest.cs" />
<Compile Include="SslStreamAllowRenegotiationTests.cs" />
<Compile Include="SslStreamAllowTlsResumeTests.cs" />
<Compile Include="SslStreamAlpnTests.cs" />
<Compile Include="SslStreamCertificateTrustTests.cs" />
<Compile Include="SslStreamDisposeTest.cs" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册