仅在一台特定计算机上的 .NET 应用程序的 SSL 错误“收到的消息意外或格式错误”

Tho*_*mas 8 c# ssl

我有一个 .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.
Run Code Online (Sandbox Code Playgroud)

异常是从抛出的,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"));
        }
Run Code Online (Sandbox Code Playgroud)

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

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);
        }
Run Code Online (Sandbox Code Playgroud)

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

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

我已经确认:

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

我需要在代码中还是在机器上做些什么才能让这个调用在任何地方都能正常工作?

我试图解决问题的事情

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

小智 8

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

相同的症状
我有一个在 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 握手失败是问题所在,您将看到如下消息:警报(级别:致命,描述:握手失败)

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