请求中止:无法创建SSL/TLS安全通道

716

由于以下错误信息,我们无法使用WebRequest连接到HTTPS服务器:

The request was aborted: Could not create SSL/TLS secure channel.

我们知道服务器在所使用的路径上没有有效的HTTPS证书,但为了绕过这个问题,我们使用从另一个StackOverflow帖子中获取的以下代码:

private void Somewhere() {
    ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(AlwaysGoodCertificate);
}

private static bool AlwaysGoodCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors) {
   return true;
}

问题在于服务器从未验证证书,因此出现上述错误。有人知道我该怎么做吗?


我应该提到,几周前我和一位同事进行了测试,并且使用类似我上面写的东西正常工作。我们发现唯一的“主要区别”是我使用的是Windows 7,而他使用的是Windows XP。这会改变什么吗?


4
也请查看这个链接 https://dev59.com/I3I-5IYBdhLWcg3w8dYQ - Oskar Kjellin
135
到了2018年,这个问题已经被查看了308,056次,但仍然没有适当的解决方法!!我会随机遇到这个问题,这里或其他线程提到的任何修复措施都不能解决我的问题。 - Nigel Fds
7
@NigelFds 错误The request was aborted: Could not create SSL/TLS secure channel是一个非常通用的错误。它基本上表示“由于许多可能的原因之一,SSL/TLS/HTTPS连接初始化失败”。因此,如果您在特定情况下经常遇到这个错误,最好的选择是提出一个具体的问题,并提供有关该情况的具体细节。同时,检查事件查看器以获取更多信息。另外,启用一些.NET客户端调试以获取更多详细信息(例如服务器证书是否不受信任?是否存在密码匹配问题?SSL/TLS协议版本不匹配等)。 - MarnixKlooster ReinstateMonica
6
@MarnixKlooster,我已经检查了所有内容,证书不可能是问题,因为如果我重试它,它就可以工作。我怀疑如果我在 SO 上提出这个问题,没有人会标记它为重复或其他什么。 - Nigel Fds
5
使用4.5.2几乎肯定是问题的主要原因。运行时确定安全协议的默认设置,而4.5.x只启用了SSL 3.0和TLS 1.0,这意味着如果您的应用程序调用了一个已禁用TLS 1.0的API,它将无法工作。尝试使用更高版本的.NET Framework,最好是4.7或更高版本。请参见我的答案获取更多详细信息,特别是如果您的应用程序是ASP.NET站点。 - JLRishe
显示剩余14条评论
51个回答

870

我终于找到了答案(我没有记录来源,但是是从搜索中获得的);

虽然代码在 Windows XP 上可以工作,在 Windows 7 上必须在开头添加以下内容:

// using System.Net;
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// Use SecurityProtocolType.Ssl3 if needed for compatibility reasons

现在它完美地工作了。


补充说明

如Robin French所提到的,如果您在配置PayPal时遇到此问题,请注意他们将从2018年12月3日开始不再支持SSL3。 您需要使用TLS。 这是Paypal网页


6
降低到SecurityProtocolType.Tls12实际上解决了我的问题。请参见下面的回答。 - Bryan Legend
27
SSLv3已经有18年的历史了,现在容易受到POODLE漏洞攻击 - 正如@LoneCoder所建议的那样,SecurityProtocolType.Tls12是取代SecurityProtocolType.Ssl3的适当选择。 - gary
4
在找到漏洞之前,SecurityProtocolType.Tls可能是更好的选择(写作时并不是所有网站都支持Tls12)。请注意,这句话已经被翻译成中文。 - gary
25
请参考这篇文章。您不需要将其专门设置为单个类型,也可以进行追加。System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12; - Nae
4
您无需设置 ServicePointManager.Expect100Continue = true;。它已默认启用(参见此处)。 - Gucu112
显示剩余22条评论

268

在.NET 4.5中解决方案为

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

如果您没有.NET 4.5,则使用

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

20
谢谢!我需要使用.NET 4.0,但不知道如何解决这个问题。这似乎在这里起作用了。 :) - Fabiano
2
无法在Windows Server 2008R2上运行(可能在2012上也是如此) - Misam
@billpg,请阅读此文以获取更准确的答案。 - Vikrant
5
针对 VB 类型(因为此答案在 Google 中显示),等效代码是 ServicePointManager.SecurityProtocol = DirectCast(3072, SecurityProtocolType) - ConfusionTowers
适用于我的.NET 3.5应用程序。 - tala9999

