Contents of this directory is archived and no longer updated.

Posts on this page:

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

Delete the Private key if the export is successful

К сожалению, метод Export() у объектов X509Certificate2 не позволяет штатно проделывать данную операцию. Это можно сделать через CAPICOM.PrivateKey.Delete(), но данная возможность отсутствует в PowerShell, в то время, как вы можете это сделать в VBS. Это связано с весьма паршивой поддержкой COM со стороны PowerShell, поэтому для реализации функционала этой галочки вам придётся проделать следующие шаги:

  1. Получить объект X509Certificate2 самого сертификата;
  2. Экспортировать его в PFX;
  3. Открыть хранилище в режиме ReadWrite;
  4. Удалить данный сертификат из хранилища;
  5. Экспортировать объект сертификата уже не в PFX, а в Cert;
  6. Импортировать этот экспортированный объект Cert (хотя, на самом деле там будет массив байтов, но это не столь существенно).

На языке PowerShell это выглядеть будет примерно так:

# получаем объект сертификата с закрытым ключом
$cert = (dir cert:\currentuser\my)[0]
# записываем пароль для PFX файла
$pass = Read-Host "Password" -AsSecureString
# экспортируем его в PFX формат
$bytes = $cert.Export("pfx", $pass)
# и записывем его в файл
[System.IO.File]::WriteAllBytes('mycert.pfx', $bytes)
# снова экспортируем данный объект, но уже в Cert формат
$tempcert = $cert.Export("Cert")
# создаём объект нашего хранилища
$store = New-Object system.security.cryptography.X509Certificates.X509Store "my", "CurrentUser"
# открываем его на чтение и на запись
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
# удаляем текущий сертификат с закрытым ключом из хранилища
$store.Remove($cert)
# записываем обратно Cert объект в хранилище, теперь уже без закрытого ключа
$store.Add($tempcert)
$store.Close()

Это только образец кода и его не стоит использовать прямо в таком виде, т.к. вы должны точно убедиться, что у вас PFX файл создался и он валидный. В противном случае вы останетесь совсем без закрытого ключа. И ещё раз напоминаю, при работе с хранилищем – не забывайте его закрывать после работы.

В предыдущих статьях мы с вами уже изучили методы экспорта сертификатов из хранилища Certificate Store в файл:

а так же рассмотрели сопутствующие вопросы. Теперь же предлагаю рассмотреть обратную операцию – добавление сертификатов в cert store из файлов. Этот процесс будет несколько легче, чем экспорт, поэтому я в одном посте покажу импорт всех типов сертификатов (они все используют одну схему).

Для импорта нам потребуется метод x509Certificate.Import(), который так же как и метод Export() имеет несколько конструкторов. Мы можем выбрать один универсальный, который подойдёт нам для всех типов файлов сертификатов и это будет конструктор: X509Certificate2.Import Method (String, SecureString, X509KeyStorageFlags). В качестве аргументов он принимает следующее:

  • String – путь к файлу сертификата
  • SecureString – строка с паролем. Требуется только для импорта PFX сертификатов, т.к. остальные типы файлов не содержат закрытых ключей и защищать их паролем нет смысла. Если это не PFX, то значение этого аргумента просто выставляется в $null.
  • объект типа X509KeyStorageFlags, который описывает область хранения сертификатов.

Давайте немного разберёмся с флагами последнего аргумента:

  • DefaultKeySet – указывает, что сертификат предназначен для импорта в контейнер Personal хранилища CurrentUser (дефолт).
  • Exportable – используется только для PFX файлов и говорит, будет ли возможность экспортировать сертификат с закрытым ключом. Если мы этот флаг не указываем, то после импорта не будет возможности снова экспортировать в PFX файл.
  • MachineKeySet – указывает, что сертификат будет установлен в хранилище компьютера (LocalSystem).
  • PersistKeySet – используется только для PFX и сохраняет пароль от PFX файла в сертификате. Данный пароль будет использован при дальнейшем экспорте в PFX файл (будет работать только с флагом Exportable).
  • UserKeySet – указывает, что сертификат будет установлен в пользовательское хранилище.
  • UserProtected – используется только для PFX файлов и для пользовательских сертификатов. Этот флаг включает Private Key Strong Protection, который при каждом использовании закрытого ключа будет требовать ввода пароля.

