Как программно импортировать pfx с цепочкой сертификатов в хранилище сертификатов?

Я пытаюсь программно импортировать сертификат X509 (pfx/PKCS # 12) в хранилище сертификатов локального компьютера. Этот конкретный сертификат имеет цепочку сертификатов, путь сертификации выглядит примерно так:

  • Корневой сертификат CA
    • Сертификат организации CA
      • Сертификат организации 2 CA
        • Мой сертификат

Используемый мной код выглядит так:

cert = new X509Certificate2(pathToCert, password);

if (cert != null)
{
    var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.ReadWrite);
    if (!store.Certificates.Contains(cert))
    {
        store.Add(cert);
    }
}

Этот код импортирует сертификат, но, похоже, он игнорирует цепочку. Если я проверяю сертификат в магазине, путь сертификации показывает только:

  • Мой сертификат

Однако, когда я импортирую pfx вручную, он показывает полный путь. Могу ли я пропустить шаг здесь, или мне не хватает какого-то параметра? Может кто-то пролить свет на это?

Ответы

Ответ 1

Вы должны иметь возможность перебирать сертификаты в своем PFX (и импортировать их в хранилище сертификатов по вашему выбору), открыв файл PFX в качестве объекта X509Certificate2Collection.

Вот документы на X509Certificate2Collection:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2collection.aspx

MSDN предоставляет примерный код на этой странице документов о том, как проверять каждый сертификат в коллекции.

Как только вы узнаете CN/Эмитенты/другую информацию о каждом сертификате, должно быть ясно, в каком хранилище сертификатов каждый должен быть добавлен. Для этого вы можете использовать класс X509Store и перечисление StoreName, чтобы указать, в каком хранилище вы хотите открыть/добавить:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509store.aspx

http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.storename.aspx

Также см. мой ответ на аналогичный вопрос SO:

Как получить сертификаты из файла pfx с помощью С#?

Как упоминалось в одном из последних комментариев по этому вопросу, когда вы пытаетесь импортировать сертификат в текущее хранилище Root ( "StoreName.Root" и "StoreLocation.CurrentUser" в качестве имени/местоположения), вы получите всплывающее диалоговое окно с просьбой подтвердить.

Чтобы решить эту проблему, я просто добавил небольшой код MS UI Automation в свой метод импорта сертификатов, чтобы щелкнуть OK в приглашении.

Или, как комментирует "CodeWarrior" в другом комментарии ответа SO, чтобы избежать всплывающего диалога, вы можете попробовать помещать корневой сертификат в хранилище LocalMachine вместо CurrentUser.

Пример кода:

string certPath = <YOUR PFX FILE PATH>;
string certPass = <YOUR PASSWORD>;

// Create a collection object and populate it using the PFX file
X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import(certPath, certPass, X509KeyStorageFlags.PersistKeySet);

foreach (X509Certificate2 cert in collection)
{
    Console.WriteLine("Subject is: '{0}'", cert.Subject);
    Console.WriteLine("Issuer is:  '{0}'", cert.Issuer);

    // Import the certificate into an X509Store object
}

Ответ 2

Для дальнейшего использования я обнаружил другой способ сделать это, используя объект X509Chain:

var cert = new X509Certificate2(pathToCert, password);

X509Chain chain = new X509Chain();
chain.Build(cert);
for (int i = 0; i < chain.ChainElements.Count; i++)
{
   //add to the appropriate store
}

Ответ 3

Сертификат X.509 содержит только цепочку, которая связывает ее с корневым сертификатом (включая промежуточные полномочия), но эти сертификаты не содержатся в сертификате. Эта цепочка используется, когда сертифицирован конечный сертификат (который не является самоподписанным) - он должен привести к доверенному корневому сертификату. Более точно, открытый ключ каждого ЦС используется для декодирования и проверки хеша для выданного сертификата. Этот процесс повторяется до тех пор, пока не будет достигнут корневой сертификат. После проверки всей цепочки, если корневому сертификату доверяют, сертификату конца также доверяют. Конечно, процесс включает и другие проверки (например, дата начала, дата окончания, список отзыва сертификатов, например), но я детализировал только часть, связанную с использованием цепочки.

Итак, вы правильно импортировали "Мой сертификат" вместе с цепочкой в ​​ "Корневой сертификат CA" - эта цепочка закодирована в "Мой сертификат", и вы можете подтвердить это, просмотрев ее свойства, но эта цепочка является только ссылкой и он не содержит сертификатов "Сертификат CA корневого сертификата", "Сертификат CA центра сертификации" и "Сертификат CA сертификатов организации 2".

Я надеюсь, что это решит вашу проблему, но если это не так, не могли бы вы более конкретно узнать, что вы пытаетесь выполнить?

Ответ 4

Для тех, кто хочет создать "подходящее хранилище" общее решение

Это то, что я создал с помощью VB, поэтому не должно быть трудно переносить на С#. Я использовал приведенные выше сообщения, чтобы начать меня, и я - полный NooB.

    Dim certPath = "C:\Users\08353153\Documents\Visual Studio 2015\Projects\WindowsApplication2\WindowsApplication2\bin\Debug\8870-thebigchess.pfx"
    Dim certPass = "eduSTAR.NET"
    Dim Collection As New X509Certificate2Collection
    Collection.Import(certPath, certPass, X509KeyStorageFlags.PersistKeySet)

    Dim certOne As X509Certificate2 = Collection(0)
    Dim certTwo As X509Certificate2 = Collection(2)
    Dim certThree As X509Certificate2 = Collection(1)

    Dim personal As New X509Store(StoreName.My, StoreLocation.LocalMachine)
    personal.Open(OpenFlags.ReadWrite)
    personal.Add(certOne)
    personal.Close()

    Dim trust As New X509Store(StoreName.Root, StoreLocation.LocalMachine)
    trust.Open(OpenFlags.ReadWrite)
    trust.Add(certTwo)
    trust.Close()

    Dim intermed As New X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine)
    intermed.Open(OpenFlags.ReadWrite)
    intermed.Add(certThree)
    intermed.Close()