А знаете ли вы, что сервером OCSP (Online Responder) можно легко (ну это я приукрасил) управлять прямиком из PowerShell? Если знаете, можете дальше не читать. А если не знаете, тогда тоже можете не читать, потому что вряд ли это так уж интересно.

Пкитим реализовал несколько COM интерфейсов (по правде говоря, они кроме COM'ов ничего не умеют) для управления Online Responder. Основных интерфейсов 2:

IOCSPAdmin используется для управления самой фермой респондеров и для управления конфигурациями отзыва на отдельно взятом сервере из фермы. Объект создаётся следующим образом:

$OCSPAdmin = New-Object -ComObject CertAdm.OCSPAdmin

А подключение к серверу происходит при помощи метода GetConfiguration:

PS C:\> $OCSPAdmin = New-Object -ComObject CertAdm.OCSPAdmin PS C:\> $OCSPAdmin.GetConfiguration("dc2",$true) PS C:\> $OCSPAdmin OCSPServiceProperties OCSPCAConfigurationCollection --------------------- ----------------------------- System.__ComObject System.__ComObject PS C:\>

Примечание: служба Online Responder нигде в сети не регистрируется и никаких локаторов службы не существует. Оснастка PKIView.msc находит эти респондеры просто путём изучения расширения AIA у самого последнего сертификата CA Exchange.

В свойстве OCSPServiceProperties хранятся общесерверные настройки и настройки фермы:

PS C:\> $OCSPAdmin.OCSPServiceProperties Name Value Modified ---- ----- -------- ArrayController dc2.contoso.com False ArrayMembers {dc2.contoso.com} False AuditFilter 0 False MaxNumOfCacheEntries 5000 False NumOfThreads 50 False

ArrayController указывает на контроллер фермы (с которым вся ферма синхронизирует конфигурации), ArrayMembers показывает имена компьютеров — членов фермы. Вобщем, всё достаточно понятно (разумеется, если вы имеете опыт конфигурирования Online Responder из UI). Здесь мы видим колонку Modified. Оно показывает, было ли изменено свойство в течении нашей сессии. Это необходимо затем, чтобы пометить настройки, которые следует обновить при вызове метода SetConfiguration.

PS C:\> $OCSPAdmin.OCSPServiceProperties.Item(4).value = 5001 PS C:\> $OCSPAdmin.OCSPServiceProperties Name Value Modified ---- ----- -------- ArrayController dc2.contoso.com False ArrayMembers {dc2.contoso.com} False AuditFilter 0 False MaxNumOfCacheEntries 5001 True NumOfThreads 50 False PS C:\> $OCSPAdmin.SetConfiguration("dc2",$true)

Т.е. вы здесь можете поменять контроллер фермы, добавить/удалить членов фермы, настроить аудит, размер кэша и количество параллельных потоков. Так сказать, не отходя от кассы. Единственное, что здесь следует учитывать — у COM'ов индексирование массивов начинается с 1 (а не с 0, как в дотнете).

Для первого раза, я думаю, будет достаточно. В следующей части мы разберём более подробно конфигурации отзыва.

Thursday, May 17, 2012 10:44:33 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Наконец-то нашёл повод, чтобы написать в бложек. В последнее время как-то интересных тем не нахожу и занятных случаев для разбора не наблюдается. Всё самое интересное я публикую в своём буржуйском бложеке. Помогая разным пользователям на технетах решать их проблемы, пришёл к выводу, что очень многие не читают документацию по технологиям, которые они внедряют. С одной стороны это обычное явление. Зачем что-то читать, если можно залезть на технетики, где вам всё расскажут и починят? С другой стороны, они доставляют лулзы, когда у них всё накрывается медным тазом.

Казалось бы, а причём тут EFS? Недавний тред на русскоязычных технетах: Доступ к зашифрованным данным EFS с нескольких клиентских ПК (да, я обещал выпилиться оттуда насовсем, но иногда наведываюсь, чтобы снизить уровень ереси до более разумных размеров). Человек, очевидно, работает системным администратором и не знает про отличия отпечатков Кербероса и NTLM. Но это никому не интересно. Интересно другое — особенности работы EFS с сетевыми ресурсами.

Как известно (я так думал, что это известно всем), при шифровании файлов локально всё происходит очень просто. Берётся локальный сертификат EFS (если нету, он где-то добывается) и шифруются данные. Поскольку сертификат находится на локальной машине, никаких особых проблем нет, пока пользователь не зашифрует данные в сетевой папке или не поменяет рабочее место (компьютер). В чём проблема каждого случая:

1) если пользователь меняет рабочее место (пересаживается на другой компьютер), там загружается новый профиль и всё начинается сначала. Надо зашифровать данные — нужен сертификат. Очевидно, что другой компьютер ничего не знает, что сертификат уже есть где-то. Следовательно, сертификаты пользователя начинают плодиться на разных компьютерах, за которыми он работает. Это уже довольно серьёзная проблема, потому что пользователю всё равно, а решать проблему надо вам. Т.е. он работает чуть-чуть здесь, чуть-чуть там и ещё вон там и на сервере терминалов. А если у вас ещё ферма? Грубо говоря, пользователь в какой-то момент просто перестанет рандомно получать доступ к данным, потому что нужный сертификат находится на другом компьютере, а ИТ-суппорт будет тихо ненавидеть MSFT.

Перемещаемый профиль? Окай, пусть будет перемещаемый, только ключи к сертификатам НЕ ПЕРЕМЕЩАЮТСЯ вместе с профилем, они остаются там, где были сгенерированы. В Windows Server 2003 единственное средство, которое решало эту проблему хоть как-то, было Credential Roaming Service. Но оно, если не ошибаюсь, появилось слишком поздно (ближе к выходу Windows Server 2008). При применении Credential Roaming Service, ключи хранятся в Active Directory (что не есть очень безопасно).

2) шифрование файлов в сетевой папке. Многие администраторы имеют правильную привычку централизации ресурсов. Например, все документы должны храниться не на клиентских компьютерах, а на файловых серверах.

Здесь есть свои и более существенные проблемы. Что происходит в этом случае? А происходит вот что: на файловом сервере в бэкграунде загружается профиль пользователя и туда доставляется сертификат EFS. Если нету Credential Roaming Service, запрашивается или генерируется новый сертификат, который хранится на этом файловом сервере. Но не всё так просто, поскольку для загрузки профиля на удалённом компьютере, он должен быть доверен для делегирования (т.е. сервер представляется пользователем и получает право на доступ к ресурсам от его имени). По умолчанию только контроллеры домена доверены для делегирования, а остальные серверы нет, потому что это снижает безопасность (в случае компрометации сервера можно натворить кучу нехороших дел) и многие администраторы не решаются на это без необходимых на то оснований.

Хорошо, вы сделали сервер доверенным для делегирования, но представьте себе, что у вас DFS, который реплицирует данные между несколькими файловыми серверами. Пользователю, сидя за одним и тем же компьютером достаточно подключиться к другому файловому серверу (физическому) автоматически теряет доступ к зашифрованным файлам, потому что на другом сервере этого сертификата просто не существует. И снова на выручку приходит Credential Roaming Service.

Всё, казалось бы, хорошо и жизнь прекрасна. Но давайте зададим вопрос (ответ на который можно извлечь уже из написанного): а в каком виде зашифрованные файлы из сетевой папки поступают к пользователю? Совершенно очевидно, что они поступают расшифрованными. Т.е. зашифрованные файлы передаются по сети в ОТКРЫТОМ ВИДЕ (plain text). Поверьте, многие, кто внедрял EFS до сих пор этого не знают. Так что используете вы EFS или нет, знайте — от вашего EFS толк есть до тех пор, пока кто-нибудь не откроет файл из сетевой папки.

Примечание: поведение EFS при шифровании сетевых файлов применимо только если сервер или клиент работает под управлением устаревшей ОС (Windows 2000, Windows XP и Windows Server 2003/R2). Т.к. таковых ещё много, с этим приходится ещё считаться.

Начиная с Windows Vista и Windows Server 2008 эта проблема решена другим, более логичным способом. Файлы передаются как есть (т.е. зашифрованные файлы передаются в зашифрованном виде, остальные в открытом виде), а процесс шифрования и дешифрования происходит локально. И, как следствие, отпадает необходимость включения доверия для делегирования файлового сервере. Но и здесь мы сталкиваемся с проблемой перемещения пользователя между компьютерами. Поскольку сертификаты хранятся только локально, нужно снова применять Credential Roaming Service.

По правде говоря, начиная с Windows Vista и Windows Server 2008 вы можете хранить сертификаты EFS вместе с ключами на смарт-картах (здесь ссылки на все материалы по теме: EFS и смарт-карты в Windows Server 2008 (часть 4, заключительная)), но это снова затраты на дополнительное оборудование. Но, наверное, ваши данные стоят того. Или вам EFS просто не нужен.

По указанным выше причинам я стараюсь избегать применения EFS и запрещаю его на уровне политик, пока для этого не будет обоснованной необходимости.

Security |  PKI |  EFS
Friday, May 11, 2012 12:19:35 AM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

С постепенным уменьшением количества систем на базе Windows XP и Windows Server 2003 и увеличением доли более новых операционных систем, всё большую популярность приобретают новые криптографические алгоритмы. Эти алгоритмы (их ещё называют Suite B) включены в концепцию Cryptography Next Generation или сокращённо CNG. Эти алгоритмы отличаются более высокой стойкостью при меньшей длине ключа, а так же поддерживают более стойкие (и более длинные) алгоритмы хеширования семейства SHA2 (SHA256, SHA384, SHA512). А ещё они поддерживают новую модель криптоповайдеров — Key Storage Provider (KSP). В этой статье я расскажу про некоторые особенности применения CNG в EFS и Key Archival.

Encrypting File System и CNG

Начиная с Windows Vista вы можете использовать CNG для шифрующей файловой системы (CNG). Для этого вам придётся скопировать шаблон BasicEFS в оснастке Certificate Templates. Когда вы копируете шаблон, вы видите вот такой выбор:

Duplicate Template: template version

Если вы выберите верхний элемент (Windows Server 2003 Enterprise), вы создадите шаблон, который сможет использовать только классические (CryptoAPI 1.0) криптопровайдеры (например, Microsoft Enhanced Cryptographic Provider v1.0) и использовать только SHA1 в качестве алгоритма подписи.

Если вы выберите нижний элемент (Windows Server 2008 Enterprise), вы создадите шаблон, который будет использовать новые KSP (CryptoAPI 2.0) провайдеры и сможет использовать алгоритмы CNG. А можете и не использовать их (т.е. использовать RSA для ключей и SHA1 для подписей). Но в случае с EFS, при использовании KSP, вам придётся использовать алгоритмы CNG. Дело в том, что EFS поддерживает KSP, но требует использования алгоритмов ECC для генерации ключей (ECDH). Если хотите использовать RSA, необходимо выбирать верхний элемент и использовать классические криптопровайдеры.

Key Archival и CNG

Это хорошая практика, если вы применяете key archival для сертификатов EFS. Это позволяет пользователю получить доступ к зашифрованным данным в случае утери ключа, не прибегая к помощи EFS Recovery Agent для расшифровки файлов. Так же, как и с EFS, в key archival вы можете использовать весь спектр CNG. Причём, там нету таких ограничений, как обязательное использование ECC для KSP. Вы можете смело использовать KSP с RSA.

Но тут есть один нюанс. В процессе восстановления ключа, восстановленный ключ загружается в тот провайдер, в котором хранится ключ агента восстановления. Если это Key Storage Provider (KSP), восстановленный ключ будет загружен в KSP и уже потом выгружен в PFX. Когда восстановленный ключ доставлен клиенту, при импорте сертификата в хранилище, ключ будет загружен в провайдер, указанный в PFX. Следовательно, восстановленный RSA ключ будет загружен в KSP и (как было отмечено выше) новый сертификат не будет работать, потому что EFS не поддерживает KSP и RSA. Поэтому при импорте восстановленного сертификата в хранилище вам следует использовать командную строку:

certutil –importpfx –user –csp "Microsoft Enhanced Cryptographic Provider v1.0" path\file.pfx

И последнее. В статье Understanding Automatic Key Archival есть сноска:

Only RSA Security encryption keys may be archived in the CA database. Signature only keys as well as non-RSA key pairs will not be archived. Denied and resubmitted requests will also not archive private keys.

Это справедливо только для Windows Server 2003. В Windows Server 2008+ вы можете архивировать и ECC ключи тоже.

Security |  PKI |  EFS
Sunday, April 22, 2012 1:37:17 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Я вчера в статье Certificate Trust List (CTL) в PowerShell показал основные принципы, как работать с CTL в PowerShell. Сегодня я продолжу тему и покажу, как можно вытащить доверенные корневые сертификаты из ресурсов Crypt32.dll и из интернетов. Но для начала, Crypt32.dll

Вот как они выглядят в каком-нибудь редакторе ресурсов:

Crypt32.dll resources

Примечание: в Windows XP и Windows Server 2003 этих ресурсов не будет. Они есть только в системах начиная с Windows Vista.

Выглядит не очень презентабельно, просто они хранятся там в сериализированном виде (serialized store). Нас будет интересовать AUTHROOTS и UPDROOTS. В WinAPI есть целая вязанка функций, которые отвечают за работу с ресурсами: Resource Functions. Нас будет интересовать только следующие функции:

  • LoadLibraryEx — эта функция загружает библиотеку в память;
  • FindResource — эта функция ищет требуемый ресурс;
  • SizeofResource — вычисляем размер ресурса, чтобы выделить под него буфер;
  • LoadResource — загружает ресурс в буфер, созданный на предыдущем этапе;
  • FreeLibrary — этой функцией завершаем работу (выгружаем библиотеку из памяти).

И вот сигнатуры функций:

$signature = @"
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr LoadLibraryEx(
    String lpFileName,
    IntPtr hFile,
    UInt32 dwFlags
);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr FindResource(
    IntPtr hModule,
    int lpID,
    string lpType
);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint SizeofResource(
    IntPtr hModule,
    IntPtr hResInfo
);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadResource(
    IntPtr hModule,
    IntPtr hResInfo
);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool FreeLibrary(
    IntPtr hModule
);
"@
Add-Type -MemberDefinition $signature -Namespace PKI -Name Kernel32

А дальше всё просто:

$path = $Env:SystemRoot + "\System32\crypt32.dll"
# загружаем Crypt32.dll в память
$hModule = [PKI.Kernel32]::LoadLibraryEx($path,[IntPtr]::Zero,0x2)
# ищем ресрус AUTHROOTS под номером 1010
$hResInfo = [PKI.Kernel32]::FindResource($hModule,1010,"AUTHROOTS")
# вычисляем размер ресурса для создания буфера в managed memory
$size = [PKI.Kernel32]::SizeOfResource($hModule, $hResInfo)
# загружаем ресурс в unmanaged memory и получаем pointer на него
$resource = [PKI.Kernel32]::LoadResource($hModule, $hResInfo)
# создаём буфер для ресурса
$bytes = New-Object byte[] -ArgumentList $size
# и маршалером копируем ресурс из неуправляемой памяти в наш буфер.
# данные будут представлять собой serialized certificate store.
[Runtime.InteropServices.Marshal]::Copy($resource, $bytes, 0, $size)
# ход конём: создаём объект X509Certificate2Collection, который поддерживает
# сериализированные хранилища SST
$AUTHROOTS = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
# импортируем наши байтики в X509Certificate2Collection
$AUTHROOTS.Import($bytes)
# повторяем процедуру для UPDROOTS
$hResInfo = [PKI.Kernel32]::FindResource($hModule,1011,"UPDROOTS")
$size = [PKI.Kernel32]::SizeOfResource($hModule, $hResInfo)
$resource = [PKI.Kernel32]::LoadResource($hModule, $hResInfo)
$bytes = New-Object byte[] -ArgumentList $size
[Runtime.InteropServices.Marshal]::Copy($resource, $bytes, 0, $size)
$UPDROOTS = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$UPDROOTS.Import($bytes)
[PKI.Kernel32]::FreeLibrary($hModule)

И вот что у нас на выходе:

[↓] [vPodans] $AUTHROOTS[0..5] Thumbprint Subject ---------- ------- 879F4BEE05DF98583BE360D633E70D3FFE9871AF CN=NetLock Uzleti (Class B) Tanusitvanykiado, OU=Tanusitvanykiadok, O=NetL... E12DFB4B41D7D9C32B30514BAC1D81D8385E2D46 CN=UTN-USERFirst-Object, OU=http://www.usertrust.com, O=The USERTRUST Netw... 0483ED3399AC3608058722EDBC5E4600E3BEF9D7 CN=UTN-USERFirst-Hardware, OU=http://www.usertrust.com, O=The USERTRUST Ne... 58119F0E128287EA50FDD987456F4F78DCFAD6D4 CN=UTN - DATACorp SGC, OU=http://www.usertrust.com, O=The USERTRUST Networ... D29F6C98BEFC6D986521543EE8BE56CEBC288CF3 E=certificate@trustcenter.de, OU=TC TrustCenter Class 4 CA, O=TC TrustCent... 838E30F77FDD14AA385ED145009C0E2236494FAA E=certificate@trustcenter.de, OU=TC TrustCenter Class 2 CA, O=TC TrustCent... [↓] [vPodans] $UPDROOTS[0..5] Thumbprint Subject ---------- ------- 132D0D45534B6997CDB2D5C339E25576609B5CC6 CN=VeriSign Class 3 Public Primary Certification Authority - G3, OU="(c) 1... 204285DCF7EB764195578E136BD4B7D1E98E46A5 CN=VeriSign Class 1 Public Primary Certification Authority - G3, OU="(c) 1... 801D62D07B449D5C5C035C98EA61FA443C2A58FE CN=Entrust.net Certification Authority (2048), OU=(c) 1999 Entrust.net Lim... 8939576E178DF705780FCC5EC84F84F6253A4893 CN=Entrust.net Secure Server Certification Authority, OU=(c) 2000 Entrust.... 7030AABF8432A800666CCCC42A887E42B7553E2B CN=eSign Imperito Primary Root CA, OU=Public Secure Services, O=eSign Aust... C73026E325FE21916B55C4B53A56B13DCAF3D625 CN=Gatekeeper Root CA, OU=Gatekeeper PKI, O=eSign Australia [↓] [vPodans]

