Вот и пришло время сделать сводный пост по своим исследованиям Software Restriction Policies. Здесь я опубликую ссылки на предыдущие материалы, посвящённые этой действительно интересной технологии. К сожалению это скорее всего будет означать завершение срывов покровов в этом направлении. Безусловно, технологии не стоят на месте, а постоянно развиваются, поэтому очень долго топтаться на одном месте не стоит. Но это совсем не означает, что SRP стал реликвией, музейным экспонатом и просто утилем. SRP до сих пор имеет и какое-то время будет иметь важное значение в обеспечении безопасности ОС Windows, поскольку теперь политики SRP поставляются во все домашние редакции Windows 7. А так же будет неотъемлемой частью обеспечения безопасности уже активно используемых систем начиная с Windows XP.

И, собственно, сам список.

Опубликованная в журнале «Системный администратор» статья даст представление о том, что такое SRP, поведает о ключевых особенностях этих политик и может выступать в качестве теоретической базы для работы с SRP.

Первая часть исправлений к журнальной статье, в которой рассказывается про особенности использования прямых «/» и обратных «\» слешей в правилах пути, а так же про обход политик через системные папки, которые разрешены пользователям для записи.

В этой части рассказаны методы обхода SRP на примере запуска CHM файлов и потенциальной возможности запуска некоторого кода с использованием файлов с расширением SCF. Плюс даны некоторые рекомендации по организации правил для типовой рабочей станции и косметические настройки обработки групповых политик в отношении SRP.

Очередной обзор нескольких способов обхода политик SRP путём подмены системных переменных окружения, использованием в обработке расширений LNK и подстановкой нелегитимных доверенных издателей (цифровых подписей). Так же приведён потенциальный полувандалистский метод атаки на SRP путём генерации зашедуленного задания в ожидании, когда политика будет отключена в профилактических целях.

В этом посте детально рассказано о непростом порядке сортировки и применения множества правил SRP. Используя этот материал вы научитесь разрешать конфликты в правилах и упростит планирование правил SRP.

И ещё один метод обхода политик SRP через шорткаты и обнаружение второй достаточно серьёзной уязвимости SRP, которую закрыть достаточно сложно.

содержит официальное заявления сотрудника техподдежки Microsoft — Rahul Denkanikota. В результате двухмесячного общения с техподдержкой мне лично была предложена замена для mshta.exe, которая уже лишена недостатка оригинального файла. Однако, данное решение не планируется выпускать в массы по причине очень большой специфики. В остальных же случаях, если это возможно, следует полностью отказаться от mshta.exe и заблокировать его на уровне правил SRP.

В этом посте опубликовано решение, которое позволяет защитить SRP от подстановки ложных сертификатов цифровых подписей. В целом, с некоторыми оговорками, после данного поста репутация SRP вновь восстановлена до уровня «надёжно».

Вот и всё… amen

Monday, September 28, 2009 10:45:20 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Примечание: данный материал публикуется как обязательный для знания IT-специалистами, которые занимаются или только собираются заниматься темой PKI (Public Key Infrastructure).

Введение

На самом деле эта тема тесно переплетается с фундаментальными основами PKI, как доверие сертификатов, защита от подделки цифровых подписей сертификатов, отзыв сертификатов и т.д. Иными словами, сама модель PKI лежит на Certificate Chaining Engine (механизм построения цепочек сертификатов). Миллионы людей в мире видят результат работы этого механизма ежедневно, но не знают, как он работает. Им это, в принципе, и не надо, а вот IT-специалистам по PKI это знать просто необходимо.

Одной из основ PKI является модель доверия центрам сертификации (Certification Authority или просто CA). Прежде чем мы начнём доверять сертификату мы должны явно доверять корневому сертификату CA и так же явно или косвенно (по правилу одностороннего транзитивного доверия) доверять всем промежуточным CA в цепочке. Корневые сертификаты устанавливаются в систему вручную путём добавляения сертификата CA в секцию Trusted Root CAs. Если корневой сертификат CA есть в этом списке, то мы доверяем всем сертификатам, которые выдал этот CA и любые подчинённые CA (Intermediate CA).

В качестве наиболее доступного и понятного примера можно рассмотреть SSL веб-сайт. Если мы зайдём на https://www.dreamspark.com, то мы увидим что с SSL сертификатом этого сайта всё в порядке. Если же мы зайдём на https://cert.startcom.org, то мы наоборот увидим грозное предупреждение, что с SSL сертификатом этого сайта что-то не так. Это как раз и есть результат работы Chaining Engine. А вот что он сделал на самом деле — вот об этом мы сейчас и поговорим.

