Posts on this page:
Представлю на обозрение пробный скрипт по управлению сертификатами в хранилище Certificate Store — CertMgmtPack.ps1, который (как я надеюсь) может найти применение на серверах Windows Server 2008 R2 Server Core и реализован на основе наших недавних исследований:
Версия пока что 0.47 (т.к. предстоит добавить ещё разного функционала и допилить существующий). Пока что мне удалось реализовать функционал импорта сертификатов из файлов в store и экспорт из store в файлы сертификатов.
Примечание: скрипт работает только в PowerShell V2. В каждую функцию вложена справка, которую вы можете прочитать набрав Get-Help Import/Export-Certiifcate.
После подключения скрипта (используя dot-sourcing) у вас будут доступны 2 функции:
И пару слов о том, как их использовать.
содержит следующие параметры:
dir *.cer | Import-CertificateПараметр обязательный.
Read-Host "Enter password for PFX file" –AsSecureString
И пару примеров, как использовать функцию:
$pass = Read-Host "Enter password for PFX" –AsSecureString Import-Certificate mycert.pfx -Password $pass -Exportable
импортирует файл mycert.pfx в контейнер Personal хранилища текущего пользователя и пометит ключ как экспортируемый.
Import-Certificate mycert.pfx -Password $pass -StrongProtection
Импортирует файл mycert.pfx в контейнер Personal хранилища текущего пользователя, пометит ключ как неэкспортируемый и включит режим усиленной защиты закрытого ключа, что потребует ввод пароля пользователя каждый раз при его использовании.
Import-Certificate certs.p7b –Storage 'Computer' –Container 'TrustedPublisher'
Импортирует все сертификаты из файла certs.p7b в контейнер Trusted Publishers компьютерного хранилища.
Я не смог провести полноценное тестирование функции экспорта, поэтому гарантировать 100% работу пока не могу. Но вы, уважаемые читатели, можете мне в этом помочь.
И несколько примеров использования:
Export-Certificate c:\certs -Type 'PKCS7'
Экспортирует все сертификаты из Current User\Personal в один PKCS7 файл с именем ExportedCertificates.p7b (это имя зашито на уровне скрипта).
Export-Certificate c:\certs -Type 'cert' -Storage 'LocalMachine' -Container 'Root' -SerialNumber '663'
Экспортирует все сертификаты из контейнера доверенных центров сертификации (Trusted Root CAs) компьютерного хранилища, у которых в серийном номере встречается последовательность 663 в CER файлы. При этом каждый сертификат будет экспортирован в индивидуальный файл с именем, которое строится по схеме: SubjectCN_Thumprint.CER, например: Thawte Premium Server CA_4F65566336DB6598581D584A596C87934D5F2AB4.cer
Export-Certificate c:\certs -Type 'PKCS12' -Password $pass -DeleteKey -Subject 'UserName'
Экспортирует все сертификаты из Current User\Personal в PFX файлы у которых в любом месте поля Subject есть UserName. При успешном экспорте закрытый ключ сертификата будет удалён из хранилища и будет содержаться только в PFX файле.
В связи с тем, что CN поля Subject может содержать недопустимые символы для имён файлов, мне скорее всего придётся искать другой метод именования экспортируемых сертификатов и чтобы они были распознаваемые. Если вы обнаружите какие-то ошибки или будут пожелания, то пишите мне в коментарии или на почту. По мере дописывания я буду выкладывать новые версии скрипта.
Короткая заметка. Когда вы экспортируете сертификат вместе с закрытым ключом, то можете заметить такую опцию:
К сожалению, метод Export() у объектов X509Certificate2 не позволяет штатно проделывать данную операцию. Это можно сделать через CAPICOM.PrivateKey.Delete(), но данная возможность отсутствует в PowerShell, в то время, как вы можете это сделать в VBS. Это связано с весьма паршивой поддержкой COM со стороны PowerShell, поэтому для реализации функционала этой галочки вам придётся проделать следующие шаги:
На языке 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). В качестве аргументов он принимает следующее:
Давайте немного разберёмся с флагами последнего аргумента:
Примечание: вы никогда не должны использовать флаг 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 нам нужно указывать пароль:
Следовательно, конструктор метода 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 хранилища
Внутри уже видны контейнеры, которые мы видим в виде папок в 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.