TLS 1.2 在没有显式 ServicePointManager.SecurityProtocol 调用的情况下未在 .NET 4.7 中协商

TLS 1.2 not negotiated in .NET 4.7 without explicit ServicePointManager.SecurityProtocol call

我需要升级 .NET 应用程序以支持在仅支持 TLS 1.2 的网站上调用 API。据我了解,如果应用程序的目标是 4.6 或更高版本,那么它将默认使用 TLS 1.2。

为了测试,我创建了一个针对 4.7 的 Windows Forms 应用程序。不幸的是,当我没有明确设置 ServicePointManager.SecurityProtocol 时它会出错。这是代码:

HttpClient _client = new HttpClient();

var msg = new StringBuilder();

// If I uncomment the next line it works, but fails even with 4.7
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://sandbox.authorize.net");

httpWebRequest.KeepAlive = false;

try
{
    var httpWebResponse = (HttpWebResponse) httpWebRequest.GetResponse();

    msg.AppendLine("The HTTP request Headers for the first request are: ");

    foreach (var header in httpWebRequest.Headers)
    {
        msg.AppendLine(header.ToString());
    }

    ResponseTextBox.Text = msg.ToString();

}
catch (Exception exception)
{
   ResponseTextBox.Text = exception.Message;

   if (exception.InnerException != null)
   {
       ResponseTextBox.Text += Environment.NewLine + @"  ->" + exception.InnerException.Message;

       if (exception.InnerException.InnerException != null)
       {
            ResponseTextBox.Text += Environment.NewLine + @"     ->" + exception.InnerException.InnerException.Message;
       }
   }
}

如果您取消注释掉以下行:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

有效。这不是一个好的解决方案,因为它硬编码了要使用的 TLS 版本,因此将来不会使用 TLS 1.3。

如果没有这条线,我还需要做什么才能让它工作。我在一台安装了 4.7 的 Window 10 机器上进行测试。

更新

我尝试使用 HttpClient 进行测试并得到相同的结果,我必须显式设置 SecurityProtocol。

代码:

var msg = new StringBuilder();

// Need to uncomment code below for TLS 1.2 to be used
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

try
{
   var response = await _client.GetAsync(@"https://sandbox.authorize.net");

   msg.AppendLine("response.IsSuccessStatusCode : " + response.IsSuccessStatusCode);

   msg.AppendLine(await response.Content.ReadAsStringAsync());

   textBox.Text = msg.ToString();
  }

  catch (Exception exception)
  {
      textBox.Text = exception.Message;

      if (exception.InnerException != null)
      {
          textBox.Text += Environment.NewLine + @"  ->" + exception.InnerException.Message;
      }
   }

我找到了一种解决方案。它没有回答关于为什么在 .NET 4.7 的 Win10 上默认不使用 TLS 1.2 的问题,但它确实让我不必设置 ServicePointManager.SecurityProtocol。

适用于我的 4.5.2 和 4.7 测试应用程序的解决方案是将以下内容添加到 app.config:

<AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false"/>

这里全app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7"/>
    </startup>
    <runtime>
      <AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false"/>
    </runtime>
</configuration>

从面向 .NET Framework 4.7 的应用程序开始,ServicePointManager.SecurityProtocol 属性 的默认值为 SecurityProtocolType.SystemDefault.

此更改允许基于 SslStream 的 .NET Framework 网络 API(例如 FTP、HTTPS 和 SMTP)从操作系统继承默认安全协议而不是使用 .NET Framework 定义的硬编码值。

这就是您遇到新行为和需要新配置的原因:

<runtime>
   <AppContextSwitchOverrides value="Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols=false;Switch.System.Net.DontEnableSchUseStrongCrypto=false" /> 
</runtime>

here and here

更新(有用信息)

请记住,最佳安全做法建议更新您的 IIS 配置,并逐步禁用旧协议和密码密钥(例如 TLS 1.0、1.1)。有关非常有趣的信息,请参阅 Setup Microsoft Windows or IIS for SSL Perfect Forward Secrecy and TLS 1.2

如果你按照这个做法,你不需要设置上面的配置(MS 建议),因为你的 Win 服务器/IIS 已经配置好。

当然,这只有在您可以通过适当的授权访问服务器的情况下才有可能。

我在 Windows 7 和 .NET 4.7.1

其他两个答案中提到的使用 Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocolsSwitch.System.Net.DontEnableSchUseStrongCrypto 的建议在我的系统中不起作用项目和 OP 的代码也失败了。

查看 ServicePointManagerLocalAppContextSwitches 的源代码,我发现另一个 config setting 有效。

<runtime>
  <AppContextSwitchOverrides value="Switch.System.Net.DontEnableSystemDefaultTlsVersions=true" />
</runtime>

作为 Nick Y 回答的替代方案,我发现在 Windows 7 上使用 .NET 4.7+,我需要启用这些注册表设置以便 Microsoft Secure Channel (Schannel) 包正确发送TLS1.1 和 TLS1.2。

这允许 .NET 客户端继续将 System.Net.ServicePointManager.SecurityProtocol 设置为 SystemDefault 并在 Windows 7 计算机上获取 TLS 1.1 和 1.2。

使用 SystemDefault 选项允许 .NET 将协议的选择推迟到 OS。这意味着当 Microsoft 发布 OS 的修补程序以禁用不安全协议或在其本机 SCHANNEL 库中启用对新协议的支持时,.NET 框架应用程序 运行 将自动获得此新行为。

以下是注册表项:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client]
"DisabledByDefault"=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client]
"DisabledByDefault"=dword:00000000

我遇到了同样的问题(Windows 10 和 SSL3 / TLS only...not System Default)针对 4.7.2 的旧版应用程序。我的问题是,在多年来的升级过程中,我们从未将 targetFramework 添加到 system.web > httpRuntime 元素(注意:它确实存在于 system.web > compilation元素)。在采取更大的步骤之前,请确保您的 system.web 类似于以下内容:

<system.web>
    <compilation targetFramework="4.7.2"></compilation>
    <httpRuntime targetFramework="4.7.2" />
</system.web>

在上面的示例中,将 4.7.2 换成您当前使用的 >= 4.7 的框架版本。

Windows 7 默认禁用 TLS1.2 和 TLS1.1。在IE中启用它对其他应用程序没有影响。

您应该通过注册表启用它: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS1.2\Client] "DisabledByDefault"=dword:00000000

Very useful article with script to set as only TLS1.2

我在 Word VSTO add-in 项目中遇到了同样的问题,该项目最初使用 .NET Framework 4.5.2 开发,然后升级到 4.7。尝试了此处的所有解决方案,但其中 none 对我有用。终于找到这个article.

对于那些在 VSTO add-in 项目中遇到与我相同问题的人,请将这些代码添加到 ThisAddIn.cs class 中,它对我有用。

AppContext.SetSwitch("Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols", false);
AppContext.SetSwitch("Switch.System.Net.DontEnableSchUseStrongCrypto", false);