Постройка цепочки сертификатов

Давайте сначала посмотрим на путь сертификатов (цепочки сертификатов) обоих веб-сайтов:

 Valid certification path   Invalid certification path with untrusted root

В первом случае цепочка сертификатов заканчивается на сертификате VeriSign, которому мы доверяем явно. Вы можете убедиться в этом нажав кнопку View Certificate, чтобы посмотреть его внутренности и найти этот же сертификат в оснастке certmgr.msc –> Trusted Root CAs. Однако, вы видите, что не корневой CA выдал сертификат сайту, а подчинённый (Intermediate CA). Это доказывает то, что если мы доверяем корню, то и явно или косвенно доверяем всем сертификатам этого CA или любым его прямым или косвенным промежуточным CA. Если говорить простым языком, то в сертификатах используется одностороннее транзитивное доверие сертификатам CA. Чуть позже будет рассказано как строится эта цепочка. Во втором случае мы видим, что корневой сертификат CA не находится у нас в Trusted Root CAs и, следовательно, мы не доверяем ни одному сертификату, который издал этот CA или любой другой его подчинённый CA. И, так же как и в первом случае, сертификат самому веб-сайту выдал подчинённый CA. На самом деле этот момент является одной из наиболее частых ошибок системных администраторов, которые организовывают доступ к ресурсам с использованием цифровых сертификатов.

Но нас теперь интересует другой вопрос — а откуда же взялись все эти сертификаты в цепочке? Chaining engine как правило использует только 3 метода построения цепочки:

  • по полю AIA (Authority Information Access) каждого сертификата
  • с использованием файла цепочки сертификатов в формате pkcs7
  • поиск подходящих сертификатов в локальном хранилище (с использованием exact, key, name matches, о которых будет рассказно ниже)

Примечание: в сертификате любое поле содержащее слово Authority будет означать какие-то сведения о сертификате вышестоящего CA. Например, Authority Key Identifier — комбинация серийного номера, поля Subject или хеша открытого ключа вышестоящего CA. А любое поле содержащее слово Subject — означает то же самое, но применительно к конкретно этому сертификату.

Давайте посмотрим поле AIA первого сертификата:

[1]Authority Info Access
     Access Method=Certification Authority Issuer (1.3.6.1.5.5.7.48.2)
     Alternative Name:
          URL=
http://www.microsoft.com/pki/mscorp/Microsoft%20Secure%20Server%20Authority(5).crt

В этом поле содержится ссылка на сертификат того CA, который непосредственно издал и подписал этот сертификат. Если нажать по этой ссылке, то вы скачаете сертификат центра сертификации Microsoft Secure Server Authority. В этом сертификате тоже будет поле AIA, по ссылке из которого скачается сертификат вышестоящего CA — Microsoft Internet Authority. И так проверка проводится далее до тех пор, пока цепочка не оборвётся или не закончится каким-либо корневым CA (корневой CA характеризуется самоподписанным сертификатом, т.е. сертификат выдан самому себе). Если вы посмотрите сертификат Microsoft Internet Authority, то вы в нём не обнаружите поля AIA. И вот здесь мы сталкиваемся со вторым методом добычи сертификатов в цепочке – файл сертификатов формата pkcs7.

Бывают случаи, когда цепочка сертификатов не может быть построена и она обрывается на пути. Типичный пример такого обрыва (когда цепочка не может быть построена до корня с использованием любого вышеуказанного метода) можно посмотреть на сайте БиЛайна: https://trust.beeline.ru

 Invalid certificate chain. Root certificate s unreachable

если посмотреть поле AIA этого сертификата, то мы увидим там 2 пути до CA, который издал этот сертификат. Но оба эти пути нерабочие совсем (кстати говоря, пути в CDP там тоже нерабочие. Причём, во всех сертификатах! Вот такой он БиЛайн :) ). Но цепочка сертификатов в этом случае всё равно построилась за счёт сертификатов в pkcs7 формате. Хотя корневой сертификат в этом pkcs7 просто отсутствует, поэтому мы даже не можем собрать корневой сертификат. Неработающие CDP/AIA — ещё одна из наиболее частых ошибок реализации инфраструктуры PKI, в результате которой по сути PKI является неработоспособной.

