Azure w/TLS/SNI 上的 .net httpclient:请求被中止:无法创建 SSL/TLS 安全通道

Posted

技术标签:

【中文标题】Azure w/TLS/SNI 上的 .net httpclient:请求被中止:无法创建 SSL/TLS 安全通道【英文标题】:.net httpclient on Azure w/ TLS/SNI: The request was aborted: Could not create SSL/TLS secure channel 【发布时间】:2020-10-23 09:47:29 【问题描述】:

之前曾多次提出类似问题 - 但在将此问题标记为重复之前,请继续阅读。这些问题中的大多数都是非常古老的。我已经解决了很多问题和答案,但没有找到合适的解决方案。

我们在 .net 4.5 中有一个 Azure 云服务项目。它可以毫无问题地连接到我们客户的数十个 API(不一定是云托管),但单个 API 失败并显示以下错误消息:

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

我在这里错过了什么?

这是我用来连接到 API 的代码(略微精简)(每个 API 运行,因此基本 URL 不会改变):

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

ApiClient = HttpClientFactory.Create();
ApiClient.DefaultRequestHeaders.Authorization = null;
ApiClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Basic passwordToken");
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

SemaphoreSlim throttler = new SemaphoreSlim(initialCount: 50);

var sp = ServicePointManager.FindServicePoint(new Uri(baseUrl));
sp.SetTcpKeepAlive(true, 30000, 30000);

foreach (var request in urls)

    Result = new HttpResponseMessage();
    Result = await ApiClient.GetAsync(url);
    ...

这就是难以调试的原因:

此问题发生在生产中,即作为 Azure 云服务运行时。在本地调试时不会。 它只发生在通过 HttpClient 发送的请求中。不使用 WebClient。 进一步研究(比较 API)发现 此 API 是唯一启用 SNI 且仅支持 TLS1.2 的 API。

从其他关于 .net Framework 中 SNI 的问题/答案中考虑的建议:

为防止误解:这是关于连接到 API 的云服务,而不是关于正在建立到云服务的连接。 HttpClient 实例被重新用于对单个 API 的所有请求。 (这很重要,因为this answer 建议将使用已初始化的域 HttpClient 创建 SNI 标签)。 I have also tried configuring TLS after the Factory instantiated the HttpClient。没有变化。 证书当然是有效的。没有自签名证书,但有现成的常规受信任证书。在任何浏览器中打开 API 也很有效。 .net framework 4.5 中默认不启用 TLS1.2,但 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 行实际上应该启用它。我这样做有什么问题吗? 使用 curl --user 'user:pwd' https://myurl 在 curl(WSL 和 Azure 远程 bash)中调用 API 也可以正常工作并返回预期的数据。 使用像openssl s_client -connect hostname:443 -tls1_2 这样的openssl 测试tls1.2 不会发现任何问题。链显示正确,并确认了 TLSv1.2 会话。使用带有 openssl s_client -connect host:443 -tls1_2 -servername host -tlsextdebug -msg 的 openssl 测试服务器的 SNI 功能通过返回 TLS server extension "server name" (id=0), len=0 来显示 SNI 支持如果我提供完全不同的幻想主机名,我将获得相同的证书。 我在本地调试时捕获了 TLS/SNI 握手(请参见下面的屏幕截图)。没有问题。我的调试能力以云服务结束。我很想看到云服务和 WireShark 中的 API 之间的握手,但我不知道在 Azure 云服务上分析该层网络流量的任何选项。但如果有人知道如何捕捉握手过程,我会很感激一些提示。 服务器在与 openssl 握手期间选择 ECDHE-RSA-AES256-GCM-SHA384 作为密码套件,这对于 TLS1.2 来说几乎是默认设置。我无权访问它将在 Client Hello 中提供的密码套件的云服务列表 - 知道如何找到吗? 我没有任何证据表明 SNI 确实导致了问题,但这是此 API 与我能发现的许多其他 API 之间的唯一区别。

堆栈跟踪:

System.Net.Http.HttpRequestException:发送请求时出错。 ---> System.Net.WebException:请求被中止:无法创建 SSL/TLS 安全通道。在 System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) 在 System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar) --- 内部异常堆栈跟踪结束 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at WCFServiceWebRole1.Controllers... 在 [调用 GetAsync() 的行]

【问题讨论】:

由于 Microsoft 在禁用 TLS 1.0/1.1 的服务器上进行安全更新,今年 6 月一切都发生了变化。微软在进行网络更新时也会更改为最新的默认值,因此 SHA 的版本也会发生变化。 SHA 必须匹配证书。我会检查证书以查看 SHA 的版本。还要更改代码以仅使用 TLS 1.2/1.3:SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13 感谢您强调这一点。我不能减少对 TLS 1.2 和 1.3 的支持,因为许多客户端还不支持这些。这些环境不在我的控制之下。 我认为您需要与您的客户讨论这个问题。一旦他们安装了 microsoft 安全更新,该应用程序可能会停止工作。检查以下 wiki 站点。使您的证书与您的客户正在使用的所有 TLS 版本兼容 (en.wikipedia.org/wiki/Transport_Layer_Security)。我怀疑所有人都在使用 TLS 1.2/1.3,因为微软安全更新禁用了其他版本的 TLS 将 TLS1.3 添加到您的选择列表中。 this API is the only one 实际上,现在每个人都需要 TLS1.2。所有云提供商、主要服务、支付行业等。这意味着谷歌也需要 TLS1.2。 【参考方案1】:

我最终在我的一台服务器上重新创建了一个简单的 API,并以这样一种方式配置了软件,以便在那里发送它的请求。这样我就可以捕获 TLS 握手并在 Wireshark 中对其进行分析。这些是受支持的密码套件(客户端,即 Azure 云服务):

这些是 API 支持但不起作用的密码套件:

我认为应该有一个匹配项,以便服务器和客户端可以就一个匹配项达成一致。但是,我找不到匹配项......猜测这就是导致问题的原因。事实上,在本地调试会话中支持的密码套件列表要长得多 - 并且至少有一个匹配项可以解释它为什么在本地工作。

【讨论】:

您在代码中明确启用了 weaker TLS 版本。所有云供应商、主要服务、航空公司、支付处理器已经切换到至少 TLS1.2 4 年了。将您的 .NET 版本升级到自动使用 TLS1.2 的版本,问题就会消失。实际上,这是 4.7.2 或更高版本,以避免与 .NET Standard 包的兼容性问题

以上是关于Azure w/TLS/SNI 上的 .net httpclient:请求被中止:无法创建 SSL/TLS 安全通道的主要内容,如果未能解决你的问题,请参考以下文章