Примечание: вы никогда не должны использовать флаг UserProtected для компьютерных сертификатов, поскольку они используются в контексте LocalSystem и у вас просто не будет возможности ввести пароль для использования этого закрытого ключа.

Т.к. X509KeyStorageFlags на самом деле является перечислителем (enumerator), то создавать его объект не обязательно и флаги можно просто использовать как строки. PowerShell уже сам подобъёт эти флаги к нужному типу данных.

т.к. у нас ещё нет готового объекта сертификатов, то мы его просто создаём:

$certs = New-Object system.security.cryptography.x509certificates.x509certificate2

И в этом объекте у нас будет метод Import(), которым мы уже воспользуемся:

$path = "mycert.pfx"
$password = Read-Host "Type password for PFX certificate" -AsSecureString
$flags = "UserKeySet, Exportable"
$certs.Import($path, $password, $flags)

Обратите внимание, что вы за один раз можете указать несколько флагов для KeyStorage, просто перечислив их через запятую. Теперь $certs у нас содержит x509Certificate2 объект нашего сертификата:

[↓] [vPodans] $certs = New-Object system.security.cryptography.x509certificates.x509certificate2
[↓] [vPodans] $path = "mycert.pfx"
[↓] [vPodans] $password = Read-Host "Type password for PFX certificate" -AsSecureString
Type password for PFX certificate: *
[↓] [vPodans] $flags = "UserKeySet, Exportable"
[↓] [vPodans] $certs.Import($path, $password, $flags)
[↓] [vPodans] $certs

Thumbprint                                Subject
----------                                -------
0F5157A8342C66493E7B1354530DD7A9F980BC69  CN=vPodans


[↓] [vPodans]

как видите, у нас всё случилось, PFX файл был успешно прочитан и помещён в объект x509Certificate2. Но это только половина дела, поскольку этот сертификат пока существует только самом в объекте, но не в хранилище. Для того, чтобы записать объект в хранилище мы должны поработать с хранилищем. За него отвечает класс X509Store. Мы создадим этот объект с нуля и укажем какой именно контейнер в каком хранилище открывать. Для этого мы воспользуемся следующим конструктором: X509Store Constructor (StoreName, StoreLocation). Здесь я подробно останавливаться не буду, поскольку названия хранилищ и контейнеров мы уже рассматривали в одном из предыдущих постов: Простой экспорт сертификатов в PowerShell, в этом посте все контейнеры показаны в первом снимке консоли. В нашем случае StoreName будет My (Personal), а Store Location будет CurrentUser (контекст текущего пользователя):

$store = New-Object system.security.cryptography.X509Certificates.X509Store "My", "CurrentUser"

Создали мы объект хранилища, теперь нам надо его открыть в режиме ReadWrite:

$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)

Этот флаг открытия хранилища указывать надо обязательно, иначе у вас просто не будет к нему доступа. Теперь когда хранилище открыто, можно методом Add(), который в качестве аргумента принимает только x509Certificate2 объекты (он у нас уже есть в переменной $certs):

$store.Add($certs)
$store.Close()

Примечание: MSDN настоятельно рекомендует закрывать хранилище после того, как вы провели с ним необходимые операции.

Вот и всё, собственно, у нас теперь PFX файл был успешно импортирован в хранилище.