Оффтоп: если на этой странице нажать Продолжить, то на новой странице будет ссылка на корневой сертификат, а так же на CRL. CRL БиЛайна доставил очень сильно. Вы посмотрите срок годности этого CRL :)

Проверка соответствия сертификатов внутри построенной цепочки

Однако, это только половина дела. Мы просто построили цепочку сертификатов, но не более того. Поскольку сертификаты могут быть скачаны в pkcs7 формате, а по ссылкам AIA просто подменить сертификаты, chaining engine делает полнуюю проверку доверия каждого сертификата в цепочке, чтобы исключить возможность подмены сертификатов, а так же достраивает цепочку (если этого не удалось сделать с помощью AIA и pkcs7 используя локальное хранилище сертификатов). Соответствие сертификатов внутри цепочки может происходить по нескольким правилам (в порядке приоритета):

  • Exact Match
  • Key Match
  • Name MAtch

Для этого chaining engine использует следующие поля сертификатов:

  • Authority Key Identfier (AKI) — может содержать комбинацию поля Subject и Serial Number сертификата вышестоящего CA или хеш открытого ключа вышестоящего CA (но бывает, что это поле совсем отсутствует).
  • Issuer – данное поле используется только если AKI отсутствует в сертификате и это поле должно быть эквивалентно полю Subject вышестоящего CA. По этому полю определяется имя CA, котороый издал этот сертификат.
  • Subject в сертификате вышестоящего CA. Данное поле должно быть эквивалентно полю Issuer в издаваемых сертификатах.
  • Serial Number сертификата вышестоящего CA.
  • Subject Key Identifier (SKI) — может содержать комбинацию поля Subject и Serial Number текущего сертификата или хеш открытого ключа текущего сертификата.

Exact match

Данный метод определения правильности построения цепочки является наиболее точным и такая цепочка почти никогда не будет заканчиваться более чем одним корневым сертификатом, поскольку требует совпадения 2-х полей вышестоящего сертификата – Subject и серийного номера с полем AKI проверяемого сертификата. Давайте посмотрим пример такой проверки в сертификате сайта https://cert.startcom.org. Поле AKI содержит следующую информацию:

KeyID=a1 e1 9e 45 25 79 4d 06 d9 02 17 92 82 d5 30 89 72 25 14 a0
Certificate Issuer:
     Directory Address:
          CN=StartCom Certification Authority
          OU=Secure Digital Certificate Signing
          O=StartCom Ltd.
          C=IL
Certificate SerialNumber=17

Поле KeyID не используется. Здесь мы видим сведения о поле Subject и серийный номер вышестоящего CA. Если посмотреть сертификат этого CA, то его серийный номер и поле Subject будут идентичными тем, которые записаны в поле AKI издаваемых ими сертификатов. Используя эти данные chaining engine может найти нужный сертификат в локальном хранилище (если такой сертификат там есть), для восполнения недостающих звеньев цепочки. Если же будет обнаружено хоть одно несоответствие между этими полями, то цепочка мгновенно обрывается и любому сертификату в этой цепочке (начиная от сертификата с несоответствующими полями и вниз до конечного сертификата выданного потребителю) будет отказано в доверии.

Key Match

Это наиболее распространённый тип проверки цепочки, т.к. зачастую поле AKI у сертификатов содержит только KeyID, который является лишь хешом открытого ключа вышестоящего CA. Например сертификат с https://www.dreamspark.com:

KeyID=14 55 c4 39 e0 3d 2e d1 55 2e 48 96 b0 d8 7e 14 22 06 93 bc

Этот хеш должен быть идентичным хешу, который записан в поле Subject Key Identifier вышестоящего CA. В этом вы можете убедиться просмотрев сертификат CA, который выдал сертификат для dreamspark.com. В случае несоответствия этого поля, любому сертификату вниз по цепочке будет отказано.

Заметка: при этом, возможна достаточно интересная ситуация, когда цепочка может заканчиваться несколькими корнями. Это может происходить если корневой сертификат CA был обновлён с использованием текущей пары открытого и закрытого ключа. Поскольку KeyID — всего лишь хеш открытого ключа, то при смене корневого сертификата CA, хеш не изменится и, следовательно, chaining engine может рандомно строить цепочку до старого или до нового корневого сертификата CA.

Name match

Но поле AKI не обязательно должно присутствовать в сертификате. Его может не быть совсем и тогда остаётся только один способ проверки целостности цепочки — сравнение поля Issuer текущего сертификата и поля Subject вышестоящего сертификата. Это самый крайний и наименее надёжный (хотя это достаточно относительно) метод проверки цепочки. Так же, как и предыдущий пример, данный метод может заканчиваться несколькими корневыми сертификатами, если сертификат CA был обновлён с использованием текущей пары открытого и закрытого ключей. Но этого не произойдёт, если при обновлении сертификата CA будут использоваться новая пара ключей. Хоть хеши и серийные номера издающих CA не проверяются, то подделка сертификатов в вашем случае закончится провалом. А об этом читайте заключительный раздел.

Не забываем про подписи

На самом деле может показаться, что обмануть chaining engine достаточно легко — надо лишь подсунуть нужные поддельные сертификаты. Но в действительности это не так. Помимо всего прочего следует помнить, что каждый сертификат подписан цифровой подписью издающего CA. Если вы ещё не знаете, что такое цифровая подпись, то пора бы и узнать. Когда CA составляет сертификат, то он высчитывает конечный хеш (с использованием алгоритма указанного в поле Signature algorithm) этого сертификата и шифрует его своим закрытым ключом и пристыковывает подпись к сертификату. При проверке сертификата chaining engine удаляет цифровую подпись из сертификата и подсчитывает сам хеш файла сертификата. Далее берёт открытый ключ у издающего CA и сравнивает полученный хеш с тем, что содержится в подписи. Следовательно, если вам удастся обмануть все 3 метода проверки цепочки путём подмены сертификатов, то вы сломаетесь на проверке цифровой подписи, т.к. здесь уже задействуется закрытый ключ легального CA. Вот как это может выглядеть:

Altered certificate certification path Altered certificate General tab

На картинке слева вы видите, что chaining engine построил цепочку до доверенного корня, поскольку мой сертификат содержал нужные поля для работы метода Name match. Но сертификат был выдан не тем CA, который виден в цепочке, а поддельным CA. Хоть мой поддельный CA внешне не отличим от легитимного CA, но у него нету самого важного — правильного закрытого ключа, чтобы подписать сертификат. Ведь при проверке подписи используется открытый ключ легитимного CA и, следовательно, подпись не может быть проверена.

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

Wednesday, September 16, 2009 11:39:21 PM (FLE Daylight Time, UTC+03:00)   Comments [5]    

 

Представлю на обозрение пробный скрипт по управлению сертификатами в хранилище Certificate Store — CertMgmtPack.ps1, который (как я надеюсь) может найти применение на серверах Windows Server 2008 R2 Server Core и реализован на основе наших недавних исследований:

Версия пока что 0.47 (т.к. предстоит добавить ещё разного функционала и допилить существующий). Пока что мне удалось реализовать функционал импорта сертификатов из файлов в store и экспорт из store в файлы сертификатов.

Примечание: скрипт работает только в PowerShell V2. В каждую функцию вложена справка, которую вы можете прочитать набрав Get-Help Import/Export-Certiifcate.

После подключения скрипта (используя dot-sourcing) у вас будут доступны 2 функции:

  • Export-Certificate
  • Import-Certificate

И пару слов о том, как их использовать.

Import-Certificate

содержит следующие параметры:

  • Path <String> — путь к файлу сертификата, который может иметь следующий набор расширений: CER, DER, PFX, P7B, SST. Распознавание типов сертификатов осуществляется по расширению файла. Данная функция может принимать параметр пути из конвейера следующим путём:
    dir *.cer | Import-Certificate
    Параметр обязательный.
  • Password <SecureString> — пароль для PFX файла. Пароль не требуется для остальных типов файлов. Однако, хочу предупредить, что пароли не принимаются в открытом виде, а должны передаватьсякак SecureString. Это можно реализовать различными способами, например, вот так:
    Read-Host "Enter password for PFX file" –AsSecureString
  • Storage <String> — указывает контекст импорта сертификата, т.е. это будет хранилище текущего пользователя (по умолчанию) или в хранилище компьютера (требуются права локального администратора). Может принимать значения User или Computer. Если параметр не указан, то берётся значение по умолчанию — User.
  • Container <String> — указывает контейнер размещения сертификата. Это может быть один из: AuthRoot, CA, Disallowed, My (по умолчанию), REQUEST, Root, SmartCardRoot, Trust, TrustedPeople, TrustedPublisher, UserDS. Расшифровка контейнеров (сопоставление названиям в оснастке CertMgr.msc) приведена внутри скрипта в хелпе функции. Если параметр не указан, то применяется параметр по умолчанию — My.
  • Exportable <Switch> — применяется только для импорта PFX файлов. Данный ключ помечает импортируемый сертификат как экспортируемый. Это означает, что вы после процедуры импорта сможете снова экспортировать сертификат с закрытым ключом. Эсли ключ Exportable не указан, то закрытый ключ сертификата не помечается как экспортируемый и при экспорте у вас не будет возможности экспортировать закрытый ключ данного сертификата.
  • StrongProtection <Switch> — применяется только для импорта PFX файлов. Данный ключ включает режим усиленной защиты закрытого ключа PFX файла. Это означает, что для каждой попытки использования закрытого ключа от этого сертификата потребуется ручной ввод пароля. Данный ключ нельзя использовать, если Storage указан как Computer, поскольку компьютерные сертификаты не поддерживают такой режим. Это связано с тем, что компьютерные сертификаты используются в контексте учётной записи LocalSystem и lsass.exe не предоставляет пользователю UI для ввода пароля. В любом случае, если вы попробуете это сделать, скрипт выдаст соответствующую ошибку и импорт произведён не будет.

И пару примеров, как использовать функцию:

$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.p7bStorage 'Computer'Container 'TrustedPublisher'

Импортирует все сертификаты из файла certs.p7b в контейнер Trusted Publishers компьютерного хранилища.

Экспорт сертификатов

  • Path <String> — указывает путь к папке (не к файлу!), в которую будут экспортированы все указанные сертификаты. Парамер обязателен.
  • Type <String> — указывает тип экспортируемых сертификатов. Может принимать одно из значений: CERT, PFX, PKCS12, PKCS7, SST (Serialized Store). Параметр так же обязателен.
  • Password <SecureString> — задаёт пароль для экспортируемых PFX файлов (в качестве типа экспортируемых сертификатов указано PFX или PKCS12 и является для них обязательным). Параметр не принимает значение в виде простой строки, а только SecureString.
  • Storage <String> — указывает контекст поиска экспортируемых сертификатов, т.е. поиск будет производиться в компьютерном хранилище или хранилище текущего пользователя (по умолчанию). Может принимать значения CurrentUser или LocalMachine. Если параметр не указан, то берётся значение по умолчанию — User.
  • Container <String> — указывает контейнер размещения сертификата (или сертификатов). Это может быть один из: AuthRoot, CA, Disallowed, My (по умолчанию), REQUEST, Root, SmartCardRoot, Trust, TrustedPeople, TrustedPublisher, UserDS. Расшифровка контейнеров (сопоставление названиям в оснастке CertMgr.msc) приведена внутри скрипта в хелпе функции. Если параметр не указан, то поиск производится в контейнере по умолчанию — My.
  • Thumbprint <String> — задаёт критерий поиска сертификатов по отпечатку ключа (соответствующее поле Thumbprint сертификата). Можно указывать как полный отпечаток, так и любую его часть.
  • Subject <String> — задаёт критерий поиска сертификатов по полю Subject. Можно указывать как полный Subject, так и любую его часть.
  • Issuer <String> — задаёт критерий поиска по конкретному издателю сертификата. Можно указывать как полный CN издателя, так и любую его часть.
  • SerialNumber <String> — задаёт критерий поиска по серийному номеру сертификата. Можно указывать как точный серийный номер, так и любую его часть.
  • NotAfter <String> — задаёт критерий поиска по дате истечения сертификата. Дата задаётся в формате dd.MM.yyyy (день.месяц.год). В результате использования этого параметра будут отобраны сертификаты, которые истекут не позднее указанной даты.
  • NotBefore <String> — задаёт критерий поиска по дате начала действия сертификате. Как и в NotAfter дата указывается в формате dd.MM.yyyy и в результате использования этого параметра будут отобраны сертификаты, действие которых начинается не раньше указанной даты.
  • DeleteKey <Swtich> — используется только при экспорте сертификатов вместе с закрытыми ключами в PFX файлы. Данный ключ при успешном экспорте удаляет закрытый ключ данного сертификата из хранилища.

Я не смог провести полноценное тестирование функции экспорта, поэтому гарантировать 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 может содержать недопустимые символы для имён файлов, мне скорее всего придётся искать другой метод именования экспортируемых сертификатов и чтобы они были распознаваемые. Если вы обнаружите какие-то ошибки или будут пожелания, то пишите мне в коментарии или на почту. По мере дописывания я буду выкладывать новые версии скрипта.

