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 хранилища
Внутри уже видны контейнеры, которые мы видим в виде папок в 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 тысяч элементов). Это же и указано в описании метода, что он возвращает. Именно массив байтов. Но что с ним делать? Я сам по-началу думал об этом, пока не вспомнил про одну вещь:
У нас есть выбор, как экспортировать сертификат – в 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:
и по нажатию на него цифровая подпись будет экспортирована в MyDocs\FileName.ext.cer, где FileName.ext – оригинальное имя файла и к нему просто пристыковывается расширение CER. В скрипте предусмотрены несколько диалоговых окон о состоянии сертификата:
В двух последних случаях сертификат не экспортируется никуда по очевидным причинам. И, собственно скрипты-инсталляторы этого контекстного меню с рабочим кодом для:
В следующих статьях я планирую поговорить про импорт сертификатов в Certificate Store, а так же и экспорт сертификатов с закрытыми ключами в PFX файл с использованем Windows PowerShell.
Столкнулся с проблемой проверки сертификатов на отзыв среди компьютеров всего домена, и соответственно пришел к 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
Самое правильное, конечно же, это не делать лишних конвертаций в Base64, а писать в бинарном виде сразу: Set-Content -path path\file.cer -value $bytes -encoding byte или использовать [io.file]::writeallbytes().
Comments: