В продолжении темы исследования COM интерфейсов CryptoAPI для управления службами Certificate Services предлагаю теперь разобрать вопросы KRA — Key Recovery Agents. Сначала напомню уже изученные темы по CryptoAPI:
И на сегодня наш план выглядит достаточно интересно:
KRA используется для архивирования закрытых ключей сертификатов в БД CA. В случае если пользователь уничтожил свой закрытый ключ он может его восстановить. Для этого нужно обратиться к администратору CA для извлечения BLOB (Binary Large OBject) в файл. После чего агент восстановления (KRA) использует свой закрытый ключ для расшифровки закрытого ключа из BLOB'а. Учитывая, что я и не только уже рассматривали этот вопрос, я не буду пересказывать принцип работы Key Archival:
Все сертификаты KRA публикуются в forest naming configuration context партиции AD и реплицируется между всеми контроллерами в лесу. Публикация просисходит в точке:
CN=KRA, CN=Public Key Services, CN=Services, CN=Configuration, DC=ForestRootDomain
PS C:\> $ldap = [ADSI]"LDAP://CN=KRA, CN=Public Key Services, CN=Services, CN=Configuration, DC=contoso,dc=com" PS C:\> $ldap distinguishedName ----------------- {CN=KRA,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com} PS C:\> $kra = $ldap.psbase.children | %{$_} PS C:\> $kra distinguishedName ----------------- {CN=contoso-DC2-CA,CN=KRA,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com} {CN=Contoso CA,CN=KRA,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com}
Мы получили список CA в лесу и посмотрев каждый из них можем посмотреть сколько KRA сертификатов выпустил каждый CA:
PS C:\> $kra[1] | fl * objectClass : {top, msPKI-PrivateKeyRecoveryAgent} cn : {Contoso CA} userCertificate : {48 130 6 104 48 130 5 80 160 3 2 1 2 2 10 21 29 22 96 0 0 0 0 0 4 48 13 6 9 42 134 72 134 247 13 1 1 5 5 0 48 67 49 19 48 17 6 10 9 146 38 137 147 242 44 100 1 25 22 3 99 111 109 49 23 48 21 6 10 9 146 38 137 147 242 44 100 1 25 22 7 99 111 110 116 111 115 111 49 19 48 17 6 3 85 4 3 19 10 67 111 110 116 111 115 111 32 67 65 48 30 23 13 48 57 48 50 49 53 49 53 48 53 50 53 9 0 23 13 49 49 48 50 49 53 49 53 48 53 50 53 90 48 86 49 19 48 17 6 10 9 146 38 137 147 242 44 100 1 25 22 3 99 111 109 49 23 48 21 6 10 9 146 38 137 147 242 44 100 1 25 22 7 99 111 110 116 <...>
Свойство UserCertificate в виде массива может содержать сертификаты KRA в виде байтового массива. Например, указанный CA выпустил 2 сертификата KRA:
PS C:\> $kra[1].usercertificate.count 2
И обратившись к индексам этого свойства можно получить конкретные сертификаты: $kra[1].usercertificate[0]. Если CA не выпускал сертификаты для KRA, то свойство UserCertificate будет содержать число ноль:
PS C:\> $kra[0] | fl * objectClass : {top, msPKI-PrivateKeyRecoveryAgent} cn : {contoso-DC2-CA} userCertificate : {0} <...>
Поэтому для получения всех сертификатов KRA нужно пройтись по всем членам CN=KRA и выбрать из них ненулевые значения свойства UserCertificate. Если сертификатов больше, чем 1, то они будут храниться в этом свойстве в виде массива. Чтобы посмотреть сам сертификат, вы можете просто сохранить этот массив байтов в файл:
PS C:\> [System.IO.File]::WriteAllBytes("C:\kra.cer",$kra[1].usercertificate[1]) PS C:\> & .\kra.cer
А теперь вернёмся к интерфейсу ICertAdmin2. С его помощью мы можем посмотреть количество агентов восстановления, которые назначены на CA и посмотреть их сертификаты. Однако, этот интерфейс имеет какой-то баг с PropID, которые связаны с KRA, о которых я писал в предыдущей статье, поэтому для получения этих данных мы будем использовать интерфейс ICertRequest3.
PS C:\> $CertRequest = New-Object -ComObject CertificateAuthority.Request.1 PS C:\> $CertRequest.GetCAProperty("dc1\contoso ca",0x18,0,1,0) 1 PS C:\> $CertRequest.GetCAProperty("dc2\contoso-dc2-ca",0x18,0,1,0) 0 PS C:\> $CertRequest.GetCAProperty("dc1\contoso ca",0x19,0,1,0) 1 PS C:\> $CertRequest.GetCAProperty("dc2\contoso-dc2-ca",0x19,0,1,0) 0
Мы видим, что на сервере DC1 назначен 1 агент восстановления, а на DC2 ни одного. Сегодня мы добавим агентов восстановления на DC2.
Примечание: чем отличаются KRACERTUSEDCOUNT и KRACERTCOUNT? KRACERTCOUNT показывает сколько всего KRA назначено на конкретном сервере CA. А KRACERTUSEDCOUNT показывает сколько из них будет использовать CA при архивации каждого закрытого ключа сертификата и это число может быть меньше, чем общее количество сертификатов KRA. В таком случае CA рандомно выбирает KRACERTUSEDCOUNT сертификатов и шифрует ими закрытый ключ. Например, KRACERTCOUNT = 5, а KRACERTUSEDCOUNT = 3, то CA при архивации ключа выберет рандомно 3 сертификата KRA из списка и зашифрует ими ключ. И только эти 3 агента восстановления могут восстановить ключ, а остальные 2 уже не смогут.
Чтобы посмотреть сами сертификаты KRA, мы должны воспользоваться PropID = CR_PROP_KRACERT (0x1A).
И вот что мы увидим:
PS C:\> $CertAdmin.GetCAProperty("dc1\contoso ca",0x1A,0,3,0) -----BEGIN CERTIFICATE----- MIIGaDCCBVCgAwIBAgIKFR0WYAAAAAAABDANBgkqhkiG9w0BAQUFADBDMRMwEQYK CZImiZPyLGQBGRYDY29tMRcwFQYKCZImiZPyLGQBGRYHY29udG9zbzETMBEGA1UE AxMKQ29udG9zbyBDQTAeFw0wOTAyMTUxNTA1MjVaFw0xMTAyMTUxNTA1MjVaMFYx EzARBgoJkiaJk/IsZAEZFgNjb20xFzAVBgoJkiaJk/IsZAEZFgdjb250b3NvMQ4w DAYDVQQDEwVVc2VyczEWMBQGA1UEAxMNQWRtaW5pc3RyYXRvcjCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAJG7T5yMninhrkXFQU8WGUr5BYYUeOz10Rkn <...>
В нулевом индексе мы видим первый сертификат. Последующие будут храниться в других индексах:
$CertAdmin.GetCAProperty("dc1\contoso ca",0x1A,$Index,3,0)
Этот сертификат можно спокойно записать в файл и через GUI просмотреть его свойства. Но вообще это не родной формат сертификата KRA для данного интерфейса, поскольку добавлять сертификаты KRA на сервере CA нужно в ASN1 DER-encoded string формате. Т.е. Flags должен быть не 0, а 2:
$CertAdmin.GetCAProperty("dc1\contoso ca",0x1A,$Index,3,2)
и увидим примерно такое:
PS C:\> $kra = $CertAdmin.GetCAProperty("dc1\contoso ca",0x1a,0,3,2) PS C:\> $kra ?????A???` ?????c?♣??????????????????T???????????????????????????????????????T???????????????????????????????????????? ??ca♣??????☺??????????????????????????????????????????????????????????????????????????????????????????????????????????? ??????????????????????☺????????Є?????????????????☻???????☻??????????????ЎЖ?????????????CA??????Ё????????????h??A??????? ??????????????????????????????????????????????╣???????????????????????????????????????????????????????????????????????? ?????????????????????C?cЁ??????C??????????????????????????????????????????????????????????????????????????????????????? ???????????????????????????????????????Х???CA?????Ё???????CA?????Б????CA??????????????????????a???☺???????????????????? ????????????????????????????????????c?????????????????????????????????????H╝??????????????????{?????????????
выглядит весьма уныло, но вот в таком формате и надо записывать сертификаты агентов восстановления в CA.
Когда мы рассмотрели основные моменты связанные с агентами восстановления в контексте CryptoAPI, мы можем приступить к финальной части — назначению новых агентов восстановления. В принципе, здесь нет ничего сложного, поскольку для добавления KRA нужно сделать 5 вещи:
Первый этап у нас уже пройден, поскольку у нас уже есть такой массив в AD. Если сертификата агента восстановления нет в AD, то можно его получить из файла:
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $cert.Import(".\desktop\kra.cer") $bytes = $cert.RawData
и переменная $bytes будет содержать такой же байтовый массив.
А вот с конвертером чуть сложнее. Я не нашёл ни одного готового конвертера, который бы выполнял все шаги из пункта 2, поэтому я написал свой конвертер с блек-джеком и шлюхами:
function ConvertTo-DERstring ([byte[]]$bytes) { # создаём объект StringBuilder, который нам будет собирать конечную символьную строку $SB = New-Object System.Text.StringBuilder # преобразовываем каждый байт в его hex значение $bytes1 = $bytes | %{"{0:X2}" -f $_} # циклом перебираем каждую пару байт for ($n = 0; $n -lt $bytes1.count; $n = $n + 2) { # переставляем элементы пары местами, чтобы получить little-endian hex-строку # после чего получаем десятичное представление этой строки и получаем Unicode # символ, который соответствует этому десятичному значению. [void]$SB.Append([char](Invoke-Expression 0x$(($bytes1[$n+1]) + ($bytes1[$n])))) } $SB.ToString() }
А дальше уже по накатанной дороге:
[Administrator] # создаём COM объект ICertAdmin2 [Administrator] $CertAdmin = new-object -com certificateauthority.admin.1 [Administrator] # выбираем все Enterprise CA [Administrator] $ldap = [ADSI]"LDAP://CN=KRA, CN=Public Key Services, CN=Services, CN=Configuration, DC=contoso,dc=com" [Administrator] $kra = $ldap.psbase.children | %{$_} [Administrator] # сразу конвертируем все сертификаты в DER-encoded string [Administrator] $kra1 = ConvertTo-DERstring $kra[1].usercertificate[0] [Administrator] $kra2 = ConvertTo-DERstring $kra[1].usercertificate[1] [Administrator] # можем убедиться на месте, что строка имеет ожидаемый вид [Administrator] $kra2 ?????A???E ?????c?♣??????????????????T???????????????????????????????????????T???????????????????????????????????????? ??ca♣??????☺????????????????????????????K?????????????????????????????????????????????т???????????????????????????????? ??????????????????????☺????????Є?????????????????☻???????☻??????????????ЎЖ?????????????CA??????Ё????????????h??A??????? ??????????????????????????????????????????????╣???????????????????????????????????????????????????????????????????????? ?????????????????????C?cЁ??????C??????????????????????????????????????????????????????????????????????????????????????? ???????????????????????????????????????Х???CA?????Ё???????CA?????Б????CA??????????????????????a???☺???????????????????? ???????????????????????????????????????????????????????????????????????????????????????????????????????????? [Administrator] # и записываем первую строку сертификат агента восстанвовления в нулевой индекс [Administrator] $CertAdmin.SetCAProperty("dc2\contoso-dc2-ca",0x1a,0,3,$kra1) [Administrator] # все последующие строки сертификатов записываются в последующие индексы массива [Administrator] $CertAdmin.SetCAProperty("dc2\contoso-dc2-ca",0x1a,1,3,$kra2) [Administrator] # используем PropID = KRACERTUSEDCOUNT (0x18), чтобы сказать сколько у нас будет использоваться агентов [Administrator] # я буду их использовать все [Administrator] $CertAdmin.SetCAProperty("dc2\contoso-dc2-ca",0x18,0,1,2) [Administrator] # перезапускаем службу CertSvc [Administrator] $wmi = gwmi win32_service -comp dc2 -filter "name='certsvc'" [Administrator] [void]$wmi.StopService() [Administrator] [void]$wmi.StartService() [Administrator] # и проверяем нашу работу: [Administrator] $CertReq = new-object -com certificateauthority.request.1 [Administrator] $CertReq.GetCAProperty("dc2\contoso-dc2-ca",0x18,0,1,0) 2 [Administrator] $CertReq.GetCAProperty("dc2\contoso-dc2-ca",0x19,0,1,0) 2 [Administrator]
всё.
Comments: