【问题标题】:TLS 1.2 not negotiated in .NET 4.7 without explicit ServicePointManager.SecurityProtocol call没有显式 ServicePointManager.SecurityProtocol 调用的 .NET 4.7 中未协商 TLS 1.2
【发布时间】:2017-11-28 18:57:01
【问题描述】:

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

为了测试,我创建了一个面向 4.7 的 Windows 窗体应用程序。不幸的是,当我没有明确设置 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;
      }
   }

【问题讨论】:

  • 如果您愿意,您可以将 tls 版本组合在一起以支持它们。但是为了避免这种使用 HttpClient 的需要(如果你可以让你的代码异步而没有任何阻塞调用)
  • 我不想定义安全协议,因为当 TLS 1.3 等新协议可用时,它不会被使用,因为它不在列表中。我将用 HttpClient 重写我的测试,HttpClient 与 HttpWebResponse 使用的默认安全协议有区别吗?
  • 不要相信我的话,但我假设 HttpClient 将支持协商可用于 CLR 版本的最高协议。因此,您的下一个砖墙将需要 CLR 升级而不是代码升级。这可能是一个可怕的想法,但我想知道如果你只打开所有位会发生什么:SecurityProtocolType s = (SecurityProtocolType)2147483647;
  • 有趣。查看有关 v4.7 的此发行说明github.com/Microsoft/dotnet/blob/master/releases/net47/…
  • 这很有趣。我试图将其设置为“SystemDefault”,但失败了。假设它是默认值,那么 TLS 1.2 应该是 Win10 OS 上使用的值。

标签: c# .net winforms httpwebrequest tls1.2


【解决方案1】:

我找到了一种解决方案。它没有回答为什么在带有 .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>

【讨论】:

  • 这毫无意义。 Switch.System.Net.DontEnableSchUseStrongCrypto 的默认值已经是 false。 false:不使用不安全的协议 (SSL),true:允许不安全的协议。这根本不应该与 TLS1.2 相关。我不否认它有效(我没有测试过),只是说它没有意义。
  • 我在一个简单的控制台应用程序中进行了尝试,该应用程序向仅支持 TLS 1.2 的 Web 服务发出 HttpClient 请求,并在 Windows 7 SP1 上运行了控制台程序。不幸的是,它的效果为零。投反对票。
  • 我已经在运行 4.7.2 单元测试项目的 windows 10 机器上尝试了这个线程上的所有答案,唯一有效的是硬编码ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; -- 与微软握手
  • 是的,此解决方案对 Windows 7 SP1 无效。
【解决方案2】:

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

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

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

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

herehere

更新(有用信息)

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

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

当然,这只有在您有权访问具有适当授权的服务器时才有可能。

【讨论】:

    【解决方案3】:

    我使用的是 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>
    

    【讨论】:

    • 查看我的答案以获得仍然允许 .NET 保留其“SystemDefaultTls”设置的替代方案
    • 此配置设置仅适用于控制台应用程序。面向 4.7 或更高版本的 Web 应用程序将始终具有 System.Net.ServicePointManager.SecurityProtocol = SystemDefault,除非它明确地硬编码为其他协议(不推荐)
    【解决方案4】:

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

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

    使用SystemDefault 选项允许.NET 将协议选择推迟到操作系统。这意味着,当 Microsoft 向操作系统发布修补程序以禁用不安全协议或在其原生 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
    

    【讨论】:

    • 也为 2008 R2 工作过。
    • @osexpert 没有。如果设置“DontEnableSystemDefaultTlsVersion=true”,则会禁用操作系统的默认协议,并且仅在已安装的 .NET 版本中启用默认协议。就我而言,如果操作系统被修补以启用新协议,我希望它们启用。我不想仅仅为了启用新版本的 TLS 而更新应用程序的配置或 .NET 框架版本。
    • @osexpert 查看我的更新答案,了解为什么这可能比更改应用程序以禁用 SystemDefault 更有利。
    • @osexpert 我指出这可能是操作系统配置问题,而不是 .NET 应用程序问题。
    • 同意Merick,微软推荐使用SystemDefault,启用较新版本的TLS应该是windows update/hotfix的工作;
    【解决方案5】:

    我在针对 4.7.2 的旧版应用程序中遇到了同样的问题(仅限 Windows 10 和 SSL3 / TLS...不是系统默认设置)。我的问题是,在多年来的升级过程中,我们从未将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 的任何版本的框架。

    【讨论】:

    • 这正是我所拥有的。同样的,我们将一个应用升级到4.7.2,忘记更改httpRuntime
    • 这里一样,得到旧的 tls
    • 谢谢,这解决了问题...谁能告诉我为什么在项目升级到 .Net 4.7 后 httpRuntime 没有自动设置为 4.7.2?
    • Tx 你成功了!
    【解决方案6】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-03
      • 1970-01-01
      • 2017-10-23
      • 1970-01-01
      • 1970-01-01
      • 2016-06-16
      • 2018-10-31
      • 2017-07-18
      相关资源
      最近更新 更多