如何检查X509证书是否已启用“扩展validation”?

如果X509Certificate(或X509Certificate2)设置了“扩展validation”(EV)标志,我很难找到一种可靠的方法来检查我的C#(。Net 4.0)应用程序。 有谁知道最好的方法?

您可以检查X509Certificate包含其中一个OId 。 此外,您可以查看Chromium的Source以获取已实施的OId列表。 你可以在这里找到来源。 如果您想坚持使用Firefox,可以在此处获取实现。

我现在更新了我的源代码并进行了测试。 我写了一个小方法来validation来自Wikipedia / Chromium的OId-List的X509Certificate2 。 在这种方法中,我使用的是维基百科列表,但最好采用Chromium-List。


OId是如何得救的?

每个CA都有一个或多个ObjectIds OId 。 它们不会像您猜测的那样保存为扩展名,它们将保存为Policy Extensions中的条目。 要获得确切的扩展,建议使用Policy Extension本身的Oid,而不是使用友好名称。 政策扩展的OId是2.5.29.32

提取信息

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

资源

  ///  /// Checks if a X509Certificate2 contains Oids for EV ///  ///  ///  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可以执行此操作。 这样做的具体细节我将遗漏,但有大量的信息来展示如何调用此function。 您需要将lpszStructType参数设置为值(IntPtr)16来调用它。 这将返回一个CERT_POLICIES_INFO结构,该结构具有指向CERT_POLICY_INFO结构数组的计数和指针。 这个结构上有一个名为pszPolicyIdentifier的字段。 这是我们感兴趣的政策OID。

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

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

根指纹

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

这是因为除了具有适当OID的证书之外,它必须由指纹匹配的CA颁发。 在Chromium源代码中,我们看到如下内容:

 {{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.17326.10.14.2.2.2”策略标识符,但根必须具有上面看到的二进制文件的SHA1指纹。

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

撤销检查

如果浏览器无法检查证书是否被撤销,则不会将其视为EV证书。 必须完成在线吊销检查,但客户端可以缓存结果。

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

证书透明度

这个有点难以检查,但Google在Certificate Transparency网站上有相应的文档。 如果证书是在2015年1月1日之后颁发的,则需要证书透明度。 如Chromium Project Page所示,某些证书也会被Chrome列入白名单。

受信任的根

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

多个信任路径

证书可能具有多个信任路径,例如证书是否由交叉签名的根颁发。 如果存在多个信任路径,则所有路径必须有效。 同样,必须对所有路径进行撤销检查。 如果任何路径将证书显示为已撤销,则证书无效。

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

结合所有这些,如果它们全部通过,那么证书可以被认为是EV证书。