Продолжая тему криптографии, предлагаю несколько ближайших постов посвятить вопросам использования CryptoAPI в PowerShell для управления Cerficiation Authority (сокращённо CA). Я считаю эту тему достаточно интересной, хоть у нас есть космическая утилита certutil.exe. Но тот, кто видел хелп к нему, тот знает какой это ужас, автоматизировать что-то с использованием certutil. Microsoft подарил нам не менее космический и православный инструмент автоматизации — PowerShell. К сожалению, вынужден признать, что в ряде задач PowerShell сильно уступает аналогам в cmd и с этим ничего нельзя поделать. Но по мере своих сил буду пытаться доказывать обратное. Одно из преимуществ PowerShell является поддержка .NET, который очень удобен в использовании. Но .NET не всемогущ и не содержит в себе ни единого класса, при помощи которого можно было бы управлять службой certificate services (точнее самим CA). CryptoAPI неплохо документированы на уровне unmanaged code (а это подразумевает использование низкоуровневых языков программирования, как C++). Но, опять, слава сиськамСноверуПротоколу у нас есть одна лазейка — COM интерфейсы к CryptoAPI. Изучив немного эти интерфейсы, обнаружил, что они местами очень интересные и досточно легко управляемы. Но это не всегда так, иногда они бывают достаточно сложными для понимания с первого раза. В процессе исследования, были обнаружены вообще нелогичные вещи, с которыми нам придётся считаться.
Эти COM интерфейсы позволили мне реализовать едва ли не половину функционала моего PowerPack'а для PowerGUI. Оценить моё детище можно по этой ссылке: Enterprise PKI management. Я в него вложил достаточно много знаний для того, чтобы упростить жизнь администраторам инфраструктур PKI. Пока прогресс неважный, т.к. я пока только повторяю функционал консолей certmgr.msc, certsrv.msc, ocsp.msc и certutil.exe. Но я надеюсь реализовать там действительно что-то нужное и очень полезное.
Наша отправная точка на MSDN будет находиться здесь: Cryptography Interfaces. Для начала мы поработаем с интерфейсом ICertAdmin2, который зарегистрирован в системе как COM класс CertificateAuthority.Admin.1. Вот как он создаётся:
PS C:\> $CertAdmin = New-Object -ComObject "CertificateAuthority.Admin.1" PS C:\> $CertAdmin | gm TypeName: System.__ComObject#{f7c3ac41-b8ce-4fb4-aa58-3d1dc0e36b39} Name MemberType Definition ---- ---------- ---------- DeleteRow Method int DeleteRow (string, int, Date, int, int) DenyRequest Method void DenyRequest (string, int) GetArchivedKey Method string GetArchivedKey (string, int, int) GetCAProperty Method Variant GetCAProperty (string, int, int, int, int) GetCAPropertyDisplayName Method string GetCAPropertyDisplayName (string, int) GetCAPropertyFlags Method int GetCAPropertyFlags (string, int) GetConfigEntry Method Variant GetConfigEntry (string, string, string) GetCRL Method string GetCRL (string, int) GetMyRoles Method int GetMyRoles (string) GetRevocationReason Method int GetRevocationReason () ImportCertificate Method int ImportCertificate (string, string, int) ImportKey Method void ImportKey (string, int, string, int, string) IsValidCertificate Method int IsValidCertificate (string, string) PublishCRL Method void PublishCRL (string, Date) PublishCRLs Method void PublishCRLs (string, Date, int) ResubmitRequest Method int ResubmitRequest (string, int) RevokeCertificate Method void RevokeCertificate (string, string, int, Date) SetCAProperty Method void SetCAProperty (string, int, int, int, Variant) SetCertificateExtension Method void SetCertificateExtension (string, int, string, int, int, Variant) SetConfigEntry Method void SetConfigEntry (string, string, string, Variant) SetRequestAttributes Method void SetRequestAttributes (string, int, string) PS C:\>
Как вы видите, этот интерфейс позволяет использовать достаточно много интересных методов. Давайте начнём с простого (а может быть и не очень простого) — GetCAProperty. По ссылке вообще ничего непонятно, что и как там использовать. Единственное, что я понял — это то, что данный метод принимает 5 параметров в определённом порядке и всё. Поэтому давайте будем считать, что этой странички не существует в природе (кроме случаев, когда будем преследовать порядок расположения аргументов). Пошарившись на MSDN, нашёл более годную ссылку в спецификациях Windows Communications Protocols — 3.2.1.4.2.2 ICertRequestD2::GetCAProperty (Opnum 7). И вот как должны выглядеть параметры:
Примечание: по всей видимости в табличке с флагами закралась ошибка, т.к. использование первого флага даёт Base64, но с заголовками Begin/End Certificate. А второй — наоборот, без заголовка.
Вам кажется, что это всё ужасно и сложно? Поверьте, это не так. Давайте прочитаем парочку свойств. Например, прочитаем ссылки на CRL и CRT, которые публикуются в CDP и AIA издаваемых сертификатах. Исходя из таблички мы знаем, что CRL'ы вызываются PropID = 0x29 (или 41 в десятичной нотации), а CRT — PropID = 0x2A (или 42). Поскольку это индексируемое свойство, то первая ссылка будет содержаться в индексе 0 (первый индекс массива), а последующие ссылки в индексах 1 и далее и PropIndex мы укажем 0. PropType у нас будет String и числовое значение — 4 (не забываем, что PropType считаются начиная с единицы). Flags может быть любой, т.к. он будет игнорироваться при PropType = 4:
PS C:\> $CertAdmin.GetCAProperty("ca\sysadmins-lv-ca", 41, 0, 4, 0) http://ca.sysadmins.lv/sysadmins-LV-CA.crl PS C:\> $CertAdmin.GetCAProperty("ca\sysadmins-lv-ca", 42, 0, 4, 0) http://ca.sysadmins.lv/sysadmins-LV-CA.crt PS C:\>
Как вы видите, на самом деле здесь нет ничего страшного. Нужно лишь научиться оперировать табличкой. Давайте для разнообразия посмотрим на сертификат CA. Для этого мы должны найти PropID, который отвечал за сертификат. Это будет PropID = 0xC. Поскольку этот параметр так же индексируемый, то он будет содержать все сертификаты CA, если их там несколько. Самый первый сертификат будет храниться в индексе 0, следующий сертификат в индексе 1 и т.д. Исходя из таблички, PropType должен быть 3. Посмотрим сертификат в Base64 кодировке, т.е. Flags должен быть 0 или 1:
PS C:\> $cert = $CertAdmin.GetCAProperty("ca\sysadmins-lv-ca", 0xc, 0, 3, 0) PS C:\> $cert -----BEGIN CERTIFICATE----- MIIFbTCCA1WgAwIBAgIQPf+vkU1hMoJCe9WRwftYbTANBgkqhkiG9w0BAQUFADBJ MRIwEAYKCZImiZPyLGQBGRYCbHYxGTAXBgoJkiaJk/IsZAEZFglzeXNhZG1pbnMx GDAWBgNVBAMTD3N5c2FkbWlucy1MVi1DQTAeFw0wOTA4MDYwNzIxMDFaFw0xNDA4 MDYwNzMwNTRaMEkxEjAQBgoJkiaJk/IsZAEZFgJsdjEZMBcGCgmSJomT8ixkARkW CXN5c2FkbWluczEYMBYGA1UEAxMPc3lzYWRtaW5zLUxWLUNBMIICIjANBgkqhkiG 9w0BAQEFAAOCAg8AMIICCgKCAgEAwL5wHTMEsMg6yDzqWPd9e/GT856o9OWJrv4Y 9iXtTa5mkdEZsbckVmAJODG28lcI7ScuD0rfceB6/gVn4VMK2SITsOv2XzC3ApRE zExEIuRmspFlpaDmOvBTYafkzokN2RJ+UQq8M9RSnSBHrOcvNJQYgglbjQOHsg4f qyMoPoZ+LYjoGZcCtqmR+FMOnalUGohnanf/fQJ/gcoAET6H45rqAB/P90sWOsZo pwCgGmvJe4GBXbZDg4ADZIbrYm2baxwKJlhsDAhzZ8Fo2irnSBFJavu6+LwQHv8P fL/ZGHBlf8s+uTeHfluG95bIe8VAGRwEwCyV5t+emBLq4ny5Q21MjWGDD3YjkKjA lqC7c2mpCC6YVxBArVVm1G7/d8mpBuO5ZzyqIVkEhxZuYPfK6k7EEKx3BuCC97UT ZcSVJBCHmxbxWm/U30VIpu0HDlbCiHX0OSDRJhv32ppazDonGSp/GiBocXNKTLA+ PyXQqRvObovTFPMutzLQDczeee5zvfJ7Gs7xCskcsLp8uBuhGJMnW87hRglm4p0/ tdIViIbMrwbVxjtHPnp5MARvQBT26ZzE3k0zr9Do3Gi6AY8Jt6ssD6r5CXbBA5SF iGgp1czWb2xToqJtzuEjG1ssBj5MTMzINa4bMhQs52nXdUI2aG7n7mwoAVR3tHiW sK1lvdsCAwEAAaNRME8wCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFHrmiNrp+Fw+BwTeEaI4Iql1oxOrMBAGCSsGAQQBgjcVAQQDAgEAMA0G CSqGSIb3DQEBBQUAA4ICAQCuLfQSOcRoQ8EcuVMgIVjepuonY1MG7PhPvJ1fyanw 0MeKonNZ4a7gOop7R/8DSGBfnQMQJClVcFTxe8Osa88OkeJmb9LQix9wMfR1GAaw NwaX6ve0x4Ixr7m3KQEBOc+fAAGNLcxnlIDUD9EiSlFqqH+HkjxREU5RmE0TQFVS NfNtwFSX0loMuL8mo10m8CD/ETRdq6jXO6vMWll8wBirSD9/Zostn5+t1g6TU4fi bpR/56ueiRh0px2A1lhDTTGjV7UTqPvavUqJejZJCHf269xJb1r8mc+AJn/O4g4q WvVsTILLMw+Oit/7ZagFbjZa7jSBRFfEyU4lKnV9oxIO47FTjWgGi7R+Rg8VDWvQ a5JMhMadUWrz5VQRY9iBr1wlmvZH5100otrikZLfVxQ+HZVccgLvZ4BJpdk+Lzyk bqZSAwbOJlFF0po+4wWbakhzS/jZLcoO8NzE4SgW3NTcuWYsS2G8ubV139IykSCy RA9UcyoNNQM8CXr6I2UmhQwBqMrGZIi/rybPRQvlVSssuusfbxIiWMjdo/mQyUH2 A1diiIt1bpkVdJ9yRSDtuldoYe2w0QtId6WWAg2H8bTiF+/cu1kZ//rcK5UQyuku 1ggS22YqWCUrSQ4/heQdP1Ni619sMHFcMYWZTP5XySLiN+BBrs6/4pnJOVNsy742 KQ== -----END CERTIFICATE----- PS C:\> $cert > file.cer PS C:\> & c:\file.cer PS C:\>
Последней строчкой я вызвал команду, которая покажет мне сертификат как я бы запустил сам файл (я это и сделал этой командой на самом деле). Если там сертификат не один, то следующий сертификат можно было бы получить вот такой командой:
$CertAdmin.GetCAProperty("ServerName\CA Name", 0xC, 1, 3, 0)
Я просто увеличил PropIndex на единицу. Чтобы узнать сколько же там всего сертификатов, достаточно посмотреть PropID = 0x0B, PropType = 1 (т.к. команда нам вернёт число, то тип должен быть числовой — Long):
PS C:\> $CertAdmin.GetCAProperty("ca\sysadmins-lv-ca", 0xb, 0, 1, 0) 1 PS C:\>
Вот в данном случае у меня там всего 1 сертификат. На другом сервере у меня 2 сертификата:
[Administrator] $CertAdmin.GetCAProperty("dc1\contoso ca", 0xb, 0, 1, 0) 2 [Administrator]
Если вам это интересно, то советую поупражняться в получении различных свойств в различном формате с использованием ICertAdmin2 интерфейса и таблички: 3.2.1.4.2.2 ICertRequestD2::GetCAProperty (Opnum 7).
Это была первая часть использования CryptoAPI в PowerShell. В следующих частях мы рассмотрим другие интересные методы интерфейса ICertAdmin2.
Comments: