如何以编程方式查找用于签署给定证书的证书?

sha*_*oth 16 .net c# ssl x509certificate2 x509certificate

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

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

在此输入图像描述

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

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

我的对象代表"github.com",链中的最低线.我需要以编程方式识别中间行("DigiCert SHA2扩展验证服务器CA").

我如何知道指纹或任何等同物,以便我识别用于签署证书的证书?

Cry*_*t32 11

在这种特定情况下(github.com),X509Chain.Build将起作用,因为结束证书包含有关颁发者证书位置的信息(在Authority Information Access扩展中).

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

选项1 - SSL连接

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

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

-Leaf Certificate
    -Issuer Certificate
        -(Optional Issuer certs when available)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

好的,根据您的意见,您想确定颁发者证书是否安装在本地证书存储中.通过阅读你的问题,我没有得到它.我们为什么要猜猜你到底在想什么?最后,我仍然不确定你是否了解/了解你想要实现的目标.

如果要查找是否在本地存储中安装了颁发者,可以使用以下算法:

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

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