在这种特定情况下 (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)
所以,你需要检查两件事:
- 如果
ChainElements 至少包含两个元素(例如,叶证书和提议的颁发者)。
- 如果
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 变量将证书存储在主题中(搜索颁发者)。这种方法存在问题,因为它不验证签名并且可能返回误报。