Я показал только по 6 первых элементов из каждого ресурса. На самом деле их там чуточку больше:

[↓] [vPodans] $AUTHROOTS.Count 87 [↓] [vPodans] $UPDROOTS.Count 165

С этим списком вы уже можете делать что хотите. Единственное, что я могу посоветовать — не пропускайте их без лишней надобности через certificate chaining engine (это включает как вызов метода Verify(), использование в X509Chain или просмотр сертификата в UI, т.к. это тоже запускает certificate chaining engine), потому что они будут устанавливаться в хранилище. А оно вам надо?

Однако, этот список не самый актуальный. Если не ошибусь, он обновляется (в самой библиотеке) с выходом сервис-пака. А список членов программы изменяется почаще (см: Windows Root Certificate Program Members). И вот пруф номер 2:

[↓] [vPodans] $UPDROOTS | ?{$_.thumbprint -eq "C060ED44CBD881BD0EF86C0BA287DDCF8167478C"} Thumbprint Subject ---------- ------- C060ED44CBD881BD0EF86C0BA287DDCF8167478C E=info@diginotar.nl, CN=DigiNotar Root CA, O=DigiNotar, C=NL [↓] [vPodans] dir cert:\CurrentUser\Disallowed\C060ED44CBD881BD0EF86C0BA287DDCF8167478C Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\Disallowed Thumbprint Subject ---------- ------- C060ED44CBD881BD0EF86C0BA287DDCF8167478C E=info@diginotar.nl, CN=DigiNotar Root CA, O=DigiNotar, C=NL [↓] [vPodans]

А, ведь, DigiNotar выпилен из программы.

На практике это происходит вот как: клиент скачивает самый свежий authrootstl.cab, списывает себе хеши и выпиливает все сертификаты из Third-Party CAs, чьи хеши не представлены в CTL. А если в CTL есть хеш нового участника, а сертификата у клиента нету? Он его должен где-то взять и заныкать до лучших времён (т.е. доставит в хранилище по первому требованию). Берёт его он примерно вот здесь: http://www.download.windowsupdate.com/msdownload/update/v3/static/trustedr/en/<Thumbprint>.crt

Возьмём какой-нибудь сертификат (из тех, что есть в Crypt32.dll) и поштриноль:

[↓] [vPodans] $rm = Invoke-RestMethod "http://www.download.windowsupdate.com/msdownload/update/v3/static/trustedr/en/879 F4BEE05DF98583BE360D633E70D3FFE9871AF.crt" [↓] [vPodans] [byte[]]$rm = $rm.ToCharArray() | %{[byte]$_} [↓] [vPodans] New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 (,$rm) Thumbprint Subject ---------- ------- 879F4BEE05DF98583BE360D633E70D3FFE9871AF CN=NetLock Uzleti (Class B) Tanusitvanykiado, OU=Tanusitvanykiadok, O=NetL... [↓] [vPodans]

Командлет Invoke-RestMethod появился в PowerShell 3.0 и является простым враппером для WebRequestCmdlet. Т.е. при наличии интернетов, вы можете взять authrootstl.cab и через Invoke-RestMethod или Invoke-WebRequest накачать себе сертификатов под завязку :)

Sunday, April 01, 2012 12:42:02 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Как вы знаете, .NET не имеет нативной поддержки для X.509 CRL и X.509 CTL (или STL — Security Trust List). Именно поэтому я в своё время написал свой прототип для объекта X509CRL2. Недавно мне потребовалось получить доступ к CTL из PowerShell.

Во-первых, что такое CTL? Это просто список данных (например, хешей сертификатов), который подписан доверенной стороной. Где они применяются? Например, в Microsoft Root Certificate Program, в которой участвуют различные коммерческие и государственные центры сертификации. Эти CA доверены в Windows по умолчанию. Сами сертификаты хранятся в нескольких местах:

  • Контейнер Third-Party Certification Authorities локального хранилища сертификатов;
  • Crypt32.dll (только в Windows Vista+);
  • Microsoft Update.

Список доверенных CA достаточно велик (более 300) и его состав контролируется через Certificate Trust List. Клиент Windows периодически скачивает с Windows Update этот самый CTL, в котором хранятся хеши всех доверенных корневых CA и сравнивает их с хешами сертификатов, установленных в Third-Party CAs. Все сертификаты, чьи хеши не представлены в CTL, выпиливаются из хранилища и к этим CA больше автоматического доверия нету. А если в списке есть хеш, но такого сертификата ВНЕЗАПНО нету, тогда клиент ещё раз обращается к Windows Update и скачивает оттуда сертификаты. Следует понимать, что этот CTL *не содержит* сами сертификаты, только их хеши и атрибуты (например, Friendly Name). Как я уже говорил, CTL защищается от подделок цифровой подписью.