193

在创建 HttpWebRequest 之前,请确保已设置 ServicePointManager 设置,否则它将无法正常工作。

有效:

ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
       | SecurityProtocolType.Tls11
       | SecurityProtocolType.Tls12
       | SecurityProtocolType.Ssl3;

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://google.com/api/")

失败:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://google.com/api/")

ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
       | SecurityProtocolType.Tls11
       | SecurityProtocolType.Tls12
       | SecurityProtocolType.Ssl3;

5
你提到的“Works”和“Fails”之间有什么区别?(意思为:以上提到的“Works”和“Fails”有何不同之处?) - Chandy Kunhu
8
太棒了。我的请求只有在第二次尝试后才有效,这没有意义,然后我看到了你的帖子,在请求之前移动了安全协议,嗯哼,修复了。谢谢 @hogarth45 - deanwilliammills
3
没错!当我把 ServicePointManager 放在请求创建之前时,它对我有用了。谢谢你,伙计,你救了我的一天。 - user9867960
2
在我们的情况下,请求第一次失败了,但之后就成功了。这正是这个答案所述原因的原因! - Mohammad Dehghan
2
我简直不敢相信,像初始化顺序这样的小问题竟然为我解决了这个问题。摇头!感谢@horgath45!! - Steve H.
显示剩余3条评论

76

注意: 这里有几个得票最高的答案建议设置ServicePointManager.SecurityProtocol,但是微软明确建议不要这样做。下面,我将介绍此问题的典型原因以及解决它的最佳实践。

这个问题的最大原因之一是运行的.NET Framework版本。.NET Framework运行时版本默认启用哪些安全协议。

  • 在ASP.NET网站中,框架运行时版本通常在web.config中指定。(见下文)
  • 在其他应用程序中,运行时版本通常是项目构建的版本,无论它是否在具有新.NET版本的计算机上运行。

似乎没有任何权威的文档说明它在不同版本中特别如何工作,但是似乎默认情况下是由以下方式更多或更少地确定的:

框架版本 默认协议
4.5及更早版本 SSL 3.0、TLS 1.0
4.6.x TLS 1.0、1.1、1.2、1.3
4.7+ 系统(操作系统)默认

对于旧版本,您的情况可能会因系统上安装了哪些.NET运行时而有所不同。例如,可能存在这样一种情况,即您正在使用非常旧的框架,并且不支持TLS 1.0,或者使用4.6.x并且不支持TLS 1.3。

微软的文档 强烈建议使用4.7+和系统默认:

我们建议您:

  • 在您的应用程序上针对.NET Framework 4.7或更高版本进行目标设置。在您的WCF应用程序上针对.NET Framework 4.7.1或更高版本进行目标设置。
  • 不要指定TLS版本。配置您的代码以让操作系统决定TLS版本。
  • 进行彻底的代码审计,确保您没有指定TLS或SSL版本。

对于ASP.NET网站:请检查<httpRuntime>元素中的targetFramework版本,因为这(当存在时)确定实际使用的运行时。

<httpRuntime targetFramework="4.5" />

更好:

<httpRuntime targetFramework="4.7" />

对我来说,添加到<httpRuntime>是解决方法。 - BrianK
我在我的一个项目中遇到了同样的问题(之前它一直正常)。原来是因为网站随着时代的变化不再支持旧的安全协议。我将项目的框架从4更新到4.6.1,而无需进行任何代码更改,它就可以正常工作了。 - Skyfish
这个解决方案在我的三个Web应用程序中为我解决了问题。 - nshathish
被接受的答案由Simon Dugre提供,本来对我也适用,但我需要在多个地方添加安全协议定义。在web.config中更新目标框架解决了整个应用程序的问题。 - mikeck
这应该是最佳和被接受的答案,这是正确的解决方案,并为我解决了问题。 - nmase88
这对我解决了问题。即使应用程序是为.NET 4.5编译的,我将targetFramework设置为4.7。网站按预期工作,并且可以使用更新的协议版本。我的建议是检查安装的框架版本,并在此设置中使用最新的可能版本。 - mmyta