Monday, September 14, 2009 10:02:32 PM (FLE Daylight Time, UTC+03:00)   Comments [2]    

 

Короткая заметка. Когда вы экспортируете сертификат вместе с закрытым ключом, то можете заметить такую опцию:

Delete the Private key if the export is successful

К сожалению, метод Export() у объектов X509Certificate2 не позволяет штатно проделывать данную операцию. Это можно сделать через CAPICOM.PrivateKey.Delete(), но данная возможность отсутствует в PowerShell, в то время, как вы можете это сделать в VBS. Это связано с весьма паршивой поддержкой COM со стороны PowerShell, поэтому для реализации функционала этой галочки вам придётся проделать следующие шаги:

  1. Получить объект X509Certificate2 самого сертификата;
  2. Экспортировать его в PFX;
  3. Открыть хранилище в режиме ReadWrite;
  4. Удалить данный сертификат из хранилища;
  5. Экспортировать объект сертификата уже не в PFX, а в Cert;
  6. Импортировать этот экспортированный объект Cert (хотя, на самом деле там будет массив байтов, но это не столь существенно).

На языке 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 файл создался и он валидный. В противном случае вы останетесь совсем без закрытого ключа. И ещё раз напоминаю, при работе с хранилищем – не забывайте его закрывать после работы.

Thursday, September 10, 2009 10:07:54 PM (FLE Daylight Time, UTC+03:00)   Comments [2]    

 

В предыдущих статьях мы с вами уже изучили методы экспорта сертификатов из хранилища Certificate Store в файл:

а так же рассмотрели сопутствующие вопросы. Теперь же предлагаю рассмотреть обратную операцию – добавление сертификатов в cert store из файлов. Этот процесс будет несколько легче, чем экспорт, поэтому я в одном посте покажу импорт всех типов сертификатов (они все используют одну схему).

Для импорта нам потребуется метод x509Certificate.Import(), который так же как и метод Export() имеет несколько конструкторов. Мы можем выбрать один универсальный, который подойдёт нам для всех типов файлов сертификатов и это будет конструктор: X509Certificate2.Import Method (String, SecureString, X509KeyStorageFlags). В качестве аргументов он принимает следующее:

  • String – путь к файлу сертификата
  • SecureString – строка с паролем. Требуется только для импорта PFX сертификатов, т.к. остальные типы файлов не содержат закрытых ключей и защищать их паролем нет смысла. Если это не PFX, то значение этого аргумента просто выставляется в $null.
  • объект типа X509KeyStorageFlags, который описывает область хранения сертификатов.

Давайте немного разберёмся с флагами последнего аргумента:

  • DefaultKeySet – указывает, что сертификат предназначен для импорта в контейнер Personal хранилища CurrentUser (дефолт).
  • Exportable – используется только для PFX файлов и говорит, будет ли возможность экспортировать сертификат с закрытым ключом. Если мы этот флаг не указываем, то после импорта не будет возможности снова экспортировать в PFX файл.
  • MachineKeySet – указывает, что сертификат будет установлен в хранилище компьютера (LocalSystem).
  • PersistKeySet – используется только для PFX и сохраняет пароль от PFX файла в сертификате. Данный пароль будет использован при дальнейшем экспорте в PFX файл (будет работать только с флагом Exportable).
  • UserKeySet – указывает, что сертификат будет установлен в пользовательское хранилище.
  • UserProtected – используется только для PFX файлов и для пользовательских сертификатов. Этот флаг включает Private Key Strong Protection, который при каждом использовании закрытого ключа будет требовать ввода пароля.

Примечание: вы никогда не должны использовать флаг 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.

Спасибо за внимание :)

Wednesday, September 09, 2009 10:20:18 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Продолжая тему экспорта сертификатов поговорим о более сложных вещах, чем простой экспорт сертификата из хранилища в CER/DER файл. На сей раз расскажу про экспорт сертификатов из хранилища в PFX (Personal Information Exchange Syntax Standard) и PKCS#7 (Cryptographic Message Syntax Standard) формат. Будет немного треша, но в пределах разумного.

Для экспорта сертификата в PFX нам потребуется выполнить половину кода из предыдущей статьи: Простой экспорт сертификатов в PowerShell.

Сначала выберем объект сертификат из хранилища:

$cert = (dir cert:\currentuser)[4]

Как вы помните, если экспортировать сертификат в PFX нам нужно указывать пароль:

Certificate Export Wizard windows

Следовательно, конструктор метода 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. А на сегодня, пожалуй, хватит.

