仅在一个特定的计算机上,针对.NET应用程序的SSL错误“收到的消息不符合预期或格式不正确”

18

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

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层面上,我们的证书内容在这里并不相关。

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

认证服务的构造函数:

  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"));
        }

获取授权令牌的代码:

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(该协议在失败的机器上已经激活和启用)。

有趣的是,我可以通过HTTPS在浏览器中完全访问服务器URL,这表明我的代码存在问题,而不是机器,但在其他地方它可以正常工作。

我确认:

  • 我在机器上使用来验证连接的证书是有效的,并且具有信任链(尽管如上所述,我们似乎没有达到TLS连接本身失败的那一步)
  • 我们调用的服务器支持TLS 1.2(通过强制使用它)
  • 我可以独立通过浏览器访问URL的网站

是否需要在代码或机器上执行某些操作才能在各处使此调用正常工作?

我尝试过的解决方法

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

3个回答

18
我可能能至少指出您正确的方向...

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

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

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

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

一个快速的警告:有些密码套件比其他的更安全,所以您需要阅读最佳实践。

附录
如果您想进一步诊断故障,我建议在安装 .NET Core 应用程序的机器上安装 Wireshark(另一个免费工具:https://www.wireshark.org/#download)。如果 TLS 握手失败是问题所在,您将会看到以下信息:Alert (Level: Fatal, Description: Handshake Failure)

这篇关于 Wireshark 输出的入门指南对我很有帮助:https://blog.catchpoint.com/2017/05/12/dissecting-tls-using-wireshark/


嗨,感谢您的回答。我安装了Wireshark并且看起来是由于握手失败导致的。如果Chrome能够支持比Windows本地更多的密码套件,那么我该如何将其添加到我的应用程序中(我可以控制客户端应用程序但无法控制服务器)?谢谢。 - Thomas
客户端应用正在运行在你拥有管理员权限的Windows 8.1电脑上,对吗?(你提到了更新)。如果IIS Crypto没有用处,我想这篇文章可能会有帮助:https://www.howtogeek.com/221080/how-to-update-your-windows-server-cipher-suite-for-better-security/ - VeritasKhan
当我连接到由Cloudflare提供的服务器时,在Win7上的netcore 3.0应用程序中出现了类似的TLS握手错误System.ComponentModel.Win32Exception(0x80090326):接收到的消息意外或格式不正确。 Wireshark确实指出了握手问题。 - Anton Krouglov
我也遇到了密码套件的同样问题。使用IIS加密工具,按照应用程序上的最佳实践按钮选择套件,重新启动Windows,错误消失了。 - Pradeep

1

我遇到了类似的问题,为了帮助其他人,这是我的结论:

成功执行此代码并不意味着您的应用程序支持指定的协议版本,当尝试建立连接时仍然可能出现“SSL错误”:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

在我的情况下,我试图强制使用Tls13,但发现我的应用程序配置实际上不支持它:
  • Net core 3.0 运行在 Windows Server Datacenter 2019, version 1809
因此,我不得不更改我的配置如下,以提供我所需的协议版本的支持:
  • Net framework 5.0Windows Server Datacenter 2022, OS build 20348.288 上运行
我试图连接一个突然停止支持 Tls 1.2 的端点(不确定为什么),从那时起只接受 Tls 1.3。

0

我在使用 MySql 8.0.32 版本时遇到了同样的问题,不得不将其降级为 8.0.26 版本。


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