47

我在尝试访问https://ct.mob0.com/Styles/Fun.png时遇到了问题,这是CloudFlare在其支持诸如SPDY和奇怪重定向SSL证书的CDN上分发的图像。

与Simons答案中指定Ssl3不同,我通过像这样降到Tls12来修复它:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
new WebClient().DownloadData("https://ct.mob0.com/Styles/Fun.png");

谢谢Lone...这真的很疯狂,因为问题似乎有许多不同的可能性,具体取决于情况...而且,据我所见,没有真正的文档记录。好吧,感谢你指出给那些可能会遇到同样问题的人。 - Simon Dugré
1
这对我有用。当我从办公室的局域网切换到家庭网络时,我遇到了错误。同样的代码,同样的笔记本电脑! - Amal
你是否总是(在所有请求中)或者有时候会出现错误? - PreguntonCojoneroCabrón

42
你遇到的问题是 aspNet 用户没有访问证书的权限。你需要使用 winhttpcertcfg.exe 来授权访问。
设置示例请参见:http://support.microsoft.com/kb/901183,详见更多信息中的第 2 步。
编辑:在较新版本的 IIS 中,此功能内置于证书管理工具中,可通过右键单击证书并使用管理私钥选项来访问。更多细节请参阅:https://serverfault.com/questions/131046/how-to-grant-iis-7-5-access-to-a-certificate-in-certificate-store/132791#132791

我尝试执行winhttpcertcfg.exe...需要注意的是我使用的是Windows 7。这会有影响吗? - Simon Dugré
我不确定是否相关,但是这篇帖子给了我一个想法,当我从VS中进行此调用时,以管理员身份运行VS,并且这为我解决了问题。 - PFranchise
2
在Windows 7及更高版本中,证书必须存储在本地计算机而不是当前用户的存储库中,以便“管理私钥”。 - Lukos
2
没错,这就是我的问题。使用mmc.exe,添加证书控制台(对我来说,我选择了“本地计算机”)。右键点击证书,所有任务,管理私钥。添加“everyone”(对于本地开发来说,这是最简单的 - 生产环境显然需要您明确的IIS网站应用程序池/用户)。 - Ian Yates

41
经过长时间的研究,我发现客户端服务所运行的ASP.NET账户没有访问证书的权限。我通过进入Web应用程序所运行的IIS应用程序池,进入高级设置,并将身份从NetworkService更改为LocalSystem账户来解决这个问题。
更好的解决方案是让证书与默认的NetworkService帐户一起工作,但对于快速功能测试,此方法有效。

1
在找到这篇文章之前,我经历了数天不断增加的挫败感。在我的情况下,AppPool以ApplicationPoolIdentity身份运行,这是默认设置,但将其更改为LocalSystem解决了问题。 - Hanno
1
如果我的应用程序是控制台应用程序,而且还是在 .NET Core (FW 5.0) 中开发的呢? - Rohit Vyas
这就是我们所缺少的。非常感谢你! - Kevin Owino

37
错误是普遍的,SSL/TLS 协商失败的原因有很多。最常见的是无效或过期的服务器证书,通过提供自己的服务器证书验证钩子,您已经解决了这个问题,但这并不一定是唯一的原因。服务器可能需要双向身份验证,可能配置了一套您的客户端不支持的密码套件,它的时间偏移量可能太大,导致握手失败等等。
最好的解决方案是使用 SChannel 故障排除工具集。SChannel 是负责 SSL 和 TLS 的 SSPI 提供程序,您的客户端将使用它进行握手。请参阅 TLS/SSL 工具和设置
此外,请参阅 如何启用 Schannel 事件日志记录

在Windows 7-8-10中,“Schannel事件日志记录”的路径在哪里? - PreguntonCojoneroCabrón
以编程方式在 C# 中解决 TLS/SSL 问题? - Kiquenet
@PreguntonCojoneroCabrón 这是路径:Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL,将EventLogging设置为1。通过基于源为Schannel的过滤器在“事件查看器”中查找这些日志。 - Ash K

26

使用设定的方法

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

看起来没问题,因为Tls1.2是最新的安全协议版本。但我决定深入研究并回答我们是否真的需要硬编码它。

规格:Windows Server 2012R2 x64。