Когда вы импортируете сертификаты из p7b (PKCS#7) файла, то в нём может быть несколько сертификатов, поэтому здесь поменяется всего 2 строчки. Поскольку у нас в одном файле может быть несколько сертификатов, то для их чтения из файла будем использовать не x509Certificate2 объект, а специальный массив таких объектов – x509Certificate2Collection, о котором мы уже говорили в предыдущем посте:

$bytes = [System.IO.File]::ReadAllBytes($path)
$certs = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection$certs.Import($bytes)

Мы сначала прочитали байтовый массив из файла и засунули его в коллекцию x509Certificate2 объектов. Т.к. сертификатов там несколько, а метод Add() у x509Store за раз может взять только один объект – воспользуемся циклом:

$certs | %{$store.Add($_)}

А в остальном схема импорта p7b файлов будет такая же. Вот и всё, что я хотел рассказать по импорту сертификатов из файлов в хранилище. В принципе, используя эти знания вы уже можете затачивать код под свои нужды, тем более я везде, где это было необходимо, указывал ссылки на MSDN, где вы можете узнать более подробно этот материал. Но чтобы сделать жизнь чуточку проще, я постараюсь написать несколько полезных функций для управлениями сертификатами в PowerShell, что будет весьма актуально для Windows Server 2008 R2 Server Core.

Спасибо за внимание :-)

Продолжая тему экспорта сертификатов поговорим о более сложных вещах, чем простой экспорт сертификата из хранилища в CER/DER файл. На сей раз расскажу про экспорт сертификатов из хранилища в PFX (Personal Information Exchange Syntax Standard) и PKCS#7 (Cryptographic Message Syntax Standard) формат. Будет немного треша, но в пределах разумного.

Для экспорта сертификата в PFX нам потребуется выполнить половину кода из предыдущей статьи: Простой экспорт сертификатов в PowerShell.

Сначала выберем объект сертификат из хранилища:

$cert = (dir cert:\currentuser)[4]

Как вы помните, если экспортировать сертификат в PFX нам нужно указывать пароль:

Certificate Export Wizard windows

Следовательно, конструктор метода Export(X509ContentType), который мы использовали нам уже не подойдёт, поскольку в этот конструктор нельзя вложить пароль. Мы можем воспользоваться одним из оставшихся конструкторов:

SecureString/String представляют собой аргумент для пароля. Я бы не советовал использовать простую строку для хранения пароля, а SecureString. Для этого мы сделаем такую строчку:

$password = Read-Host "Password" –AsSecureString

А теперь по аналогии с примером из предыдущего поста укажем тип сертификата как PFX и применим наш метод Export(X509ContentType, SecureString):

