如何检查X509证书是否具有“;“扩展验证”;已打开

本文关键字:验证 扩展 扩展验证 何检查 检查 是否 证书 X509 | 更新日期: 2023-09-27 17:57:35

我正在努力从C#(.Net 4.0)应用程序中找到一种可靠的方法来检查X509Certificate(或X509Certification2)是否设置了"扩展验证"(EV)标志。有人知道最好的方法吗?

如何检查X509证书是否具有“;“扩展验证”;已打开

您可以检查X509Certificate是否包含这些OID之一。此外,您还可以查看Chromium的Source以获取已实现OID的列表。你可以在这里找到来源。如果你想坚持使用Firefox,你可以在这里获取实现。

我现在更新了我的源代码并对其进行了测试。我已经编写了一个小方法来根据Wikipedia/Chromium中的OId列表验证X509Certificate2。在这种方法中,我使用的是维基百科列表,最好使用Chromium列表。


OId是如何保存的

每个CA都有一个或多个ObjectId OId s。它们是而不是保存为扩展,正如您可能猜测的那样,它们保存为策略扩展中的条目。为了获得确切的扩展,建议使用Policy Extension本身的Oid,而不是使用友好名称。策略扩展的OId为2.5.29.32

提取信息

为了获得策略扩展的内部内容,我们可以使用System.Security.Cryptography.AsnEncodedData将其转换为可读的string。字符串本身包含我们需要与string[]匹配的策略,以确保它是否包含EV Certificate的OID之一。

来源

    /// <summary>
    /// Checks if a X509Certificate2 contains Oids for EV
    /// </summary>
    /// <param name="certificate"></param>
    /// <returns></returns>
    private static bool IsCertificateEV(X509Certificate2 certificate)
    {
        // List of valid EV Oids
        // You can find correct values here:
        // http://code.google.com/searchframe#OAMlx_jo-ck/src/net/base/ev_root_ca_metadata.cc&exact_package=chromium
        // or in Wikipedia
        string[] extendedValidationOids = 
        {
            "1.3.6.1.4.1.34697.2.1",
            "1.3.6.1.4.1.34697.2.2",
            "1.3.6.1.4.1.34697.2.1", 
            "1.3.6.1.4.1.34697.2.3", 
            "1.3.6.1.4.1.34697.2.4",
            "1.2.40.0.17.1.22",
            "2.16.578.1.26.1.3.3",
            "1.3.6.1.4.1.17326.10.14.2.1.2", 
            "1.3.6.1.4.1.17326.10.8.12.1.2",
            "1.3.6.1.4.1.6449.1.2.1.5.1",
            "2.16.840.1.114412.2.1",
            "2.16.528.1.1001.1.1.1.12.6.1.1.1",
            "2.16.840.1.114028.10.1.2",
            "1.3.6.1.4.1.14370.1.6",
            "1.3.6.1.4.1.4146.1.1",
            "2.16.840.1.114413.1.7.23.3",
            "1.3.6.1.4.1.14777.6.1.1", 
            "1.3.6.1.4.1.14777.6.1.2",
            "1.3.6.1.4.1.22234.2.5.2.3.1",
            "1.3.6.1.4.1.782.1.2.1.8.1",
            "1.3.6.1.4.1.8024.0.2.100.1.2",
            "1.2.392.200091.100.721.1",
            "2.16.840.1.114414.1.7.23.3",
            "1.3.6.1.4.1.23223.2", 
            "1.3.6.1.4.1.23223.1.1.1", 
            "1.3.6.1.5.5.7.1.1",
            "2.16.756.1.89.1.2.1.1",
            "2.16.840.1.113733.1.7.48.1",
            "2.16.840.1.114404.1.1.2.4.1",
            "2.16.840.1.113733.1.7.23.6",
            "1.3.6.1.4.1.6334.1.100.1",
        };
        // Logic:
        // Locate Certificate Policy Extension
        // Convert to AsnEncodedData (String)
        // Check if any of the EV Oids exist
        return (
                from X509Extension ext in certificate.Extensions 
                where ext.Oid.Value == "2.5.29.32" 
                select new AsnEncodedData(ext.Oid, ext.RawData).Format(true))
                .Any(asnConvertedData => extendedValidationOids.Where(asnConvertedData.Contains).Any()
            );
    }

如果你需要一些来源开始:

    static void Main(string[] args)
    {
        // Create Delegate for analysis of X509Certificate
        ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
        // Make sample request to EV-Website to get Certificate
        var wc = new WebClient();
        wc.DownloadString("https://startssl.com");  // EV
        wc.DownloadString("https://petrasch.biz");  // Not EV
        Console.ReadLine();
    }
    public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        var cert = (X509Certificate2) certificate;
        Console.WriteLine("Certificate: " + cert.GetNameInfo(X509NameType.SimpleName, true) + " -> " + IsCertificateEV(cert));
        return true;
    }

