首页 > 解决方案 > C# 使用 .pem 文件验证服务器证书

问题描述

我发现向启用 SSL 的 API 发送 http 请求时出现问题。我得到的错误信息是 -

AuthenticationException: The remote certificate is invalid according to the validation procedure.

根据这个要求

using (HttpResponseMessage res = client.GetAsync("https://example.com").Result)
            {
                using (HttpContent content = res.Content)
                {
                    string data = content.ReadAsStringAsync().Result;
                    if (data != null)
                    {
                        Console.WriteLine(data);
                    }
                    else
                    {
                        Console.WriteLine("Nothing returned");
                    }
                }
            }

我已经获得了一个 .pem 文件来验证发回的证书是否由我们的 CA 签名,并且在弄清楚如何在 C# 中执行此操作时遇到了一些麻烦

在python中,我可以通过将.pem文件传递给验证参数来解决证书错误,例如

r = requests.post(url="https://example.com", headers=headers, verify='mypem.pem') 

Dotnet Core 3 的 HttpClient 中是否有等价的东西?

感谢您的帮助!

标签: c#ssl

解决方案


如果由于某种原因您无法将证书设置为受信任,那么您可以绕过证书验证并自己验证服务器。不幸的是,它在 .NET 中的优雅程度要低得多,而且这可能不适用于所有平台。有关更多讨论,请参阅有关在 .net 核心中绕过无效 SSL 证书的答案。

using (var httpClientHandler = new HttpClientHandler())
{
    // Override server certificate validation.
    httpClientHandler.ServerCertificateCustomValidationCallback = VerifyServerCertificate;
    // ^ if this throws PlatformNotSupportedException (on iOS?), then you have to use
    //httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
    // ^ docs: https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.dangerousacceptanyservercertificatevalidator?view=netcore-3.0

    using (var client = new HttpClient(httpClientHandler))
    {
        // Make your request...
    }
}

我认为回调的这种实现可以满足您的需求,“固定” CA。从这个答案Force HttpClient to trust single Certificate,还有我的更多评论。 编辑:该答案的状态检查不起作用,但根据Jeremy Farmer 链接的这个答案,以下方法应该:

    static bool VerifyServerCertificate(HttpRequestMessage sender, X509Certificate2 certificate,
    X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        try
        {
            // Possibly required for iOS? :
            //if (chain.ChainElements.Count == 0) chain.Build(certificate);
            // https://forums.xamarin.com/discussion/180066/httpclienthandler-servercertificatecustomvalidationcallback-receives-empty-certchain
            // ^ Sorry that thread is such a mess!  But please check it.
            
            // Without having your PEM I am not sure if this approach to loading the cert works, but there are other ways.  From the doc:
            // "This constructor creates a new X509Certificate2 object using a certificate file name. It supports binary (DER) encoding or Base64 encoding."
            X509Certificate2 ca = new X509Certificate2("mypem.pem");

            X509Chain chain2 = new X509Chain();
            chain2.ChainPolicy.ExtraStore.Add(ca);

            // "tell the X509Chain class that I do trust this root certs and it should check just the certs in the chain and nothing else"
            chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;

            // This setup does not have revocation information
            chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;

            // Build the chain and verify
            var isValid = chain2.Build(certificate);
            var chainRoot = chain2.ChainElements[chain2.ChainElements.Count - 1].Certificate;
            isValid = isValid && chainRoot.RawData.SequenceEqual(ca.RawData);

            Debug.Assert(isValid == true);

            return isValid;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }

        return false;
    }

抱歉,我目前无法对此进行测试,但希望对您有所帮助。


推荐阅读