.NET 4.7中未显式调用ServicePointManager.SecurityProtocol时,TLS 1.2无法协商。

56

我需要升级一个.NET应用程序以支持调用仅支持TLS 1.2的网站上的API。从我所读的内容来看,如果该应用程序针对4.6或更高版本,则默认使用TLS 1.2。

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

1
如果您愿意,可以将tls版本进行OR运算以支持它们。但是为了避免这种需要,使用HttpClient(如果您可以使代码异步而没有任何阻塞调用)是更好的选择。 - Crowcoder
2
我不想定义安全协议,因为当出现新的协议(例如TLS 1.3)时,它不会被使用,因为它不在列表中。我将使用HttpClient重写我的测试,HttpClient使用的默认安全协议与HttpWebResponse有什么区别吗? - Josh
@Crowcoder,使用HttpClient得到了相同的结果,已更新问题代码。 - Josh
1
有趣。看看这个关于v4.7的发布说明 https://github.com/Microsoft/dotnet/blob/master/releases/net47/dotnet47-changes.md#networking - Crowcoder
1
很有趣。我尝试将其设置为“SystemDefault”,但失败了。假设它是默认值,那么Win10操作系统应该使用TLS 1.2作为值。 - Josh
显示剩余5条评论
7个回答

48

我遇到了同样的问题(Windows 10和SSL3 / TLS only...不是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。


2
这正是我所拥有的。同样的,我们将一个应用程序升级到了4.7.2版本,但忘记更改httpRuntime - jpgrassi
同样的问题,我也遇到了旧版 TLS。 - NeutronCode
5
谢谢,这解决了问题...有人能告诉我为什么在将项目升级到 .Net 4.7 后,httpRuntime 没有自动设置为 4.7.2 吗? - WonderHeart
谢谢你帮我解决了问题! - bau
太棒了!解决了我的问题。我已经升级到4.7.2,但没有更改Web配置:<httpRuntime targetFramework="4.7.2" maxRequestLength="15360" /> - steveareeno
这对我来说是一个非常奇怪的问题。在我的开发电脑上,有一个网站无法连接到DocuSign(使用最新的SDK),但另一个可以。它们都通过同一个内部程序集进行DocuSign调用,所以我认为这一定是一个配置问题。在失败的那个(遗留代码)中,httpRuntime的targetFramework = "4.7.2"设置缺失。添加了这个设置后,现在一切正常工作了。非常感谢! - RandyB

22

从针对.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>

请查看此处此处

更新(有用信息)

请记住,最佳安全实践建议定期更新您的IIS配置,禁用旧协议和密码密钥(例如TLS 1.0、1.1)。请参见设置 Microsoft Windows 或 IIS 以进行 SSL 完美前向保密和 TLS 1.2获取非常有用的信息。

如果您遵循此做法,则不需要设置上面的配置(如MS所建议),因为您的Win服务器/IIS已经配置良好。

当然,这仅适用于具有适当授权的服务器访问权限。


11
作为对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将协议的选择推迟到操作系统。这意味着当微软发布热补丁以禁用不安全的协议或在其本机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

1
同样适用于2008 R2。 - Brian
2
@osexpert 不需要。如果你设置“DontEnableSystemDefaultTlsVersion=true”,那么你会禁用操作系统的默认协议,只启用已安装的.NET版本中的默认协议。在我的情况下,如果操作系统已打补丁以启用新协议,则我希望它们能够被启用。我不想更新应用程序的配置或.NET框架版本,仅仅是为了启用新版本的TLS。 - MerickOWA
1
@osexpert 请查看我的更新答案,了解为什么这样做可能比更改应用程序以禁用SystemDefault更有优势。 - MerickOWA
1
@osexpert 我想指出这可能是操作系统配置问题,而不是 .NET 应用程序问题。 - MerickOWA
2
同Merick的意见一致,使用SystemDefault是微软推荐的做法,启用更新版本的TLS应该由Windows更新/热修复来完成。 - Yang
显示剩余4条评论

9
我找到了一个解决方案。它并没有回答为什么在Windows10上使用.NET 4.7时默认不使用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>

1
这没有意义。Switch.System.Net.DontEnableSchUseStrongCrypto 的默认值已经是 false。false:不使用不安全的协议(SSL),true:允许使用不安全的协议。这与 TLS1.2 没有任何关系。我不否认它正在工作(我没有测试过),只是说这毫无意义。 - osexpert
3
我尝试在一个简单的控制台应用程序中进行了测试,该应用程序向只支持TLS1.2的Web服务发出了一个HttpClient请求,并在Windows 7 SP1上运行。但很遗憾它没有任何效果。我要点踩。 - Dai
1
我在运行一个4.7.2的单元测试项目时,在一台运行Windows 10的机器上尝试了这个线程中的所有答案,唯一有效的方法是硬编码ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; -- 对着微软摇拳头 - Will P.
是的,这个解决方案对Windows 7 SP1没有影响。 - Jozef Izso

7

我使用的是Windows 7和.NET 4.7.1

在两个其他答案中提到使用Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocolsSwitch.System.Net.DontEnableSchUseStrongCrypto的建议在我的项目中没有起作用,OP的代码也无法正常运行。

通过审查ServicePointManagerLocalAppContextSwitches的源代码,我发现了另一个配置设置,它起作用了。

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

请看我的答案,其中提供了一种替代方案,仍然允许.NET保持其“SystemDefaultTls”设置。 - MerickOWA
此配置设置仅适用于控制台应用程序。 针对4.7或更高版本的Web应用程序将始终具有System.Net.ServicePointManager.SecurityProtocol = SystemDefault,除非它被明确硬编码为其他协议(这不是推荐的做法)。 - Yang

1
我曾经为一个使用.NET Framework 4.5.2开发并升级到4.7的Word VSTO插件项目遇到了同样的问题。尝试了这里的所有解决方案,但都没有起作用。最后在这篇文章中找到了解决方法:article
对于那些像我一样在VSTO插件项目中遇到相同问题的人,请将以下代码添加到ThisAddIn.cs类中,它对我有用。
AppContext.SetSwitch("Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols", false);
AppContext.SetSwitch("Switch.System.Net.DontEnableSchUseStrongCrypto", false);

0

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

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

非常有用的文章,附带设置为仅使用TLS1.2的脚本


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接