c# 验证 X509Certificate2:我这样做对吗?

kev*_*der 5 c# ssl x509certificate2

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

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

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

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

bar*_*njs 8

如果您尝试验证 HTTPS 证书是否有效,HttpWebRequest 可以为您做到这一点。

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

然后将检查:

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

这就是您所问的所有三点。最困难的是使主机名正确匹配,因为

  1. 可能有多个SubjectAlternativeName DNS 条目,并且在.NET 中没有询问它们的好方法。
  2. 任何SubjectAlternativeName DNS 条目都允许包含通配符(*)。但主题 CN 值不是(并且 .NET API 不指示您返回的名称类型)。
  3. IDNA 等的名称标准化。

事实上,HttpWebRequest 不会自动为您做的唯一一件事(除非您设置全局)是检查撤销。你可以通过以下方式做到这一点

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

而且,值得注意的是,正如我在此处输入的示例代码一样,您只需要检查 chain.Build() 的返回值,因为如果任何证书过期或诸如此类的情况,这将是错误的。您可能还想检查构建链中的根证书(或中间证书或其他证书)是否为预期值(证书固定)。

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

您应该尝试验证器以确保其正常工作: