c#验证X509Certificate2:我这样做对吗? [英] c# Validating an X509Certificate2: am I doing this right?

查看:75
本文介绍了c#验证X509Certificate2:我这样做对吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用框架4.5.1和以下要求,我这样做正确吗?

Using framework 4.5.1 and the following requirement, am I doing this right?

  1. 证书中的URL必须匹配给定的URL
  2. 证书必须有效且受信任
  3. 证书不得过期

以下通过,但这足够了吗?

The following passes, but is this sufficient?

chain.Build(cert)的调用是否特别满足上面的#2 ?

    protected bool ValidateDigitalSignature(Uri uri)
    {
        bool isValid = false;
        X509Certificate2 cert = null;
        HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
        using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
        {
            response.Close();
        }

        isValid = (request.ServicePoint.Certificate != null);
        if(isValid)
            cert = new X509Certificate2(request.ServicePoint.Certificate);
        if (isValid)
        {
            X509Chain chain = new X509Chain();
            chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
            chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
            chain.Build(cert);
            isValid = (chain.ChainStatus.Length == 0);
        }
        if (isValid)
        {
            var dnsName = cert.GetNameInfo(X509NameType.DnsName, false);

            isValid = (Uri.CheckHostName(dnsName) == UriHostNameType.Dns
                && uri.Host.Equals(dnsName, StringComparison.InvariantCultureIgnoreCase));
        }
        if (isValid)
        {
            //The certificate must not be expired
            DateTimeOffset today = DateTimeOffset.Now;
            isValid = (today >= cert.NotBefore && today <= cert.NotAfter);
        }
        return isValid;
    }

推荐答案

如果您要尝试验证HTTPS证书有效,则HttpWebRequest可以为您做到这一点.

If you're trying to validate that an HTTPS certificate is valid, HttpWebRequest can do that for you.

要使HttpWebRequest检查吊销状态,您需要在调用 GetResponse()之前设置全局 ServicePointManager.CheckCertificateRevocationList = true (我认为是GetResponse,而不是对Create()的调用.

To make HttpWebRequest check the revocation status you need to set the global ServicePointManager.CheckCertificateRevocationList = true before calling GetResponse() (I think it's GetResponse, as opposed to the call to Create()).

这将检查:

  • 证书链接到受信任的根
  • 证书没有过期(以及其他类似的东西)
  • 请求主机名与应匹配的主机名

您要问的所有这三点是什么?最难的是使主机名匹配正确,因为

Which is all three points that you asked about. The hardest one is getting the hostname matching correct, because

  1. 可以有多个SubjectAlternativeName DNS条目,而在.NET中没有很好的方法来询问它们.
  2. 任何SubjectAlternativeName DNS条目都允许在其中包含通配符(*).但是主题CN值不是(并且.NET API不会指出您返回的是哪种类型的名称).
  3. IDNA的名称归一化,等等.

实际上,HttpWebRequest不会自动为您执行的唯一操作(除非您设置了全局设置)是检查吊销.您可以通过

In fact, the only thing that HttpWebRequest doesn't automatically do for you (unless you set the global) is check revocation. And you can do that via

HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
request.ServerCertificateValidationCallback = ValidationCallback;

private static bool ValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    // Since you want to be more strict than the default, reject it if anything went wrong.
    if (sslPolicyErrors != SslPolicyErrors.None)
    {
        return false;
    }

    // If the chain didn't suppress any type of error, and revocation
    // was checked, then it's okay.
    if (chain.ChainPolicy.VerificationFlags == X509VerificationFlags.None &&
        chain.ChainPolicy.RevocationMode == X509RevocationMode.Online)
    {
        return true;
    }

    X509Chain newChain = new X509Chain();
    // change any other ChainPolicy options you want.
    X509ChainElementCollection chainElements = chain.ChainElements;

    // Skip the leaf cert and stop short of the root cert.
    for (int i = 1; i < chainElements.Count - 1; i++)
    {
        newChain.ChainPolicy.ExtraStore.Add(chainElements[i].Certificate);
    }

    // Use chainElements[0].Certificate since it's the right cert already
    // in X509Certificate2 form, preventing a cast or the sometimes-dangerous
    // X509Certificate2(X509Certificate) constructor.
    // If the chain build successfully it matches all our policy requests,
    // if it fails, it either failed to build (which is unlikely, since we already had one)
    // or it failed policy (like it's revoked).        
    return newChain.Build(chainElements[0].Certificate);
}

并且,值得注意的是,正如我在此处的示例代码中所介绍的那样,您仅需要检查chain.Build()的返回值,因为如果任何证书过期或不存在,则返回false.您可能还需要检查构建链中的根证书(或中间证书或其他)是否为期望值(证书固定).

And, of note, as I put in the sample code here, you only need to check the return value of chain.Build(), because that will be false if any cert is expired or whatnot. You also may want to check the root cert (or an intermediate, or whatever) out of the built chain for being an expected value (certificate pinning).

如果ServerCertificateValidationCallback返回false,则会在GetResponse()上引发异常.

If the ServerCertificateValidationCallback returns false an exception is thrown on GetResponse().

您应该尝试使用验证器,以确保其有效:

You should try your validator out to make sure it works:

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