И вот, мне приспичило получить вменяемый доступ к этому CTL в PowerShell. Задача оказалась достаточно лёгкой: CertCreateCTLContext. Всё очень похоже на работу с предыдущим скриптом X509CRL:

function Get-CTL {
[CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [Byte[]]$RawData
    )
Add-Type @"
using System.Security.Cryptography;

namespace System.Security.Cryptography.X509Certificates {
    public class X509CTL {
        public int Version;
        public OidCollection SubjectUsage;
        public String SequenceNumber;
        public DateTime ThisUpdate;
        public Oid SubjectAlgorithm;
        public Object[] Entries;
        public X509ExtensionCollection Extensions;
        public IntPtr Handle;
        public Byte[] RawData;
    }
    public class X509CTLEntry {
        public String Thumbprint;
        public X509Attribute[] Attributes;
        public IntPtr Certificate;
    }
    public class X509Attribute {
        public Oid OID;
        public Byte[] RawData;
    }
}
"@
$cryptsignature = @"
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CertCreateCTLContext(
    UInt32 dwMsgAndCertEncodingType,
    Byte[] pbCtlEncoded,
    UInt32 cbCtlEncoded
);
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean CertFreeCTLContext(
    IntPtr pCtlContext
);
[DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CertFindExtension(
    [MarshalAs(UnmanagedType.LPStr)]
    String pszObjId,
    UInt32 cExtensions,
    IntPtr rgExtensions
);
"@
$wincryptsignature = @"
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CTL_CONTEXT {
    public UInt32 dwMsgAndCertEncodingType;
    public IntPtr pbCtlEncoded;
    public UInt32 cbCtlEncoded;
    public IntPtr pCtlInfo;
    public IntPtr hCertStore;
    public IntPtr hCryptMsg;
    public IntPtr pbCtlContent;
    public UInt32 cbCtlContent;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CTL_INFO {
    public UInt32 dwVersion;
    public CTL_USAGE SubjectUsage;
    public CRYPTOAPI_BLOB ListIdentifier;
    public CRYPTOAPI_BLOB SequenceNumber;
    public UInt64 ThisUpdate;
    public UInt64 NextUpdate;
    public CRYPT_ALGORITHM_IDENTIFIER SubjectAlgorithm;
    public UInt32 cCTLEntry;
    public IntPtr rgCTLEntry;
    public UInt32 cExtension;
    public IntPtr rgExtension;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CTL_USAGE {
    public UInt32 cUsageIdentifier;
    public IntPtr rgpszUseageIdentifier;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CTL_ENTRY {
    public CRYPTOAPI_BLOB SubjectIdentifier;
    public UInt32 cAttribute;
    public IntPtr rgAttribute;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPT_ATTRIBUTE {
    [MarshalAs(UnmanagedType.LPStr)]
    public String pszObjId;
    public UInt32 cValue;
    public IntPtr rgValue;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPT_ALGORITHM_IDENTIFIER {
    [MarshalAs(UnmanagedType.LPStr)]
    public String pszObjId;
    public CRYPTOAPI_BLOB Parameters;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPTOAPI_BLOB {
    public UInt32 cbData;
    public IntPtr pbData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_EXTENSION {
    [MarshalAs(UnmanagedType.LPStr)]
    public String pszObjId;
    public Boolean fCritical;
    public CRYPTOAPI_BLOB Value;
}
"@
    $WarningPreference = "SilentlyContinue"
    Add-Type -MemberDefinition $cryptsignature -Namespace PKI -Name Crypt32
    Add-Type -MemberDefinition $wincryptsignature -Namespace PKI.Structs -Name Wincrypt
    [IntPtr]$CTLContext = [IntPtr]::Zero
    $CTLContext = [PKI.Crypt32]::CertCreateCTLContext(65537,$RawData,$RawData.Length)
    if (!$CTLContext.Equals([IntPtr]::Zero)) {
        $CTLContextStructure = [Runtime.InteropServices.Marshal]::PtrToStructure($CTLContext,[PKI.Structs.Wincrypt+CTL_CONTEXT])
        $CTLInfo = [Runtime.InteropServices.Marshal]::PtrToStructure($CTLContextStructure.pCtlInfo,[PKI.Structs.Wincrypt+CTL_INFO])
        # CTL usage
        $rgpszUseageIdentifier = $CTLInfo.SubjectUsage.rgpszUseageIdentifier
        $oids = New-Object Security.Cryptography.OidCollection
        for ($m = 0; $m -lt $CTLInfo.SubjectUsage.cUsageIdentifier; $m++) {
            $pszOid = [Runtime.InteropServices.Marshal]::ReadIntPtr($rgpszUseageIdentifier)
            [void]$oids.Add([Runtime.InteropServices.Marshal]::PtrToStringAnsi($pszOid))
            $rgpszUseageIdentifier = [int]$rgpszUseageIdentifier + [Runtime.InteropServices.Marshal]::SizeOf([PKI.Structs.Wincrypt+CTL_USAGE])
        }
        $bytes = New-Object byte[] -ArgumentList $CTLInfo.SequenceNumber.cbData
        [Runtime.InteropServices.Marshal]::Copy($CTLInfo.SequenceNumber.pbData,$bytes,0,$CTLInfo.SequenceNumber.cbData)
        [array]::Reverse($bytes)
        $SequenceNumber = -join ($bytes | %{"{0:x2}" -f $_})
        $ctl = New-Object System.Security.Cryptography.X509Certificates.X509CTL -Property @{
            Version = $CTLInfo.dwVersion + 1;
            SubjectUsage = $oids;
            SequenceNumber = $SequenceNumber
            ThisUpdate = [datetime]::FromFileTime($CTLInfo.ThisUpdate);
            SubjectAlgorithm = $CTLInfo.SubjectAlgorithm.pszObjId;
            Handle = $CTLContext;
        }
        $entries = @()
        if ($CTLInfo.cCTLEntry -gt 0) {
            $rgCTLEntry = $CTLInfo.rgCTLEntry
            for ($n = 0; $n -lt $CTLInfo.cCTLEntry; $n++) {
                $entry = New-Object System.Security.Cryptography.X509Certificates.X509CTLEntry
                $EntryStructure = [Runtime.InteropServices.Marshal]::PtrToStructure($rgCTLEntry,[PKI.Structs.Wincrypt+CTL_ENTRY])
                $bytes = New-Object byte[] -ArgumentList ($EntryStructure.SubjectIdentifier.cbData)
                [System.Runtime.InteropServices.Marshal]::Copy($EntryStructure.SubjectIdentifier.pbData,$bytes,0,$EntryStructure.SubjectIdentifier.cbData)
                $entry.Thumbprint = -join ($bytes | %{"{0:X2}" -f $_})
                $attribs = @()
                if ($EntryStructure.cAttribute -gt 0) {
                    $rgAttribute = $EntryStructure.rgAttribute
                    for ($m = 0; $m -lt $EntryStructure.cAttribute; $m++) {
                        $attrib = New-Object System.Security.Cryptography.X509Certificates.X509Attribute
                        $AttribStructure = [Runtime.InteropServices.Marshal]::PtrToStructure($rgAttribute,[PKI.Structs.Wincrypt+CRYPT_ATTRIBUTE])
                        $attrib.OID = $AttribStructure.pszObjId
                        #if ($AttribStructure.cValue -gt 1) {Write-Host more}
                        $blob = [Runtime.InteropServices.Marshal]::PtrToStructure($AttribStructure.rgValue, [PKI.Structs.Wincrypt+CRYPTOAPI_BLOB])
                        $bytes = New-Object byte[] -ArgumentList $blob.cbData
                        [Runtime.InteropServices.Marshal]::Copy($blob.pbData,$bytes,0,$blob.cbData)
                        $attrib.RawData = $bytes
                        $attribs += $attrib
                        $rgAttribute = [int]$rgAttribute + [Runtime.InteropServices.Marshal]::SizeOf([PKI.Structs.Wincrypt+CRYPT_ATTRIBUTE])
                    }
                }
                $entry.Attributes = $attribs
                
                $entries += $entry
                $rgCTLEntry = [int]$rgCTLEntry + [Runtime.InteropServices.Marshal]::SizeOf([PKI.Structs.Wincrypt+CTL_ENTRY])
            }
        }
        $rgExtension = $CTLInfo.rgExtension
        if ($CTLInfo.cExtension -ge 1) {
            $Exts = New-Object Security.Cryptography.X509Certificates.X509ExtensionCollection
            for ($n = 0; $n -lt $CTLInfo.cExtension; $n++) {
                $ExtEntry = [Runtime.InteropServices.Marshal]::PtrToStructure($rgExtension,[PKI.CRL+CERT_EXTENSION])
                [IntPtr]$rgExtension = [PKI.CRL]::CertFindExtension($ExtEntry.pszObjId,$CTLInfo.cExtension,$CTLInfo.rgExtension)
                $pByte = $ExtEntry.Value.pbData
                $bBytes = $null
                for ($m = 0; $m -lt $ExtEntry.Value.cbData; $m++) {
                    [byte[]]$bBytes += [Runtime.InteropServices.Marshal]::ReadByte($pByte)
                    $pByte = [int]$pByte + [Runtime.InteropServices.Marshal]::SizeOf([byte])
                }
                $ext = New-Object Security.Cryptography.X509Certificates.X509Extension $ExtEntry.pszObjId, @([Byte[]]$bBytes), $ExtEntry.fCritical
                [void]$Exts.Add($ext)
                $rgExtension = [int]$rgExtension + [Runtime.InteropServices.Marshal]::SizeOf([PKI.CRL+CERT_EXTENSION])
            }
            $CTL.Extensions = $Exts
        }
        $ctl.Entries = $entries
        $ctl
        [void][PKI.Crypt32]::CertFreeCTLContext($CTLContext)
    } else {Write-Error -Category InvalidData -ErrorId InvalidDataException -Message "The data is not valid CTL."}
}

И вот, как примерно выглядит вывод:

[↓] [vPodans] $raw = [io.file]::ReadAllBytes(".\Desktop\authroot.stl") [↓] [vPodans] $ctl = Get-CTL $raw [↓] [vPodans] $ctl Version : 1 SubjectUsage : {Root List Signer} SequenceNumber : 1401ccf1d2032b8314 ThisUpdate : 23.02.2012 4:22:38 SubjectAlgorithm : System.Security.Cryptography.Oid Entries : {System.Security.Cryptography.X509Certificates.X509CTLEntry, System.Security.Cryptography.X509Certif icates.X509CTLEntry, System.Security.Cryptography.X509Certificates.X509CTLEntry, System.Security.Cry ptography.X509Certificates.X509CTLEntry...} Extensions : Handle : 40415264 RawData : [↓] [vPodans]

В свойстве Entries находится массив элементов, каждый из которых представляет собой хеш и набор атрибутов конкретного сертификата:

[↓] [vPodans] $ctl.Entries.Length 347 [↓] [vPodans] $ctl.Entries[0] Thumbprint Attributes Certificate ---------- ---------- ----------- CDD4EEAE6000AC7F40C3802C171E30148030... {System.Security.Cryptography.Oid, S... 0 [↓] [vPodans] $ctl.Entries[0].Attributes OID RawData --- ------- System.Security.Cryptography.Oid {4, 14, 48, 12...} System.Security.Cryptography.Oid {4, 16, 240, 196...} System.Security.Cryptography.Oid {4, 20, 14, 172...} System.Security.Cryptography.Oid {4, 32, 136, 93...} System.Security.Cryptography.Oid {4, 74, 77, 0...} [↓] [vPodans]

В X509CTLEntry я добвил свойство Certificate, которое в идеале (сейчас там всегда нули) должно содержать ссылку на сертификат (хэндл). Но я пока не придумал, как это лучше сделать. Дело в том, что в Windows Vista и выше, в контейнере Third-Party CAs установлено всего несколько сертификатов. Остальные доставляются в хранилище из Windows Update или crypt32.dll по требованию. Дополнительно, здесь есть ещё 2 проблемы, которые следует решить:

1) В структуре CTL_CONTEXT у нас есть свойство hCertStore, которое содержит хэндл на хранилище сертификатов (враппер — X509Store). Но что-то сертификатов я там не нашёл.

2) Можно распотрошить ресурсы crypt32.dll (сертификаты там хранятся в виде сериализированного хранилища) и достать оттуда список всех доверенных сертификатов (хотя, он может быть не совсем актуальным). Беда в том, что если надёргать хэндлы из этих сертификатов, они сразу станут недействительными, как только закроете serialized store. Т.е. скорее всего для каждого сертификата придётся дуплицировать хэндлы и как-то их потом очищать.

Вобщем, тут есть над чем подумать. Нарпример, можно доставить форматтер, который бы отформатировал атрибуты сертификатов во что-то читабельное (сейчас они хранятся только в виде массива байтов). Можно попробовать прикрутить CryptFormatObject. Но это уже частности, потому что это всё-таки, только прототип для CTL в PowerShell.

Saturday, March 31, 2012 2:39:43 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

«Older Posts  · 

All content © 2008 - 2012, Vadims Podāns
"Spaces" Theme provided by: Vadims Podāns
About


E-mail - Send mail to the author(s)
Live Messenger -
For english language visitors
Библиотека
Календарик
<May 2012>
SunMonTueWedThuFriSat
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

Карта расположения посетителей
Favorites





Fan list



Disclaimer
Вся информация на сайте предоставляется на условиях «как есть», без предоставления каких-либо гарантий и прав.

При использовании материалов c данного сайта ссылка на оригинальный источник обязательна.
Protected by Copyscape Online Plagiarism Scanner