【问题标题】:How do I programmatically find which certificate was used to sign a given certificate?如何以编程方式查找用于签署给定证书的证书?
【发布时间】:2016-06-06 16:28:27
【问题描述】:

在我的 C# 代码中,我有一个 X509Certificate2 对象,它代表一个 SSL 证书(来自本地存储或通过 SSL 成功的 HTTP 请求)。该证书是使用本地存储中可能存在的一些中间证书签署的,也可能没有,因此使用X509Chain.Build() 可能不起作用。

Firefox 证书查看器的图片(因为我还没有可用的代码):

在详细信息下,在“证书层次结构”中,我看到:

  • DigiCert 高保证 EV 根 CA
    • DigiCert SHA2 扩展验证服务器 CA
      • github.com

我的对象代表“github.com”,链中的最低行。我需要以编程方式识别中间线(“DigiCert SHA2 Extended Validation Server CA”)。

我如何知道指纹或任何可以让我识别用于签署我的证书的证书的等效项?

【问题讨论】:

  • asked a meta question about the closure,我会在那里详细回答。请允许我几分钟回复。
  • 我有answered。对延误表示歉意。
  • IssuerName 属性有什么问题?
  • @erickson 除了它不必匹配签名者证书的主题,即使它匹配也无法知道它是完全正确的证书还是只是具有相同主题的某些证书​​。
  • @sharptooth,你错了。签名检查将证明这一点。

标签: c# .net ssl x509certificate x509certificate2


【解决方案1】:

在这种特定情况下 (github.com),X509Chain.Build 将起作用,因为最终证书包含有关颁发者证书位置的信息(在授权信息访问扩展中)。

但有时这可能不起作用(例如,对于 Thawte 证书,因为 Thawte 不提供有关颁发者证书位置的明确信息)。并且如果证书安装在本地证书存储区,则无法自动定位颁发者。

选项 1 -- SSL 连接

但是,如果您使用 SSL 证书并且可以建立 SSL 会话,则可以通过向 ServicePointManager.ServerCertificateValidationCallback 属性添加侦听器来获取证书:https://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.servercertificatevalidationcallback.aspx

RemoteCertificateValidationCallbackdelegate 包含几个参数,其中一个是chain,其中包含服务器返回的SSL证书链。如果远程服务器包含颁发者证书,它将在ChainElements 集合中显示。这个对象通常包含几个元素:

-Leaf Certificate
    -Issuer Certificate
        -(Optional Issuer certs when available)

所以,你需要检查两件事:

  1. 如果ChainElements 至少包含两个元素(例如,叶证书和提议的颁发者)。
  2. 如果ChainElements 集合的第一个元素在ChainelementStatus 集合中没有NotSignatureValid 状态。

您可以在RemoteCertificateValidationCallback 委托中添加以下代码来执行这些检查:

X509Certificate2 issuer = null;
if (
    chain.ChainElements.Count > 1 &&
    !chain.ChainElements[0].ChainElementStatus.Any(x => x.Status == X509ChainStatusFlags.NotSignatureValid)) {
    issuer = chain.ChainElements[1].Certificate;
}

如果在运行这段代码后issuer 变量是null,那么您将无法自动确定谁是您的证书的颁发者。这个过程需要一些额外的研究。并且不是null,那么issuer变量将持有实际的颁发者证书。

选项 2 -- 搜索本地证书存储

好的,根据您的 cmets,您要确定颁发者证书是否安装在本地证书存储中。通过阅读您的问题,我没有得到它。为什么我们要猜测你实际上在看什么?最终,我仍然不确定您是否知道/理解您想要实现的目标。

如果要查找本地商店是否安装了issuer,可以使用以下算法:

1) 使用X509Certificate2Collection.Find 方法,通过主题名称查找候选证书

2) 在候选列表(在步骤 1 中检索)中查找主题密钥标识符值与主题中证书的授权密钥标识符值相同的那些。

