【问题标题】:.NET HttpClient/RestSharp with X509 Certificate throws "The SSL connection could not be established, see inner exception." after NET 5 upgrade带有 X509 证书的 .NET HttpClient/RestSharp 抛出“无法建立 SSL 连接,请参阅内部异常。” NET 5 升级后
【发布时间】:2021-09-08 01:30:06
【问题描述】:

我目前正在将代码从 .NET 4.7.2 升级到 .NET 5。当下面的代码在 NET 4.7.2 下运行时,它成功连接到网络服务器,在 NET 5 下它会抛出此异常:

Exception: "The SSL connection could not be established, see inner exception."
 -> InnerException: "Authentication failed, see inner exception."
   -> InnerException: "An unknown error occurred while processing"

为什么在 .NET 4.7.2 和 .NET 5 之间会有不同的行为?

如果您不提供证书,也会遇到同样的例外情况。我尝试从 .pfx 文件和本地机器的 X509Store 加载证书。我还让公司的其他开发人员尝试过,他们得到了相同的结果。

代码在 Windows 10 上运行,使用 VS 2019,但在部署到 Windows IIS 服务器时会出现同样的问题。

请帮忙,因为我没有想法

using RestSharp;
using System;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace SwipeTestNet5
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                // certificate store fetching
                var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);

                store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);

                // getting the certificate from the store based on the app setting certificateName
                // !! obviously this url is not the real one
                var certificateName = "test-cert-name";
                // the following line will error if there are certificates with the same friendly name... you shouldn't have let this happen
                var cert = store.Certificates.Cast<X509Certificate2>().SingleOrDefault(c => c.FriendlyName == certificateName);

                if (cert == null)
                    throw new Exception("Could not find certificate with name: " + certificateName);

                // Handle any certificate errors on the certificate from the server.
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

                // !! obviously this url is not the real one
                var url = "https://test.com.au/api";
                var client = new RestClient(url);
                var request = new RestRequest(Method.GET);

                client.ClientCertificates = new X509CertificateCollection(new[] { cert });

                // Handle any certificate errors on the certificate from the server.
                // !! When ran under .NET 4.7.2 this callback is triggred, under .NET 5 it never reaches
                client.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;

                // !! obviously this url is not the real parameters
                var parameters = new
                {
                    param1 = "a",
                    param2 = "b",
                    param3 = "c"
                };

                request.AddObject(parameters);

                var response = client.Execute(request);

                // !! Under NET 4.7.2 response.ErrorException is null and continues
                // !! Under NET 5 the response.ErrorException is populated with this:
                //      ErrorException: "The SSL connection could not be established, see inner exception."
                //          -> InnerException: "Authentication failed, see inner exception."
                //              -> InnerException: "An unknown error occurred while processing the certificate."
                if (response.ErrorException != null)
                    throw response.ErrorException;

                // !! NET 4.7.2 gets to here without issue
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    Exception ex = new Exception($"Error calling {client}");

                    ex.Data.Add("xml", response.Content);
                    ex.Data.Add("status code", response.StatusCode);
                    ex.Data.Add("param1", parameters.param1);
                    ex.Data.Add("param2", parameters.param2);
                    ex.Data.Add("param3", parameters.param3);

                    throw ex;
                }

                Console.WriteLine("Success");
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

【问题讨论】:

  • 请用标准HttpClient重试,看看是否有同样的问题。
  • 另外,在 .NET 5 中,应该没有必要乱用ServicePointManager.SecurityProtocol - .NET 支持并将自动使用适当的 SSL/TLS 协议。
  • @IanKemp 我用 HttpClient 得到了同样的行为

标签: c# .net ssl x509certificate .net-5


【解决方案1】:

问题出在这里:

var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);

默认情况下,应用程序池身份没有管理员权限,但需要该权限才能从本地计算机存储中读取密钥。明确授予对存储在本地计算机中的密钥的读取权限,或者在应用程序池身份的个人存储(当前用户上下文)中安装证书。

为了授予本地计算机的应用程序池身份读取权限,导航到证书,右键单击并选择所有任务 -> 管理私钥菜单项:

【讨论】:

  • 证书不为空,因为您可以读取证书的公共部分,但不能读取私钥。
  • 我会试一试,但当我直接从文件加载证书时,我会得到相同的行为。
  • 对不起,这不起作用。我已向当前用户和所有用户授予对证书的访问权限,仍然得到相同的结果。
【解决方案2】:

改变这个

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

有了这个

   ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
or
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;

或允许忽略错误

ServicePointManager
    .ServerCertificateValidationCallback += 
    (sender, cert, chain, sslPolicyErrors) => true;

【讨论】:

    猜你喜欢
    • 2019-06-29
    • 2021-12-09
    • 1970-01-01
    • 2020-04-23
    • 1970-01-01
    • 2020-06-25
    • 2020-04-04
    • 1970-01-01
    相关资源
    最近更新 更多