[release/6.0] add TLS 1.3 support to WinHttp (#58718)
authorgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Tue, 7 Sep 2021 17:46:01 +0000 (11:46 -0600)
committerGitHub <noreply@github.com>
Tue, 7 Sep 2021 17:46:01 +0000 (11:46 -0600)
* add TLS 1.3 support to WinHttp

* remove extra line

* fix comment

* add check if WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 is supported

Co-authored-by: wfurt <tweinfurt@yahoo.com>
src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs
src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs
src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpHandler.cs

index 1b86a14..3344e34 100644 (file)
@@ -109,6 +109,7 @@ internal static partial class Interop
         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;
index da250bf..5a643c0 100644 (file)
@@ -121,7 +121,7 @@ namespace System.Net.Http.Functional.Tests
 #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
                 }
index 8173084..9fa4c01 100644 (file)
@@ -44,6 +44,7 @@ namespace System.Net.Http
 
         private static readonly StringWithQualityHeaderValue s_gzipHeaderValue = new StringWithQualityHeaderValue("gzip");
         private static readonly StringWithQualityHeaderValue s_deflateHeaderValue = new StringWithQualityHeaderValue("deflate");
+        private static readonly Lazy<bool> s_supportsTls13 = new Lazy<bool>(CheckTls13Support());
 
         [ThreadStatic]
         private static StringBuilder? t_requestHeadersBuilder;
@@ -1153,6 +1154,7 @@ namespace System.Net.Http
 
         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;
@@ -1184,10 +1186,13 @@ namespace System.Net.Http
                 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(
@@ -1198,6 +1203,30 @@ namespace System.Net.Http
             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(