SSL 错误“收到的消息意外或格式错误"仅适用于一台特定机器上的 .NET 应用程序 [英] SSL Error "The message received was unexpected or badly formatted" for a .NET application on one specific machine only

查看:39
本文介绍了SSL 错误“收到的消息意外或格式错误"仅适用于一台特定机器上的 .NET 应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 .NET Core 3.1 C# 应用程序,它通过 HTTPS 调用 API(并在获取令牌的过程中提供其公钥,因为该证书稍后用于解密单独发回的信息).几乎在我们所有的机器上,它都可以工作,但在一台 Windows 8.1 机器上,当我们尝试最初连接身份验证令牌时,我们会遇到以下一系列异常:

I have a .NET Core 3.1 C# application which is calling an API via HTTPS (and presenting its public key as part of getting the token as that certificate is later used to decrypt information sent back separately). On just about all our machines, it is working, but on one Windows 8.1 machine, we get the following series of exceptions when we try to initially connect for an authentication token:

The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
---> System.ComponentModel.Win32Exception (0x80090326): The message received was unexpected or badly formatted.

异常是从 System.Net.Http.HttpClient.FinishSendAsyncBuffered 抛出的,所以我怀疑它发生在 HTTPS 级别,无论如何我们的证书内容在这里并不真正相关.

The exception is thrown from System.Net.Http.HttpClient.FinishSendAsyncBuffered so I suspect it is happening at the HTTPS level and our certificate stuff is not really relevant here anyway.

我们获取令牌的代码如下所示:

auth 服务的构造函数:

The constructor for the auth service:

  public XXXXAuthService(IXXDbService dbService, XXXXApiConfig config)
        {
            _dbService = dbService;
            _config = config;
            
            // try forcing TLS1.2 for SSL connection exceptions thrown in some operating environments
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            
            _httpClient = new HttpClient {BaseAddress = new Uri(config.BaseUrl)};
            _httpClient.DefaultRequestHeaders.Accept.Clear();
            _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }

获取身份验证令牌的代码:

Code to get the auth token:

