如何使用 C# 验证 SSL 证书

问题描述 投票:0回答:3

也许你们中的一位可以帮助我回答有关在 .NET 框架中验证证书的问题。不幸的是,我在网上搜索时只找到了我的问题的肤浅答案,但没有确切的解释。

同时我知道我可以使用以下代码在我的软件中检查证书是否有效。

ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;

private bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors policyErrors)
{
   return policyErrors == SslPolicyErrors.None;
}

但我现在想知道微软到底在这里检查什么?框架执行了哪些检查才能得到结果 SslPolicyErrors.None?

例如,根证书是否也在此处进行验证以及它来自哪里?如果是这样,怎么办?

您认为是否仍然有必要(或有用)在这些检查中添加我自己的检查,而 .NET Framework 尚未进行这些检查?

c# .net validation ssl certificate
3个回答
13
投票

这部分只是重复c#验证X509Certificate2:我这样做对吗?,那里的简短答案是您免费获得的检查(没有自定义处理程序,或者在您的处理程序之前已经完成) ):

  • 证书链有效(否则设置
    SslPolicyErrors.RemoteCertificateChainErrors
    • 证书链接至受信任的根颁发机构
    • 证书未过期(或未来)
    • 该证书表明其旨在用作 TLS 服务器证书
    • 如果在请求上启用了撤销(默认情况下处于关闭状态),则链中的任何证书都不会被撤销。
  • 证书适用于您要连接的主机名(否则设置
    SslPolicyErrors.RemoteCertificateNameMismatch

例如,根证书是否也在此处进行验证以及它来自哪里?

此处的证书验证使用与 Internet Explorer、Chrome、Edge、Outlook 等相同的根信任列表,该列表在 Windows 中集中管理。 (Firefox 使用不同的根信任列表,该列表仅由 Firefox 管理。)

您认为...

StackOverflow 上的意见问题与主题无关。

仍然有必要(或有用)在这些检查中添加我自己的检查,而 .NET Framework 尚未进行这些检查?

这要看情况。

如果您正在联系自己的服务器之一,并且您知道有关证书链的一些信息,则可以添加额外的检查。例如,您可能知道您的服务器使用 Let's Encrypt,并且您在应用程序中嵌入了来自 https://letsencrypt.org/certificates/ 的“Let's Encrypt Authority X3”证书,然后您可以固定中间...只需确保在他们转向新的中间体时做出反应即可。

private bool ValidateRemoteCertificate(
    object sender,
    X509Certificate cert,
    X509Chain chain,
    SslPolicyErrors policyErrors)
{
    if (policyErrors != SslPolicyErrors.None)
    {
        return false;
    }

    if (chain.ChainElements.Count != 3)
    {
        return false;
    }

    byte[] foundCert = chain.ChainElements[1].Certificate.RawData;

    return s_letsEncryptX3Direct.SequenceEqual(foundCert) ||
        s_letsEncryptX3Cross.SequenceEqual(foundCert);
}

如果您不打算执行额外的检查(或进行自定义日志记录等),那么最好甚至不要注册回调,因为当没有注册回调时,内存和 CPU 的实现会更好一些。


3
投票

只需检查链条状态。已经有描述性消息:

[Test]
public async Task CheckWhatError()
{
    var handler = new HttpClientHandler();
    handler.ServerCertificateCustomValidationCallback = ValidateRemoteCertificate;
    var client = new HttpClient(handler);
    try
    {
        using (var msg = new HttpRequestMessage(HttpMethod.Get, "https://expired.badssl.com/"))
        {
            using (var response = await client.SendAsync(msg))
            {
                // do stuff with the response
            }
        }
    }
    catch (Exception ex)
    {
        throw;
    }
}

private bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors policyErrors)
{
    if(policyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
    {
        foreach(var cs in chain.ChainStatus)
        {
            Console.WriteLine(cs.StatusInformation);//validation errors here
        }
    }
    return policyErrors == SslPolicyErrors.None;
}

0
投票

我在 c# 验证 X509Certificate2:我这样做对吗? 中发布了此内容,但我也会将其发布在这里以提高可见性。对于 .NET 5 及更高版本,请按照 https://github.com/dotnet/runtime/issues/39835:

中发布的示例进行操作
RemoteCertificateValidationCallback callback = (object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) =>
{
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        return true;
    }
    else if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors || chain is null || certificate is null)
    {
        // Name mismatch or no cert
        return false;
    }
    // name matches and there was a certificate

    // Only works with .NET 5 and higher, taken from
    // https://github.com/dotnet/runtime/issues/39835
    chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
    chain.ChainPolicy.CustomTrustStore.Add(/* Load your root certificate here */);
    chain.ChainPolicy.ExtraStore.Add(X509Certificate2.CreateFromPemFile(/* Load any intermediate certificates here */);
    return chain.Build(new X509Certificate2(certificate));
};
© www.soinside.com 2019 - 2024. All rights reserved.