$type = [System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx
$bytes = $cert.Export($type, $password)

У нас снова получится массив байтов, т.к. этот метод возвращает только его. Но что с этим бинарным мусором делать? Я сначала по наивности предположил, что его так же запаковать в Base64 и записать в файл. Да, у меня всё вышло хорошо, данные были записаны в файл и Certificate Import Wizard принял его за валидный PFX файл. Но у меня всё обломалось на стадии ввода пароля, т.к. этот визард не хотел его брать. Ввиду отсутствия практики в этих делах, пришлось попросить помощи на одном из бложиков технета, откуда и пришёл ответ, что этот Certificate Import Wizard не декодирует файл обратно из Base64, поэтому для записи в файл нужно писать сразу байты:

[System.IO.File]::WriteAllBytes('certificate.pfx', $bytes)

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

Но это всё относительно просто. Когда я задался вопросом “а как экспортировать в PKCS#7?”, то пришлось убить несколько часов на выяснение этого процесса. PKCS#7 удобен тем, что в себе может содержать кучу сертификатов и главным образом используется для хранения цепочки сертификатов (Certificate Chain). Например, особенно наблюдательные пользователи при посещении многих HTTPS сайтов могут заметить, что поле AIA есть только у самих SSL сертификатов, а у промежуточных CA его уже нету. Но этих сертификатов нету и в хранилище сертификатов (Certificate Store). Напрашивается вопрос: а как же система построила эту цепочку сертификатов при отсутствующем AIA в сертификатах? Оказывается, всё очень просто, в RFC2246 есть хороший момент:

certificate_list
       This is a sequence (chain) of X.509v3 certificates. The sender's
       certificate must come first in the list. Each following
       certificate must directly certify the one preceding it. Because
       certificate validation requires that root keys be distributed
       independently, the self-signed certificate which specifies the
       root certificate authority may optionally be omitted from the
       chain, under the assumption that the remote end must already
       possess it in order to validate it in any case.

Это означает, что веб-сервер может вместе с SSL сертификатом отправлять клиенту и цепочку сертификатов, которая как раз и есть в формате PKCS#7. Если что, это было просто введение к назначению данного типа сертификатов.

Предупреждаю сразу, что методы x509Certificate2.Export Method нам не сгодятся, т.к. в описании метода есть ремарка:

The contentType parameter accepts only the following values of the X509ContentType enumeration: Cert, SerializedCert, and Pkcs12. Passing any other value causes a CryptographicException to be thrown.

Хоть класс System.Security.Cryptography.X509Certificates.X509ContentType и имеет в себе атрибут PKCS7, но метод Export класса x509Certificate2 его просто не принимает. Это был провал! Но делать нечего, пришлось искать. Наткнулся я вот на один замечательный пост: PKCS7 (p7b) bag of certificates and powershell. Но там пример как раз наоборот – как извлечь сертификаты из p7b файла и вывести их в виде x509Certificate2 объектов:

[reflection.assembly]::LoadWithPartialName("System.Security")
$data = [System.IO.File]::ReadAllBytes("certificates.p7b")
$cms = new-object system.security.cryptography.pkcs.signedcms
$cms.Decode($data)
$cms.Certificates | foreach {New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $_} | echo

Автор поста вообще удивительный человек. Просто взял и выложил код без каких-либо комментариев, предполагая, что все знают что делает этот код в деталях. Попробую за него сделать это я. Данный код читает бинарный мусор из файла (обратите внимание, что используется обратная операция экспорта PFX бинарного массива в файл). Далее создаётся объект CMS (Cryptographic Message Syntax). И что мы с него имеем:

[↓] [vPodans] [void][reflection.assembly]::LoadWithPartialName("System.Security")
[↓] [vPodans] $cms = new-object system.security.cryptography.pkcs.signedcms
[↓] [vPodans] $cms


Version      : 0
ContentInfo  : System.Security.Cryptography.Pkcs.ContentInfo
Detached     : False
Certificates : {}
SignerInfos  : {}



[↓] [vPodans] $cms | gm -MemberType methods


   TypeName: System.Security.Cryptography.Pkcs.SignedCms

Name             MemberType Definition
----             ---------- ----------
CheckHash        Method     System.Void CheckHash()
CheckSignature   Method     System.Void CheckSignature(bool verifySignatureOnly), System.Void CheckSignature(System....
ComputeSignature Method     System.Void ComputeSignature(), System.Void ComputeSignature(System.Security.Cryptograph...
Decode           Method     System.Void Decode(byte[] encodedMessage)
Encode           Method     byte[] Encode()
Equals           Method     bool Equals(System.Object obj)
GetHashCode      Method     int GetHashCode()
GetType          Method     type GetType()
RemoveSignature  Method     System.Void RemoveSignature(int index), System.Void RemoveSignature(System.Security.Cryp...
ToString         Method     string ToString()


[↓] [vPodans]

Мы имеем объект с 5 свойствами и различными методами. В коде используется метод Decode() и как видно в описании метода, в качестве аргумента принимается массив байтов, полученный из p7b файла. Этот метод преобразует этот массив обратно в массив объектов x509Certificate2, что видно из последней строки кода. Более-менее тут что-то понятно. Следовательно, для экспорта нам нужно проделать операцию наоборот, создать объект CMS, в свойство Certificates запихать x509Certificate2 объекты сертификатов и применить метод Encode(). Чтобы использовать этот метод у нас в свойстве Certificates должны храниться x509Certificate2 объекты наших сертификатов, которые хотим экспортировать. Но тут меня ждал облом, т.к. свойство Certificates является Read-only и записать туда ничего нельзя. Однако, PowerTab мне показал, что там есть что-то интересное:

[↓] [vPodans] $cms.Certificates.
                                ╔═ $cms.Certificates. ═════════════╗
                                ║ $cms.Certificates.PSBase         ║
                                ║ $cms.Certificates.Add(           ║
                                ║ $cms.Certificates.AddRange(      ║
                                ║ $cms.Certificates.Clear(         ║
                                ║ $cms.Certificates.Contains(      ║
                                ║ $cms.Certificates.CopyTo(        ║
                                ║ $cms.Certificates.Equals(        ║
                                ║ $cms.Certificates.Export(        ║
                                ║ $cms.Certificates.Find(          ║
                                ║ $cms.Certificates.GetEnumerator( ║
                                ║ $cms.Certificates.GetHashCode(   ║
                                ║ $cms.Certificates.GetType(       ║
                                ║ $cms.Certificates.Import(        ║
                                ║ $cms.Certificates.IndexOf(       ║
                                ║ $cms.Certificates.Insert(        ║
                                ║ $cms.Certificates.Remove(        ║
                                ║ $cms.Certificates.RemoveAt(      ║
                                ║ $cms.Certificates.RemoveRange(   ║
                                ║ $cms.Certificates.ToString(      ║
                                ║ $cms.Certificates.Item(          ║
                                ║ $cms.Certificates.Capacity       ║
                                ║ $cms.Certificates.Count          ║
                                ╚═[1] 1-22 (22/22)]════════════════╝

Я испробовал несколько по смыслу похожих методов, как Add, AddRange и Import, но всё неудачно. Сертификаты никак не хотели туда помещаться. После некоторого времени возни я бросил это занятие, как бесполезное, т.к. там ловить нечего:

[↓] [vPodans] $cms.Certificates | gm
Get-Member : No object has been specified to the get-member cmdlet.
At line:1 char:23
+ $cms.Certificates | gm <<<<
    + CategoryInfo          : CloseError: (:) [Get-Member], InvalidOperationException
    + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand

[↓] [vPodans]

Вобщем, всякие вариации с этим CMS объектов никаких успехов не приносили до тех пор, пока я не посмотрел тип этого свойства Certificates:

[↓] [vPodans] $cms.Certificates.GetType().FullName
System.Security.Cryptography.X509Certificates.X509Certificate2Collection

Я отправился на MSDN читать макулатуру по этому классу (точнее по его членам): X509Certificate2Collection Members. Если вы ещё не забыли, то мы ищем способ разобрать наш x509Certificate2 объект на массив байтов, который бы соответствовал формату PKCS#7. Здесь из годных методов я нашёл метод Add(X509Certificate2). У меня уже есть этот объект, поэтому мы его можем добавить в объект X509Certificate2Collection. Данный класс по сути представляет массив объектов x509Certificate2. Давайте сделаем:

[↓] [vPodans] $cert = (dir cert:\currentuser\my)[4]
[↓] [vPodans] $cert1 = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
[↓] [vPodans] $cert1.Add($cert)
0
[↓] [vPodans] $cert1

Thumbprint                                Subject
----------                                -------
0F5157A8342C66493E7B1354530DD7A9F980BC69  CN=vPodans


[↓] [vPodans]

ну хорошо, преобразовали мы x509Certificate2 в X509Certificate2Collection, а что дальше? Нам по прежнему нужен этот дурацкий массив байтов. После этого я посмотрел на метод X509Certificate2Collection Export Method (X509ContentType) нового класса и прочитал ремарки:

This method supports content types that do not require a password.

Вот тут я и сказал “слава сиськамПротоколу!”. Данный метод позволяет экспортировать объекты сертификатов в любой тип, который не требует пароля (т.е. всё, кроме PFX/PKCS#12). А дальше уже по отработанной схеме:

[↓] [vPodans] $type = [System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs7
[↓] [vPodans] $bytes = $cert1.Export($type)
[↓] [vPodans] [System.IO.File]::WriteAllBytes('certificate.p7b', $bytes)
[↓] [vPodans]

Вот таким долгим и тяжёлым (для меня, во всяком случае) я смог его победить и научиться экспортировать сертификаты в PKCS#7.

Что нас ждёт дальше? А дальше, возможно, поговорим про SerializedCert и SerializedStore (а может и не будем говорить) и уже импорт сертификатов из файлов в Certificate Store. А на сегодня, пожалуй, хватит.

Update 03.12.2011: исправлен код записи base64 строки в файл.


С различной периодичностью на ньюсгруппах и англоязычных форумах встречаю вопрос, как экспортировать сертификаты из хранилища (Certificate Store) или сертификаты цифровых подписей файлов в .CER или .DER файл. Сегодня я продемонстрирую простой метод для экспорта этих сертификатов.

На самом деле разница между экспортом сертификата из хранилища сертификатов и подписанного файла заключается только в методе извлечения самого сертификата в x509Certificate2 объект. Давайте начнём с самого простого – экспорт сертификата из хранилища. В PowerShell уже есть свой провайдер для этого хранилища и свой PSDrive:

[↓] [vPodans] dir cert:\


Location   : CurrentUser
StoreNames : {SmartCardRoot, UserDS, AuthRoot, CA...}

Location   : LocalMachine
StoreNames : {SmartCardRoot, AuthRoot, CA, Trust...}



[↓] [vPodans] dir cert:\currentuser


Name : SmartCardRoot

Name : UserDS

Name : AuthRoot

Name : CA

Name : AddressBook

Name : Trust

Name : Disallowed

Name : My

Name : Root

Name : TrustedPeople

Name : TrustedPublisher

Name : REQUEST



[↓] [vPodans] dir cert:\currentuser\my


    Directory: Microsoft.PowerShell.Security\Certificate::currentuser\my


Thumbprint                                Subject
----------                                -------
9C5E4DCEF6598C1298894F62D4BD16E601B8C780  CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=W...
986D375362652FE9E39BA4D042A6B8BA75745998  CN=Administrator, CN=Users, DC=sysadmins, DC=lv
4BB89D732920DD91DE66983DDF2CC4EEC272A802  CN=Administrator, CN=Users, DC=sysadmins, DC=lv
14B931DB4790403CCE2A3D03B62638FC7A0D5F34  CN=Administrator, OU=Administrators, DC=sysadmins, DC=lv


[↓] [vPodans]

Если просто посмотреть содержимое Cert:\, то мы увидим там 2 хранилища

  • CurrentUser – представляет хранилище текущего пользователя, т.е. является аналогом графической консоли CertMgr.msc
  • LocalMachine – представляет хранилище локального компьютера и является аналогом графической консоли MMC – Certificates, запущенной в контексте LocalComputer.

Внутри уже видны контейнеры, которые мы видим в виде папок в MMC консоли. А дальше уже хранятся наши сертификаты. Эти сертификаты здесь представлены в виде массива x509Certificate2.

[↓] [vPodans] (dir cert:\currentuser\my)[0].gettype().FullName
System.Security.Cryptography.X509Certificates.X509Certificate2
[↓] [vPodans]

Если посмотреть свойства этого объекта через Get-Member, то мы сможем там увидеть метод Export(). Но лучше посмотреть этот метод на MSDN, т.к. он имеет несколько конструкторов: x509Certificate2.Export Method. Из них нас заинтересует только первый конструктор, который Export(X509ContentType). Остальные 2 необходимы для экспорта с паролем (только для PFX). Но, как мы видим из описания метода, нам надо ещё указать x509ContentType объект, который будет указывать нам на тип экспортируемого сертификата. Типы экспортируемых сертификатов можно посмотреть по ссылке: http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509contenttype.aspx или в консоли PowerShell:

[↓] [vPodans] [System.Enum]::GetNames([system.security.cryptography.x509certificates.x509contenttype])
Unknown
Cert
SerializedCert
Pfx
Pkcs12
SerializedStore
Pkcs7
Authenticode

Нас сейчас будет интересовать только тип Cert (только открытая часть сертификата).

Теперь мы уже знаем достаточно много, чтобы уже начать писать код. Мы сейчас получим объект сертификата, укажем тип экспортируемого сертификата и выполним метод Export():

$cert = (dir cert:\currentuser\My)[0]
$type = [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert
$ExportedData = $cert.Export($type)

Но это ещё не всё. Посмотрите, что у вас находится в переменной $ExportedData! Там на самом деле огромный массив байтов (в среднем более 2-20 тысяч элементов). Это же и указано в описании метода, что он возвращает. Именно массив байтов. Но что с ним делать? Я сам по-началу думал об этом, пока не вспомнил про одну вещь:

Certificate Export Wizard

У нас есть выбор, как экспортировать сертификат – в DER или Base64 кодировке. Когда я писал про PsFCIV, то у меня была такая же ситуация. Hasher у меня так же возвращал массив байтов, который мы сразу же загоняли в Base64 кодировку. В нашем случае потребуется то же самое:

$base64Data = [System.Convert]::ToBase64String($ExportedData)

И на выходе мы молучим длинную Base64 строку, которую уже и нужно экспортировать в файл любым удобным для вас методом:

Set-Content -Path path\file.cer -Value $base64Data -Encoding Ascii

вы можете убедиться в этом просто открыв .CER файл.  Чтобы получить объект сертификата из цифровой подписи файла, то мы можем просто воспользоваться командлетом Get-AuthenticodeSignature и выбрать свойство SignerCertificate. Если подпись файла дополнительно подписана сертификатом сервера времени, то вы можете использовать так же и свойство TimeStampCertificate:

[↓] [vPodans] $a.SignerCertificate

Thumbprint                                Subject
----------                                -------
9E95C625D81B2BA9C72FD70275C3699613AF61E3  CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=W...


[↓] [vPodans] $a = Get-AuthenticodeSignature .\Desktop\EASetup.exe
[↓] [vPodans] $a.SignerCertificate

Thumbprint                                Subject
----------                                -------
9E95C625D81B2BA9C72FD70275C3699613AF61E3  CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=W...


[↓] [vPodans] $a.TimeStamperCertificate

Thumbprint                                Subject
----------                                -------
4D6F357F0E6434DA97B1AFC540FB6FDD0E85A89F  CN=Microsoft Time-Stamp Service, OU=nCipher DSE ESN:85D3-305C-5BCF, OU=MOP...


[↓] [vPodans]

В этих свойствах сертификаты уже хранятся в виде объектов x509Certificate2 и дальше их можно экспортировать тем же самым путём, как мы это делали для сертификатов из Certificate Store. Собственно, для извлечения сертификатов из цифровых подписей файлов я написал неплохой скриптик, который добавляет в контекстное меню элемент Export Certificate:

Export Certificate Context menu

и по нажатию на него цифровая подпись будет экспортирована в MyDocs\FileName.ext.cer, где FileName.ext – оригинальное имя файла и к нему просто пристыковывается расширение CER. В скрипте предусмотрены несколько диалоговых окон о состоянии сертификата:

  • файл подписан и хеш файла соответствует хешу в цифровой подписи:

Valid Certificate

  • файл подписан, но хеш файла не соответствует хешу в цифровой подписи:

Hash Mismatch

  • файл не содержит цифровых подписей:

No certificate

В двух последних случаях сертификат не экспортируется никуда по очевидным причинам. И, собственно скрипты-инсталляторы этого контекстного меню с рабочим кодом для:

  • PowerShell 1.0:
10
  • PowerShell V2:
11

В следующих статьях я планирую поговорить про импорт сертификатов в Certificate Store, а так же и экспорт сертификатов с закрытыми ключами в PFX файл с использованем Windows PowerShell.