Monday, September 07, 2009 11:28:36 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

С различной периодичностью на ньюсгруппах и англоязычных форумах встречаю вопрос, как экспортировать сертификаты из хранилища (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 хранилища

  • CurrentUser – представляет хранилище текущего пользователя, т.е. является аналогом графической консоли CertMgr.msc
  • LocalMachine – представляет хранилище локального компьютера и является аналогом графической консоли MMC – Certificates, запущенной в контексте LocalComputer.

Внутри уже видны контейнеры, которые мы видим в виде папок в 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 тысяч элементов). Это же и указано в описании метода, что он возвращает. Именно массив байтов. Но что с ним делать? Я сам по-началу думал об этом, пока не вспомнил про одну вещь:

Certificate Export Wizard

У нас есть выбор, как экспортировать сертификат – в DER или Base64 кодировке. Когда я писал про PsFCIV, то у меня была такая же ситуация. Hasher у меня так же возвращал массив байтов, который мы сразу же загоняли в Base64 кодировку. В нашем случае потребуется то же самое:

$base64Data = [System.Convert]::ToBase64String($ExportedData)

И на выходе мы молучим длинную Base64 строку, которую уже и нужно экспортировать в файл любым удобным для вас методом:

$base64Data > path\file.cer
Set-Content -Path path\file.cer -Value $base64Data

вы можете убедиться в этом просто открыв .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:

Export Certificate Context menu

и по нажатию на него цифровая подпись будет экспортирована в MyDocs\FileName.ext.cer, где FileName.ext – оригинальное имя файла и к нему просто пристыковывается расширение CER. В скрипте предусмотрены несколько диалоговых окон о состоянии сертификата:

  • файл подписан и хеш файла соответствует хешу в цифровой подписи:

Valid Certificate

  • файл подписан, но хеш файла не соответствует хешу в цифровой подписи:

Hash Mismatch

  • файл не содержит цифровых подписей:

No certificate

В двух последних случаях сертификат не экспортируется никуда по очевидным причинам. И, собственно скрипты-инсталляторы этого контекстного меню с рабочим кодом для:

  • PowerShell 1.0:
  • PowerShell V2:

В следующих статьях я планирую поговорить про импорт сертификатов в Certificate Store, а так же и экспорт сертификатов с закрытыми ключами в PFX файл с использованем Windows PowerShell.

Sunday, September 06, 2009 12:46:46 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Иногда бывает необходимость проверить, заблокирован ли файл каким-то приложением в монопольном режиме, прежде чем совершать какое-то действие над ним. Например, мой скрипт Get-PsFCIV.ps1 очень сильно ругается краснотой, если файл заблокирован, т.к. System.IO.StreamReader, который у меня используется для подсчёта хешей файлов требует, чтобы файл никем не был занят. Принцип проверки файла заключается в том, что мы пробуем получить доступ к файлу через System.IO.StreamReader и если это нам не удалось, значит файл заблокирован. Вот и решил написать такой удобный фильтр, который можно использовать и отдельно, просто для проверки блокировки файлов:

filter Test-FileLock {
    # проверяем, откуда пришли данные - из конвейера или через аргументы фильтра
    # и создаём объекты текущего элемента
    if ($args[0]) {$filepath = gi $(Resolve-Path $args[0]) -Force} else {$filepath = gi $_.fullname -Force}
    # отфильтровываем папки
    if ($filepath.psiscontainer) {return}
    # задаём переменную $locked в исходное значение $false
    $locked = $false
    # создаём ловушку для ошибки доступа. Если ловушка сработает, то переменная 
    # $locked выставляется в $true, что будет означать, что файл заблокирован
    # в монопольном режиме каким-то приложением
    trap {
        Set-Variable -name locked -value $true -scope 1
        continue
    }
    # открываем файл в режиме read/write. Если файл заблокирован, то ошибка будет
    # поймана ловушкой
    $inputStream = New-Object system.IO.StreamReader $filepath
    # если файл не был заблокирован, то закрываем StreamReader
    if ($inputStream) {$inputStream.Close()}
    # выводим результаты на экран в виде хеш-таблицы
    @{$filepath = $locked}
}

фильтр можно запускать как с аргументами, так и в конвейере:

