Как программно найти, какой сертификат использовался для подписи данного сертификата?

В моем коде С# у меня есть объект X509Certificate2, который представляет сертификат SSL (из локального хранилища или из успешного HTTP-запроса через SSL). Сертификат подписан с некоторым промежуточным сертификатом, который, возможно, присутствует в локальном хранилище, возможно, нет, поэтому использование X509Chain.Build(), вероятно, не будет работать.

Рисунок просмотра просмотра сертификатов Firefox (потому что у меня еще нет полезного кода):

введите описание изображения здесь

В разделе "Подробности" в "Иерархии сертификатов" я вижу следующее:

  • DigiCert High Assurance EV Root CA
    • DigiCert SHA2 Extended Validation Server CA
      • github.com

Мой объект представляет "github.com", самую низкую строку в цепочке. Мне нужно программно идентифицировать среднюю строку ( "DigiCert SHA2 Extended CA CA CA Validated Server" ).

Как узнать отпечаток или что-нибудь эквивалентное, которое позволит мне определить, какой сертификат был использован для подписания моего сертификата?

Ответы

Ответ 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 хранит сертификат в субъекте (для которого выполняется поиск эмитента). Этот подход имеет проблемы, поскольку он не подтверждает подпись и может возвращать ложные срабатывания.

Ответ 2

Я спросил: "Что не так с собственностью?"

Ответ был: "Ничто, кроме него, не должно совпадать с темой сертификата подписчика и даже если оно не соответствует ему, не знает, действительно ли это правильный сертификат или только какой-то сертификат с тем же предметом".

Это неверно.

PKIX & sect; 6.1 говорит: "для всех x в {1,..., n-1} субъект сертификата x является эмитентом сертификата x + 1."

Подразделы уточняют это, называя "Присвоить имя субъекту сертификата рабочему_исследованию", а затем для следующего сертификата в цепочке, проверяя, что имя " & hellip; имя сертификата - имя_заменителя_производства.

Вы можете быть смущены, потому что у вас может быть много сертификатов эмитента с тем же именем, но с разными ключами. В этом случае правильный ключ подписи можно идентифицировать, сопоставляя идентификатор идентификатор ключевого слова с идентификатором ключа субъекта. Однако сопоставление ключевых идентификаторов недостаточно: имена должны соответствовать первым.

Непонятно, почему вы пытаетесь сделать это вручную. Вы должны предоставить все доступные промежуточные продукты вашей библиотеке создания путей и пусть найдет действительную цепочку, если таковая существует.