X509Certificate2Collection certs = new X509Certificate2Collection();
// grab candidates from CA and Root stores
foreach (var storeName in new[] { StoreName.CertificateAuthority, StoreName.Root }) {
    X509Store store = new X509Store(storeName, StoreLocation.CurrentUser);
    store.Open(OpenFlags.ReadOnly);
    certs.AddRange(store.Certificates);
    store.Close();
}
certs = certs.Find(X509FindType.FindBySubjectDistinguishedName, cert.Issuer, false);
if (certs.Count == 0) {
    Console.WriteLine("Issuer is not installed in the local certificate store.");
    return;
}
var aki = cert.Extensions["2.5.29.35"];
if (aki == null) {
    Console.WriteLine("Issuer candidates: ");
    foreach (var candidate in certs) {
        Console.WriteLine(candidate.Thumbprint);
    }
    return;
}
var match = Regex.Match(aki.Format(false), "KeyID=(.+)", RegexOptions.IgnoreCase);
if (match.Success) {
    var keyid = match.Groups[1].Value.Replace(" ", null).ToUpper();
    Console.WriteLine("Issuer candidates: ");
    foreach (var candidate in certs.Find(X509FindType.FindBySubjectKeyIdentifier, keyid, false)) {
        Console.WriteLine(candidate.Thumbprint);
    }
} else {
    // if KeyID is not presented in the AKI extension, attempt to get serial number from AKI:
    match = Regex.Match(aki.Format(false), "Certificate SerialNumber=(.+)", RegexOptions.IgnoreCase);
    var serial = match.Groups[1].Value.Replace(" ", null);
    Console.WriteLine("Issuer candidates: ");
    foreach (var candidate in certs.Find(X509FindType.FindBySerialNumber, serial, false)) {
        Console.WriteLine(candidate.Thumbprint);
    }
}

假设cert 变量将证书存储在主题中(搜索颁发者)。这种方法存在问题,因为它不验证签名并且可能返回误报。

【讨论】:

    【解决方案2】:

    我问,“IssuerName 属性有什么问题?”

    回复是,“除了它不必与签名者证书的主题相匹配,即使它匹配,也无法知道它是完全正确的证书还是只是具有相同主题的某些证书​​。”

    这是不正确的。

    PKIX § 6.1 说,“对于 {1, ..., n-1} 中的所有 x,证书 x 的主题是证书 x+1 的颁发者。”

    子部分通过指示“将证书主体名称分配给 working_issuer_name”来阐明这一点,然后,对于链中的下一个证书,验证“……证书颁发者名称是 working_issuer_name”。

    您可能会感到困惑,因为您可能有许多同名但密钥不同的颁发者证书。在这种情况下,可以通过将颁发者的subject key identifier 与主题的授权密钥标识符进行匹配来识别正确的签名密钥。然而,匹配键标识符是不够的:名称必须首先匹配。

    不清楚您为什么要手动执行此操作。您应该可以将provide all available intermediates 发送到您的path-building library, 并让您找到有效的链(如果存在)。

    【讨论】:

    • 如果您拥有正确的证书,ExtraStore 属性会很有帮助,但情况并非总是如此。正如我在 Thawte 中提到的,客户端不需要中间的 Thawte CA 证书即可在 Web 浏览器中成功运行,因为此证书由 Web 服务器交付并在证书验证后处理。
    • @CryptoGuy 我在您的评论中看不到任何相关性。如果服务器向您发送中间件,您就有了,可以将其放入ExtraStore。它是否是“正确”的证书是你通过验证来测试的。你能更清楚地表达你的观点吗?
    • 我通过添加一些代码来编辑我的回复并澄清我的观点。希望这说明清楚。
    • 看起来很有希望。我没有使用路径构建库,因为在构建信任链时,中间证书可能来自商店或权威机构(通过 AIA 或其他方式),而我想要做的是在本地商店。
    • @sharptooth "来自本地商店或 [...] 通过 SSL 的请求":您尝试做的不一定有意义(或至少你需要小心你正在尝试做的事情)。与其他人一起验证本地商店中的证书需要进一步规范。特别是,您不仅需要匹配发行者和签名来构建您的链,还需要匹配日期/时间有效性和目的属性。原则上,您可能有多个匹配的候选人。路径构建库往往会在 SSL 连接的上下文中为您完成所有这些工作。
    猜你喜欢
    • 1970-01-01
    • 2015-10-13
    • 1970-01-01
    • 2016-05-20
    • 1970-01-01
    • 1970-01-01
    • 2015-08-22
    • 2012-10-09
    • 2015-04-29
    相关资源
    最近更新 更多