[↓] [vPodans] Test-FileLock ntuser.dat Name Value ---- ----- C:\Users\vPodans\ntuser.dat True [↓] [vPodans] Test-FileLock ntuser.ini Name Value ---- ----- C:\Users\vPodans\ntuser.ini False [↓] [vPodans] dir ntuser.dat -Force | Test-FileLock Name Value ---- ----- C:\Users\vPodans\ntuser.dat True [↓] [vPodans] dir ntuser.ini -Force | Test-FileLock Name Value ---- ----- C:\Users\vPodans\ntuser.ini False [↓] [vPodans]

надеюсь, этот фильтр кому-нибудь сгодится ещё :)

Saturday, September 05, 2009 10:46:15 PM (FLE Daylight Time, UTC+03:00)   Comments [0]    

 

Одной из самых больших проблем с Software Restriction Policies была настройка доверенных издателей (Trusted Publishers), о которой я уже писал: Секреты Software Restriction Policies (часть 3). Другое, более элегантное решение лежало практически на поверхности, но я его никак не замечал. В кратце напомню проблематику:

Если у нас есть недозволенное подписанное приложение, то мы можем импортировать сертификат цифровой подписии в секцию Trusted Publishers для доверия конкретному издателю. Но кроме этого нам нужно обеспечить доверие сертификату подписи, путём размещения корневого сертификата в цепочке в секцию Trusted Root Certification Authorites пользовательского хранилища (User Store). Чтобы как-то защититься от этого, приходилось в политике SRP запрещать пользователям добавлять сертификаты цифровых подписей в секцию Trusted Publishers (делая её read-only). Это спасало от нечистых на руку пользователей, которые могли добавлять свои сертификаты и обходить политику. Однако, этот метод имел серьёзные негативные эффекты: переставал работать Windows Update на Windows XP/2003, останавливалась служба Windows Defender, невозможно было использовать продукты Live и др.

Сегодня блуждая по политикам наткнулся на ещё одну настройку, которая запрещает доверять сертификатам в пользовательском контейнере Trusted Root CAs! Для этого нужно открыть редактор групповой политики и перейти по пути:

Computer Configuration –> Windows Settings –> Security Settings –> Public Key Policies –> выбрать свойства Trusted Root CAs

Trusted Root Certification Authorities settings in Windows Server 2003

и здесь убрать единственную галочку. В Windows Vista/2008 и выше эта настройка выделена в отдельный элемент. Вместо Trusted Root CAs элемент называется Certificate Paths Validation Settings:

Certificate Path Validation Settings in Windows Server 2008 R2

Хоть пути до настроек чуточку отличаются, но суть от этого не меняется. Достаточно убрать эту галочку и получать профит. Так же, как и с паблишерами, секция Trusted Root CAs в пользовательском хранилище становится в режим Read Only. Причём, эта настройка удаляет все корневые сертификаты, которые были добавлены пользователями (т.е. существуют только в пользовательских хранилищах, но не в компьютерном), т.е. администраторам не надо будет заботиться о чистке пользовательских Cert Store, оно будет вычещенно само. Если попытаться импортировать корневой сертификат из файла, то получите вот такой бонус:

Select Certificate Store window

как вы видите, у нас нету больше возможности добавлять свои сертификаты в этот контейнер.

Примечание: данная настройка отсутствует в политике Windows XP

Кстати говоря, данная настройка в Windows 7/Windows Server 2008 R2 так же позволяет избежать атаки на правила издателя (Publisher) AppLocker'а путём подстановки своего корневого сертификата и запуска файлов с поддельной цифровой подписью.

Вот таким нехитрым способом мы можем значительно укрепить SRP и AppLocker от криптографических махинаций с цифровыми подписями. Хотя, это нас не спасёт от случаев, если сертификат подписи выдан публичным CA, которому система доверяет по умолчанию. В таком случае SRP можно защитить только через запрет установки своих паблишеров. А вот AppLocker спасти уже не удастся :'(

з.ы. на днях я ВНЕЗАПНО узнал о существовании такой мега-завальной штеллы как Snipping Tool, который поставляется вместе с Windows Vista и выше для создания скриншотов нужных окон! За 2,5 года использования висты я ни разу его так и не запустил, из-за чего приходилось извращаться через Paint, что было несколько уныло, зато у меня теперь есть удобный инструмент для снятия пруфпиков и ещё один веский повод сказать, что Vista рулит! Rock

Thursday, September 03, 2009 9:59:28 PM (FLE Daylight Time, UTC+03:00)   Comments [3]    

 

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


E-mail - Send mail to the author(s)
Live Messenger -
My former blog -
For english language visitors
Библиотека
Календарик
<September 2010>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

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





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

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