从互联网上得知,.NetFramework 4.6+必须默认使用Tls1.2。但当我将项目更新到4.6时,什么也没有发生。 我找到了一些信息,告诉我需要手动进行一些更改才能默认启用Tls1.2

https://support.microsoft.com/en-in/help/3140245/update-to-enable-tls-1-1-and-tls-1-2-as-default-secure-protocols-in-wi

但建议的Windows更新程序不适用于R2版本

但对我有帮助的是向注册表添加2个值。您可以使用下一个PS脚本,这样它们将自动添加

Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord
这差不多是我在找的东西。但我仍然无法回答为什么NetFramework 4.6+不会自动设置这个...Protocol值的问题?

更改后需要重新启动服务器吗? - Sharon
@Sharon 如果你在说机器的话,那么不需要,只要重新启动应用程序/主机就够了。 - simply good
1
在我的情况下,添加注册表键有所帮助。 来自 https://learn.microsoft.com/en-us/dotnet/framework/network-programming/tls#schusestrongcrypto 的附加信息: “值为1会导致您的应用程序使用强密码。强密码使用更安全的网络协议(TLS 1.2、TLS 1.1 和 TLS 1.0),并阻止不安全的协议。值为0会禁用强密码。” 重新启动我的应用程序就足够了。 - bugybunny
@bugybunny 谢谢,我会更新答案。 - simply good
如果您的项目是一个ASP.NET网站,那么通常重要的是在web.config中指定的框架版本,而不是.csproj所针对的版本。我在我的回答中详细阐述了这一点。 - JLRishe

25
另一个可能导致“请求已中止:无法创建SSL/TLS安全通道”错误的原因是客户端PC配置的cipher_suites值与服务器愿意并能够接受的值不匹配。在这种情况下,当客户端在其初始SSL握手/协商“Client Hello”消息中发送其能够接受的cipher_suites值列表时,服务器会看到提供的值都不可接受,并可能返回一个“警报”响应,而不是继续进行SSL握手的“Server Hello”步骤。
要调查这个可能性,您可以下载Microsoft Message Analyzer,并使用它来对在您尝试并未能建立与服务器的HTTPS连接(在您的C#应用程序中)时发生的SSL协商进行跟踪。
如果您能够从另一个环境(例如您提到的Windows XP机器,或者通过使用不使用操作系统的密码套件设置的非Microsoft浏览器(如Chrome或Firefox)访问HTTPS URL)建立成功的HTTPS连接,请在该环境中运行另一个Message Analyzer跟踪以捕获SSL协商成功时发生的情况。
希望您能够看出这两个客户端Hello消息之间的差异,以便精确定位导致SSL协商失败的原因。然后,您应该能够对Windows进行配置更改,以使其成功。IISCrypto是一个很棒的工具,可以用于此(即使是客户端PC,尽管它有“IIS”名称)。
以下两个Windows注册表键管理PC将使用的cipher_suites值:
HKLM\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002
HKLM\SYSTEM\CurrentControlSet\Control\Cryptography\Configuration\Local\SSL\00010002
这是我如何调查和解决此种类型“无法创建SSL/TLS安全通道”问题实例的完整说明:http://blog.jonschneider.com/2016/08/fix-ssl-handshaking-error-in-windows.html

1
在我这种情况下,这个答案是有帮助的。此外,由于我怀疑我的客户端电脑缺少一些加密套件,我采取了捷径,并直接安装了此Windows更新来试试运气(https://support.microsoft.com/en-hk/help/3161639,需要Windows重新启动),然后发现我很幸运,它解决了我的问题,省去了我搜寻的时间。 - sken130
1
请注意,当您在Firefox等浏览器中测试HTTPS链接时,即使您获得的密码与任何给定Windows更新提供的密码不同,仍然值得尝试安装新密码,因为安装新密码将影响客户端PC和服务器之间的密码协商,从而增加找到匹配项的希望。 - sken130
4
针对我的问题要点回答。有两件事帮助我找到需要改变的地方。 1. 网络服务器支持的加密套件:https://www.ssllabs.com/ssltest/ 2. 不同Windows版本支持的加密套件:https://learn.microsoft.com/en-us/windows/win32/secauthn/cipher-suites-in-schannel - Paul B.

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