diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs index 1b86a149072899..3344e34b0265e3 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs @@ -109,6 +109,7 @@ internal static partial class WinHttp public const uint WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 = 0x00000080; public const uint WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 = 0x00000200; public const uint WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 = 0x00000800; + public const uint WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 = 0x00002000; public const uint SECURITY_FLAG_IGNORE_UNKNOWN_CA = 0x00000100; public const uint SECURITY_FLAG_IGNORE_CERT_DATE_INVALID = 0x00002000; diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs index da250bf305f32c..5a643c09aec385 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs @@ -121,7 +121,7 @@ public async Task GetAsync_AllowedSSLVersion_Succeeds(SslProtocols acceptedProto #if !NETFRAMEWORK handler.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13; #else - handler.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; + handler.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | (SslProtocols)12288; #endif #pragma warning restore 0618 } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs index 93781b803cc754..81f28b66142d76 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs @@ -44,6 +44,7 @@ public class WinHttpHandler : HttpMessageHandler private static readonly StringWithQualityHeaderValue s_gzipHeaderValue = new StringWithQualityHeaderValue("gzip"); private static readonly StringWithQualityHeaderValue s_deflateHeaderValue = new StringWithQualityHeaderValue("deflate"); + private static readonly Lazy s_supportsTls13 = new Lazy(CheckTls13Support()); [ThreadStatic] private static StringBuilder? t_requestHeadersBuilder; @@ -1156,6 +1157,7 @@ private void SetSessionHandleConnectionOptions(SafeWinHttpHandle sessionHandle) private void SetSessionHandleTlsOptions(SafeWinHttpHandle sessionHandle) { + const SslProtocols Tls13 = (SslProtocols)12288; // enum is missing in .NET Standard uint optionData = 0; SslProtocols sslProtocols = (_sslProtocols == SslProtocols.None) ? SecurityProtocol.DefaultSecurityProtocols : _sslProtocols; @@ -1187,10 +1189,13 @@ private void SetSessionHandleTlsOptions(SafeWinHttpHandle sessionHandle) optionData |= Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2; } - // As of Win10RS5 there's no public constant for WinHTTP + TLS 1.3 - // This library builds against netstandard, which doesn't define the Tls13 enum field. + // Set this only if supported by WinHttp version. + if (s_supportsTls13.Value && (sslProtocols & Tls13) != 0) + { + optionData |= Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3; + } - // If only unknown values (e.g. TLS 1.3) were asked for, report ERROR_INVALID_PARAMETER. + // If only unknown values were asked for, report ERROR_INVALID_PARAMETER. if (optionData == 0) { throw WinHttpException.CreateExceptionUsingError( @@ -1201,6 +1206,30 @@ private void SetSessionHandleTlsOptions(SafeWinHttpHandle sessionHandle) SetWinHttpOption(sessionHandle, Interop.WinHttp.WINHTTP_OPTION_SECURE_PROTOCOLS, ref optionData); } + private static bool CheckTls13Support() + { + try + { + using (var handler = new WinHttpHandler()) + using (SafeWinHttpHandle sessionHandle = Interop.WinHttp.WinHttpOpen( + IntPtr.Zero, + Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY, + Interop.WinHttp.WINHTTP_NO_PROXY_NAME, + Interop.WinHttp.WINHTTP_NO_PROXY_BYPASS, + (int)Interop.WinHttp.WINHTTP_FLAG_ASYNC)) + { + uint optionData = Interop.WinHttp.WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3; + + handler.SetWinHttpOption(sessionHandle, Interop.WinHttp.WINHTTP_OPTION_SECURE_PROTOCOLS, ref optionData); + return true; + } + } + catch + { + return false; + } + } + private void SetSessionHandleTimeoutOptions(SafeWinHttpHandle sessionHandle) { if (!Interop.WinHttp.WinHttpSetTimeouts(