Contents of this directory is archived and no longer updated.

В продолжении темы исследования COM интерфейсов CryptoAPI для управления службами Certificate Services предлагаю теперь разобрать вопросы KRA — Key Recovery Agents. Сначала напомню уже изученные темы по CryptoAPI:

И на сегодня наш план выглядит достаточно интересно:

  • Получение списка доступных KRA в лесу и просмотр их сертификатов
  • Получение списка KRA назначенных на сервере CA;
  • Назначение новых KRA для CA.

Получение списка доступных KRA в лесу

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

Получение списка KRA назначенных на сервере CA

А теперь вернёмся к интерфейсу 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 назначенных на сервере CA

Чтобы посмотреть сами сертификаты 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.

Назначение новых KRA для CA

Когда мы рассмотрели основные моменты связанные с агентами восстановления в контексте CryptoAPI, мы можем приступить к финальной части — назначению новых агентов восстановления. В принципе, здесь нет ничего сложного, поскольку для добавления KRA нужно сделать 5 вещи:

  1. Получить массив байтов, которые бы представляли сертификат агента восстановления откуда угодно. Хоть из AD, хоть из файла сертификата;
  2. Используя специальный конвертер, разобрать этот массив на пары байтов, сконвертировать эти пары в little-endian hex строку, получить десятичное представление этой hex-строки и получить итоговую символьную строку;
  3. Записать эту строку используя метод SetCAProperty();
  4. Задать новое количество используемых агентов восстановления;
  5. Перезапустить службу CA.

Первый этап у нас уже пройден, поскольку у нас уже есть такой массив в 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]

всё.


Share this article:

Comments:

Comments are closed.