Ответ 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
RemoteCertificateValidationCallback Делегат содержит несколько параметров, один из которых chain
, который содержит цепочку сертификатов SSL, возвращаемую сервером. И если удаленный сервер содержит сертификат эмитента, он будет представлен там в коллекции ChainElements
. Этот объект обычно содержит несколько элементов:
-Leaf Certificate
-Issuer Certificate
-(Optional Issuer certs when available)
Итак, вам нужно проверить две вещи:
- Если
ChainElements
содержит как минимум два элемента (например, сертификат листа и предлагаемый эмитент). - Если первый элемент коллекции
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;
}
Если после выполнения этой части кода переменная 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);
}
}
предполагая, что переменная cert
хранит сертификат в субъекте (для которого выполняется поиск эмитента). Этот подход имеет проблемы, поскольку он не подтверждает подпись и может возвращать ложные срабатывания.