private async Task<string> GetXXXXBearerToken(string userId, DateTime creationTime)
        {
            var token = await GenerateProviderJwtForXXXX(userId, creationTime);
            var kvp = new List<KeyValuePair<string, string>>
            {
                new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange"),
                new KeyValuePair<string, string>("subject_token", token),
                new KeyValuePair<string, string>("subject_token_type", "urn:ietf:params:oauth:token-type:jwt")
            };
            var data = new FormUrlEncodedContent(kvp);
            var publicKey = await GetXXXXPublicKey();

            _httpClient.DefaultRequestHeaders.Remove("X-XXXX-Public-Cert");
            _httpClient.DefaultRequestHeaders.Add("X-XXXX-Public-Cert", publicKey);

            var response = await _httpClient.PostAsync("Identity/token", data);
            if (!response.IsSuccessStatusCode)
                throw new Exception("XXXX Token Server Error: " + response.ReasonPhrase);
            var result = await response.Content.ReadAsStringAsync();

            var authResponse = JsonConvert.DeserializeObject<OAuthResponse>(result);

            if (!string.IsNullOrEmpty(authResponse.access_token))
                return authResponse.access_token;

            System.Diagnostics.Trace.WriteLine("Token Exchange Result: " + result);
            if (!string.IsNullOrEmpty(authResponse.error))
            {
                var outcome = new XXX.XXXX.Model.OperationOutcome();
                outcome.Issue.Add(new XXX.XXXX.Model.OperationOutcome.IssueComponent()
                {
                    //some code to throw an error is here
            }

            throw new XXX.XXXX.Rest.XXXXOperationException("Bearer Token Exchange failed", response.StatusCode);
        }

不幸的是,Stack Overflow 或网络其他地方的任何现有问题/建议都没有针对此特定错误提供帮助.它们主要是关于客户端和服务器之间的版本差异,这似乎不是这里的情况,因为我强制使用 TLS 1.2(它在故障机器上处于活动状态并启用).

Unfortunately none of the existing questions/advice anywhere on Stack Overflow, or the rest of the web, for this particular error seems to have helped. They are primarily about version discrepancies between client and server which seems not to be the case here as I am forcing TLS 1.2 (which is active and enabled on the failing machine).

有趣的是,我可以通过 HTTPS 在浏览器中访问服务器 URL 就好了,这表明我的代码有问题而不是机器有问题,但它在其他任何地方都有效.

Interestingly, I can visit the server URL in a browser via HTTPS just fine, which suggests there is something about my code that is the problem rather than the machine, but it works everywhere else.

我已经确认:

  • 我用来对机器上的连接进行身份验证的证书是有效的,并且具有信任链(尽管如上所述,我认为我们还没有达到 TLS 连接本身失败的程度)
  • 我们调用的服务器支持 TLS 1.2(通过强制)
  • 我可以通过浏览器独立访问该网址的网站

是否需要在代码中或机器上执行某些操作才能使此调用在任何地方都能正常工作?

Is there something I need to do either in the code or on the machine to get this call to work everywhere?

我尝试解决的问题

  • 安装目前所有的 Windows 8.1 更新
  • 在代码中强制使用 TLS 1.2(参见上面的代码示例)
  • 将 VM 限制为仅限 TLS 1.2

推荐答案

我或许至少可以为您指明正确的方向……

I might be able to at least point you in the right direction…

相同的症状
我有一个在 IIS(Windows Server 2012 R2)上运行的 .NET Core 3.1 Web 应用程序,当它尝试使用 TLS 1.2 连接到另一台服务器时,它得到了完全相同的错误和堆栈跟踪.我也出现了可以连接浏览器(Chrome),但不能连接应用程序的症状.(看看 Internet Explorer 浏览器是否工作会很有趣.)

Same Symptoms
I had a .NET Core 3.1 web app running on IIS (Windows Server 2012 R2) that got the exact same error and stacktrace when it tried to connect to another server using TLS 1.2. I also had the symptom where I could connect with the browser (Chrome), but not with the app. (Would have been interesting to see if Internet Explorer browser worked though.)

根本原因
TLS 握手失败,因为两台服务器无法就共同的密码套件达成一致.(使用 Wireshark,我发现当我的应用尝试连接时,它提供的密码套件集比 Chrome 浏览器发出呼叫时更有限.)

Root Cause
The TLS handshake was failing because the two servers were unable to agree on a common cipher suite. (Using Wireshark, I discovered that when my app tried to connect it provided a more limited set of cipher suites than when the Chrome browser made the call.)

解决方案
就我而言,我使用了 IIS Crypto(一个小的免费工具:https://www.nartac.com/Products/IISCrypto/) 以在我的 Web 应用程序的服务器上启用其他密码套件.我下载并运行了 IIS Crypto,在其密码套件选项卡上勾选了其他密码套件,然后重新启动了机器.

Solution
In my case, I used IIS Crypto (a small free tool: https://www.nartac.com/Products/IISCrypto/) to enable additional cipher suites on my web app's server. I downloaded and ran IIS Crypto, checkmarked additional cipher suites on its Cipher Suites tab, and then restarted the machine.

其中一个新密码套件与我的应用程序和目标服务器配合使用,因此 TLS 握手成功并且错误得到解决.

One of the new cipher suites worked with my app and the destination server, so the TLS handshake was successful and the error was resolved.

一个快速警告:某些密码套件比其他密码套件更安全,因此您需要阅读最佳实践.

One quick caveat: Some cipher suites are more secure than others, so you'll want to read up on best practices.

附录
如果您想进一步诊断故障,我建议您安装 Wireshark(另一个免费工具:https://www.wireshark.org/#download) 与您的 .NET Core 应用程序的计算机上.如果问题是 TLS 握手失败,您将看到如下消息:警报(级别:致命,描述:握手失败)

Addendum
If you want to further diagnose the failure, I'd recommend installing Wireshark (another free tool: https://www.wireshark.org/#download) on the machine with your .NET Core app. If a TLS Handshake Failure is the issue, you will see a message like: Alert (Level: Fatal, Description: Handshake Failure)

这本关于wireshark输出的入门帮助了我:https://blog.catchpoint.com/2017/05/12/dissecting-tls-using-wireshark/

This primer on wireshark output helped me: https://blog.catchpoint.com/2017/05/12/dissecting-tls-using-wireshark/

这篇关于SSL 错误“收到的消息意外或格式错误"仅适用于一台特定机器上的 .NET 应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