Contents of this directory is archived and no longer updated.

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:
  • PowerShell V2:

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


Share this article:

Comments:

www.google.com/accounts/o8/id?id=AItOawmK_xDzHoVDIVbX6VaCVV85vq8N0_XWCxc

Столкнулся с проблемой проверки сертификатов на отзыв среди компьютеров всего домена, и соответственно пришел к powershell скрипту. В процессе проверки сертификата пользуюсь стандартной утилитой certutil (она есть на всех компьютерах в домене). В процессе отладки заметил, что если пользоваться связкой $base64Data = [System.Convert]::ToBase64String($ExportedData) $base64Data > path\file.cer то в некоторых случаях(скорее всего, причина 32-bit OS, WinXP SP3) возникает ошибка при попытке проверить этит сертификат: certutil -verify c:\scripts\1.cer 402.203.0: 0x80070057 (WIN32: 87): ..CertCli Version LoadCert(Cert) returned ASN1 bad tag value met. 0x8009310b (ASN: 267) CertUtil: -verify command FAILED: 0x8009310b (ASN: 267) CertUtil: ASN1 bad tag value met. 301.3160.0: 0x8009310b (ASN: 267) Поэтому, спасибо, хорошим людям :), было найдено решение: set-content -value $bytes -encoding byte -path path\file.cer

Vadims Podāns

Самое правильное, конечно же, это не делать лишних конвертаций в Base64, а писать в бинарном виде сразу: Set-Content -path path\file.cer -value $bytes -encoding byte или использовать [io.file]::writeallbytes().

Comments are closed.