如果有人知道实现这一目标的更好方法,请告诉我们。

我想我会发布一个更完整的答案,尽管这个问题已经很老了。我不会依赖现有的答案,这样这个答案就完整了。

EV证书有一些检查需要通过,浏览器才能认为该证书是EV。

  1. 证书具有已知为EV策略的策略标识符
  2. 证书根的指纹与固定的策略标识符匹配
  3. 证书必须通过联机吊销检查
  4. 如果证书的notBefore(颁发日期)在2015年1月1日之后,则证书必须支持证书透明度
  5. 证书必须由受信任的根颁发
  6. 如果存在多个信任路径,则所有链都是有效的

让我们剖析一下每一个。

策略标识符

证书有一个名为策略标识符的扩展。可以从X509Certificate2.Extensions属性访问扩展。策略标识符扩展的对象标识符("OID")为2.5.29.32。所以我们可以使用这样的东西来获得原始扩展:

var extension = certificate.Extensions["2.5.29.32"]

如果返回null,意味着根本没有策略,那么您可以立即假设这不是EV证书。

更可能的是证书有某种策略。在这种情况下,您需要对数据进行解码。属性将在原始ASN.1中提供给您,我们需要从中获得意义。

不幸的是,目前.NET中没有任何东西可以开箱即用。但是,如果您使用平台调用,CryptDecodeObjectEx可以做到这一点。关于这样做的细节我将省略,但有很多信息可以展示如何调用此函数。您需要使用设置为值(IntPtr)16的lpszStructType参数来调用它。这将返回一个CERT_POLICIES_INFO结构,它有一个计数和指向CERT_POLICY_INFO结构数组的指针。这个结构上有一个名为pszPolicyIdentifier的字段。我们感兴趣的是这个策略OID。

每个证书颁发机构都有一个或多个OID,用于将证书作为EV。每个CA都在其策略页面上记录它们。然而,获取最新列表的最佳位置可能是Chromium的源代码。

如果证书的策略与其中一个OID匹配,那么我们可以继续进行下一次检查。

根指纹

如果你查看上面链接中的Chromium Source,你会发现除了策略标识符之外,它还保留了根的SHA256指纹。

这是因为除了具有正确OID的证书外,它还必须由指纹匹配的CA颁发。在铬源中,我们看到这样的东西:

{{0x06, 0x3e, 0x4a, 0xfa, 0xc4, 0x91, 0xdf, 0xd3, 0x32, 0xf3, 0x08,
      0x9b, 0x85, 0x42, 0xe9, 0x46, 0x17, 0xd8, 0x93, 0xd7, 0xfe, 0x94,
      0x4e, 0x10, 0xa7, 0x93, 0x7e, 0xe2, 0x9d, 0x96, 0x93, 0xc0}},
    {
        // AC Camerfirma uses the last two arcs to track how the private key
        // is managed - the effective verification policy is the same.
        "1.3.6.1.4.1.17326.10.14.2.1.2", "1.3.6.1.4.1.17326.10.14.2.2.2",
    }

因此,证书必须具有"1.3.6.1.4.1.17326.10.14.2.1.2"或"1.3.6.1.4.1.1 7326.10.14.2.2.2"策略标识符,但根必须具有上述二进制文件的SHA1指纹。

这可以防止流氓CA使用它不拥有的策略ID。

吊销检查

如果浏览器无法检查证书是否被吊销,则该证书将不会被视为EV证书必须进行在线吊销检查,尽管客户端可能会缓存结果。

使用X509Chain.Build时,可以通过在调用Build之前在链上设置适当的标志来执行吊销检查。

证书透明度

这个有点难检查,但谷歌在证书透明度网站上有适当的文档。如果证书是在2015年1月1日之后颁发的,则要求证书透明。一些证书也被Chrome列入白名单,如Chromium项目页面所示。

受信任的根

这是一个相当直接的方法,但证书必须属于受信任的根。如果证书是自签名的,则不能是EV。调用X509Chain.Build()时可以再次检查。

多个信任路径

一个证书可能有多个信任路径,比如说,如果证书是由交叉签名的根颁发的。如果存在多个信任路径,则所有路径都必须有效。同样,必须对所有路径进行吊销检查。如果任何路径显示证书已吊销,则该证书无效。

不幸的是,据我所知,.NET甚至Win32都没有很好的方法来检查所有证书链,甚至无法获得多个证书链。

将所有这些结合起来,如果它们都通过了,那么该证书可以被视为EV证书。