Posts on this page:
Ссылки на другие материалы из этой серии:
Когда-то давно я писал о том, как можно создать самоподписанный сертификат в Windows Vista и более новых системах при помощи интерфейсов CertEnroll: Создание самоподписанного сертификата средствами PowerShell. И недавно для обновления адд-она PowerGUI Script Editor — Script Signing я написал более обновлённый вариант, который использует WinAPI функции. Этот код может работать и в Windows XP.
Итак, основная функция для этого будет функция CertCreateSelfSignCertificate. Но прежде чем её использовать нам надо получить хэндл к криптопровайдеру, который можно получить при помощи CryptAcquireContext. Так же нам понадобятся следующие функции: CryptGenKey для генерации пары ключей, CryptExportPublicKeyInfo для экспорта открытого ключа (нужен будет для подсчёта хеша для расширения Subject Key Identifier), CryptHashPublicKeyInfo для подсчёта хеша открытого ключа, CryptEncodeObject для кодировки стандартного формата данных в нотацию ASN.1. Так же потребуется одна небольшая функция, которая сконвертирует FileTime в SystemTime (этот формат нужен для CertCreateSelfSignCertificate). И функции освобождения памяти: CryptDestroyKey для выгрузки закрытого ключа из памяти, CryptReleaseContext для выгрузки хэндла (контекста) криптопровайдера. В итоге список функций выглядит так:
Самый первый и очевидный вопрос: как добраться до этих функций? И какой будет синтаксис в PowerShell? Существует система, называемая Platform Invocation Service или просто p/invoke. При помощи неё мы можем вызывать неуправляемые функции в управляемом коде и получать соответствующий профит. В первую очередь каждую функцию нужно соответствующим образом объявить. Объявления многих функций и структур можно найти на p/invoke.net. Если там нет, придётся это делать самим. Объявление, как правило, использует следующий синтаксис:
[DllImport("DllFileName.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern ReturnType FunctionName(
DataType ArgumentName;
DataType2 Argument2Name;
DataType3 Argument3Name;
);
Например, вот как выглядит объявление функции CryptReleaseContext в C++:
BOOL WINAPI CryptReleaseContext(
__in HCRYPTPROV hProv,
__in DWORD dwFlags
);
BOOL (Boolean) означает тип возвращаемого значения. Как правило функции WinAPI возвращают Boolean (но не всегда) указывая статус вызова функции, успешно или неуспешно. Далее мы видим объявление аргументов функции. __in означает, что это входной аргумент, __out означает, что это выходной аргумент. Т.е. функция в ходе своего выполнения будет что-то записывать в переменную, которая указана в этом аргументе. __inout означает что этот аргумент может быть как входным, так и выходным. __opt означает опциональный аргумент и может быть выставлен в null.
Теперь более сложное — тип данных. Неуправляемые типы значительно отличаются от привычных типов, как int, string, byte, но можно подобрать соответствующий аналог в .NET. В конкретном примере мы видим, что аргумент hProv имеет тип HCRYPTPROV. Соответствующего аналога в .NET вы не найдёте. Следовательно, здесь можно пойти двумя путями:
Тип DWORD — это 32-разрядное число и имеет соответствующий аналог в .NET — int, который представляет собой тоже 32-разрядное число.
Следовательно объявление этой функции в PowerShell будет иметь следующий вид:
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool CryptReleaseContext(
IntPtr hProv,
int flags
);
Давайте посмотрим на ещё одну функцию:
BOOL WINAPI CryptGenKey(
__in HCRYPTPROV hProv,
__in ALG_ID Algid,
__in DWORD dwFlags,
__out HCRYPTKEY *phKey
);
В данном случае мы можем смело указать первый аргумент: IntPtr hProv. Второй аргумент должен иметь тип ALG_ID. Здесь нам не удастся использовать указатель IntPtr, потому что нам нужно будет явно указывать значение аргумента. Если мы укажем IntPtr, нам придётся создать соответствующую структуру и скопировать её в память, чтобы получить адрес указателя. Структура ALG_ID достаточно проста и состоит из одного 32-разрядного числа. Следовательно мы можем второй аргумент описать как: int Algid. Третий аргумент нам знаком и опишем его как: int dwFlags.
С четвёртым аргументом тоже всё просто, кроме одного момента. Структура HCRYPTKEY нам не нужна и достаточно знать адрес структуры в памяти, чтобы передать этот адрес в другую функцию. Но это выходной аргумент, т.е. при вызове функции аргумент не должен иметь значения и функция в процессе работы запишет данные в эту функцию. В p/invoke все выходные аргументы помечаются как ref (reference) и переменная, которая будет принимать возвращаемое функцией значение должна быть объявлена заранее и может иметь нулевое значение. Для числовых значений нулевое значение будет 0, для строковых — пустая строка "", для указателей — специальное статическое свойство класса IntPtr::Zero и т.д.
Следовательно мы можем объявить функцию:
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool CryptGenKey(
IntPtr phProv,
int Algid,
int dwFlags,
ref IntPtr phKey
);
А как же должна быть объявлена функция CertCreateSelfSignCertificate? Вот её объявление в C++:
PCCERT_CONTEXT WINAPI CertCreateSelfSignCertificate(
__in_opt HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey,
__in PCERT_NAME_BLOB pSubjectIssuerBlob,
__in DWORD dwFlags,
__in_opt PCRYPT_KEY_PROV_INFO pKeyProvInfo,
__in_opt PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
__in_opt PSYSTEMTIME pStartTime,
__in_opt PSYSTEMTIME pEndTime,
__opt PCERT_EXTENSIONS pExtensions
);
Здесь мы видим, что функция возвращает не Boolean, а структуру PCCERT_CONTEXT, которая содержит наш итоговый сертификат. Здесь так же, нам может быть достаточно указателя на адрес памяти, содержащий эту структуру или объявлять структуру и разбирать её по частям. В нашем случае нам хватает указателя на адрес памяти, поскольку у X509Certificate2 есть хороший конструктор: X509Certificate2(IntPtr). Т.е. выдёргивать данные из памяти придётся в любом случае (чтобы увидеть наш сертификат и его свойства), но мы возложим эту задачу на класс X509Certificate2.
В функции используется довольно много структур, которые нам всё-таки придётся объявлять и собирать в коде вручную: CRYPT_KEY_PROV_INFO, CRYPT_ALGORITHM_IDENTIFIER, SYSTEMTIME и CERT_EXTENSIONS. Забегая вперёд, заранее привожу список структур, которые нам потребуются:
Структуры по сути являются классом с набором свойств и имеют следующий формат объявляения:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct StructureName {
public PropertyType PropertyName;
public Property2Type Property2Name;
}
Например, следующая структура (кстати говоря, самая популярная в CryptoAPI) CRYPTOAPI_BLOB объявляется в С++ следующим образом:
typedef struct _CRYPTOAPI_BLOB {
DWORD cbData;
BYTE *pbData;
} CRYPT_INTEGER_BLOB
Звёздочка у свойства pbData означает, что это указатель на адрес в памяти, где находится соответствующий массив байтов. Свойство cbData указывает на размер этого массива в байтах. Следовательно, эта структура будет описываться в PowerShell следующим образом:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPTOAPI_BLOB {
public int cbData;
public IntPtr pbData;
}
Некоторые структуры могут содержать свойства, представляющие другие структуры. Например, CERT_PUBLIC_KEY_INFO. Эта структура состоит из двух свойств: алгоритм, используемый при генерации ключа и самого открытого ключа. Алгоритм в свою очередь состоит из двух свойств: OID алгоритма и параметры идентификатора. А открытый ключ представлен структурой с 3-мя свойствами: длина ключа в байтах, адрес памяти, который содержит сам ключ в виде байтового массива и количества неиспользуемых битов (это вообще отдельная история и заморачиваться этим не нужно). Вот как будут объявляться эти структуры:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPT_BIT_BLOB {
public uint cbData;
public IntPtr pbData;
public uint cUnusedBits;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CERT_PUBLIC_KEY_INFO {
public CRYPT_ALGORITHM_IDENTIFIER Algorithm;
public CRYPT_BIT_BLOB PublicKey;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CRYPT_ALGORITHM_IDENTIFIER {
public String pszObjId;
public CRYPTOAPI_BLOB Parameters;
}
При описании и объявлении функций и структур следует очень внимательно читать справку MSDN, чтобы правильно это сделать. Бывает, что одну и ту же функцию нужно объявлять несколько раз и у каждой из них какой-то аргумент (или несколько) будут иметь разный тип данных. Это как в конструкторах классов .NET. Например, класс X509Certificate2. У этого класса несколько конструкторов, с одним аргументом. Но каждый тип конструктора принимает свой тип данных.
В отличии от .NET, где все классы рассортированы по пространствам имён, функции и структуры WinAPI являются линейными, т.е. всё в одной куче без классов и пространств имён. .NET и p/invoke позволяют создать произвольный класс в произвольном пространстве имён и приаттачить объявленные функции и структуры к этому классу. Вы можете функции и структуры сортировать по разным классам. И вот как финально будет выглядеть объявление наших функций и структур в PowerShell:
$signature = @" [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern bool CryptAcquireContext( ref IntPtr phProv, string pszContainer, string pszProvider, uint dwProvType, Int64 dwFlags ); [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern bool CryptReleaseContext( IntPtr phProv, int flags ); [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern bool CryptGenKey( IntPtr phProv, int Algid, int dwFlags, ref IntPtr phKey ); [DllImport("Crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern bool CryptExportPublicKeyInfo( IntPtr phProv, int dwKeySpec, int dwCertEncodingType, IntPtr pbInfo, ref int pcbInfo ); [DllImport("Crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern bool CryptHashPublicKeyInfo( IntPtr phProv, int Algid, int dwFlags, int dwCertEncodingType, IntPtr pInfo, IntPtr pbComputedHash, ref int pcbComputedHash ); [DllImport("Crypt32.dll", SetLastError=true)] public static extern bool CryptEncodeObject( int dwCertEncodingType, [MarshalAs(UnmanagedType.LPStr)]string lpszStructType, ref CRYPTOAPI_BLOB pvStructInfo, byte[] pbEncoded, ref int pcbEncoded ); [DllImport("Crypt32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern IntPtr CertCreateSelfSignCertificate( IntPtr phProv, CRYPTOAPI_BLOB pSubjectIssuerBlob, int flags, CRYPT_KEY_PROV_INFO pKeyProvInfo, IntPtr pSignatureAlgorithm, SystemTime pStartTime, SystemTime pEndTime, CERT_EXTENSIONS pExtensions ); [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern bool CryptDestroyKey( IntPtr cryptKeyHandle ); [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] public static extern bool FileTimeToSystemTime( [In] ref long fileTime, out SystemTime SystemTime ); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct CRYPT_KEY_PROV_INFO { public string pwszContainerName; public string pwszProvName; public int dwProvType; public int dwFlags; public int cProvParam; public IntPtr rgProvParam; public int dwKeySpec; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct CERT_EXTENSIONS { public int cExtension; public IntPtr rgExtension; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct CERT_EXTENSION { [MarshalAs(UnmanagedType.LPStr)]public String pszObjId; public Boolean fCritical; public CRYPTOAPI_BLOB Value; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct CERT_BASIC_CONSTRAINTS2_INFO { public Boolean fCA; public Boolean fPathLenConstraint; public int dwPathLenConstraint; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct CRYPTOAPI_BLOB { public int cbData; public IntPtr pbData; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct CRYPT_BIT_BLOB { public uint cbData; public IntPtr pbData; public uint cUnusedBits; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct CERT_PUBLIC_KEY_INFO { public CRYPT_ALGORITHM_IDENTIFIER Algorithm; public CRYPT_BIT_BLOB PublicKey; } [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 SystemTime { public short Year; public short Month; public short DayOfWeek; public short Day; public short Hour; public short Minute; public short Second; public short Milliseconds; } "@
Я их оформил в here-string. Теперь нужно создать класс в пространстве имён и прицепить всё это к этому классу:
Add-Type -MemberDefinition $signature -Namespace Quest -Name PowerGUI
Как видно, пространство имён будет Quest и имя класса PowerGUI. Как видно из описания функций (public static blablabla) эти функции станут статическими методами класса и вызываться через двоеточие после названия класса:
[Quest.PowerGUI]::CryptAcquireContext(blablabla)
А вот структуры создаются немного хитрее. Я не знаю почему так (всё же, я не программист), но при создании структуры имя структуры указывается через знак «+» после названия: класса, т.е.:
New-Object Quest.PowerGUI+CRYPT_KEY_PROV_INFO
Структура как тип указывается так же (как и класс):
[Quest.PowerGUI+CERT_EXTENSION]
Кстати говоря, то, что не подсвечивается название структуры — это баг в подсветке PowerGUI (а в ISE этого бага нет).
На сегодня всё. В следующей части я расскажу о принципом управления неуправляемой памятью, включая приём данных из функций. Это, можно сказать, основы практической работы с p/invoke.
Когда вы работаете с провайдером сертификатов (Cert:\) очень часто нужно прочитать какое-то расширение. Однако, на первый взгляд, они кажутся совершенно нечитабельными:
[↓] [vPodans] $cert = (dir cert:\CurrentUser\My)[1] [↓] [vPodans] $cert.Extensions EnhancedKeyUsages Critical Oid RawData ----------------- -------- --- ------- {Encrypting File System} False System.Security.Cryptograp... {48, 12, 6, 10...} False System.Security.Cryptograp... {48, 31, 160, 29...} False System.Security.Cryptograp... {48, 0} [↓] [vPodans]
Если с первым ещё более-менее понятно, то что означают другие — не совсем понятно. Расширение в X509Certificate2 (не всегда, но чаще всего) состоят из 3-х свойств: флаг критичности расширения, OID расширения (т.е. что это за расширение) и значение этого расширения закодированном в ASN.1 формате:
[↓] [vPodans] $cert.Extensions[1].Oid Value FriendlyName ----- ------------ 2.5.29.17 Subject Alternative Name [↓] [vPodans] $cert.Extensions[1].RawData 48 31 160 29 6 10 43 6 1 <...>
Я взял второе расширение и посмотрел его OID — Subject Alternative Name. Чтобы прочитать значение этого расширения можно извратиться через COM интерфейсы CertEnroll (доступные только начиная с Windows Vista и выше). Но можно использовать метод Format() класса X509Extension:
[↓] [vPodans] $cert.Extensions[1].Format(1) Other Name: Principal Name=vPodans@Thor [↓] [vPodans] $cert.Extensions[1].Format(0) Other Name:Principal Name=vPodans@Thor [↓] [vPodans]
Метод может принимать аргумент $true или $false. Если $true, добавляется возврат каретки в каждую строку. Это очень удобно, если значение расширения многострочное (как SAN, CDP, AIA и т.д.). В противном случае весь текст будет в одну строку и он будет нечитабелен. Кстати говоря, вывод будет форматироваться точно так же, как вы его видите в графическом сертификате (со всеми отступами и т.д.):
[↓] [vPodans] $cert = Get-Item cert:\CurrentUser\CA\2BAC956C4EE47F9D5C1E05AE8ED7F95D47C21F80 [↓] [vPodans] $cert.Extensions[3].Format(1) [1]CRL Distribution Point Distribution Point Name: Full Name: URL=http://EVSecure-crl.verisign.com/pca3-g5.crl [↓] [vPodans]
Надеюсь, кому-то пригодится.
Недавняя тема «Мифы о мифах» подтолкнула меня к очередному эпосу на тему мифов в целом и PKI в частности. Основная мысль заключается в том, что частный результат может быть очень даже противоположным общему. Хоть фундамент PKI и выглядит монолитным, его основание закладывается из множества составных деталей. И, следуя указанной мысли, одна деталька не всегда соответствует конечному итогу.
Возьмём один абстрактный пример: администратор X нашёл способ обхождения ограничения RFC (RFC5280, §4.2.1.9) путём подписания пользовательским сертификатом (например, сертификатом EFS или secure email) другого сертификата. Здесь нужно дать большое пояснение сути проблемы.
В бытность использования сертификатов версии 1 было отмечено множество проблем. Например, невозможно было определить тип сертификата — конечный сертификат или сертификат промежуточного CA. Так же было невозможно узнать для каких целей может использоваться сертификат (например, только для шифрования файлов). Нельзя было проверить сертификат на отзыв, если CRL издателя не установлен (закеширован) в системе. Так же, проблемы начинались при обновлении сертификатов самих CA. Вобщем, проблем было много и гибко использовать сертификаты не представлялось возможным. Версия 2 решила только одну проблему — обновление сертификата CA. И только сертификаты версии 3 стали способны решить многие проблемы тех времён. Это было достигнуто за счёт появления расширений сертификата. Расширения сертификатов могут содержать всевозможную информацию о самом сертификате, издателе, владельце ключа и т.д. Например, мы можем применить расширение Enhanced Key Usage и на основании его содержимого принимать решение, можно ли использовать предъявленный сертификат для конкретной задачи или нет. Например, в правильном мире ни одно приложение (rfc-conformant) не согласится использовать сертификат с EKU = Encrypting File System для установки цифровой подписи и т.д. Дальнейшая дискуссия пойдёт только о сертификатах версии 3.
Вернёмся же, к нашему вопросу. Согласно RFC5280, §4.2.1.9, подписывать сертификаты можно только сертификатом, которое содержит расширение Basic Constraints и свойство cA (certification authority) выставлено в true. Так же это расширение должно быть помечено как критичное. Если одно из этих условий не выполняется, такой сертификат не может использоваться для подписи других сертификатов (условно говоря, выступать в роли CA). Но пытливые умы находят лазейку (в виде утилит вида openssl, makecert или низкоуровневых API) и подписывают пользовательским сертификатом другой сертификат. Слабонервные могут впасть в панику, хвататься за острые и режущие предметы или выбрасываться из окон верхних этажей небоскрёбов, потому что мир рухнул. Они так верили в PKI, а тут какой-нибудь openssl взял и дискредитировал всю нерушимую идею PKI. Необходимо как-то запретить такие вольности.
Но я спешу успокоить вас. PKI существует очень давно и сегодня подобные фокусы не пройдут. Давайте посмотрим, как это выглядит на картинках. Я взял свой сертификат, предназначенный только для Code и Document Signing и подписал другой сертификат:
посмотрим вкладку Certification path:
А тут конечный сертификат ВНЕЗАПНО is Ok. Правда, можно заметить, что на предыдущем сертификате в цепочке стоит треугольничек. Когда я запустил файл, Windows пропустил сертификат через упрощённый движок построения цепочки (certificate chaining engine или сокращённо CCE). Поскольку CCE очень любит rfc и не любит openssl, он задетектировал ошибку, что предъявленный сертификат не может использоваться для указанного применения. Но мы пока не видим, каким боком тут вырос Basic Constraints. Для этого мы воспользуемся certutil'ом и посмотрим более детальный трейс CCE (публикую только отрывок):
ChainContext.dwErrorStatus = CERT_TRUST_IS_NOT_VALID_FOR_USAGE (0x10)
ChainContext.dwErrorStatus = CERT_TRUST_INVALID_BASIC_CONSTRAINTS (0x400)
вот тут мы и видим, что проблема с Basic Constraints. Дело в том, что мой сертификат подписи не содержит этого расширения, следовательно не может использоваться для подписывания других сертификатов. Вот описание этой ошибки по версии MSDN:
The certificate or one of the certificates in the certificate chain has a basic constraints extension, and either the certificate cannot be used to issue other certificates, or the chain path length has been exceeded.
Следовательно, если предъявить подобный сертификат любому приложению, которое использует rfc-conformant certificate chaining engine (будь то встроенный в Windows или что-то своё), приложение должно очень сильно ругнуться на сертификат и отклонить его.
Какая следует мораль из этой истории? Мораль, мне кажется, такова, что технически невозможно запретить пользователям делать какие-то нехорошие вещи (например, описываемый сценарий). Но будет крайне сложным доказать какому-то приложению, что вы всё сделали хорошо (разве что если вы Чак Норрис). Потому что приложения действуют по своим правилам (в пределах соответствия стандартам) и они не любят тех, кто эти стандарты пытается нарушать.
Напоследок, предлагаю ссылки на указанные сертификаты, если кто-то хочет заняться анализом вопроса самостоятельно.
В разных интернетах разные пользователи доставляют разные заблуждения на тему PKI. Это вполне нормальное поведение для тех, кто не является специалистом в этой области. Но ненормальное для пользователей претендующих на это звание. Сегодня на повестке дня 3 вопроса:
Вчера в одном блоге вычитал утверждение, что Standalone CA может использовать шаблоны сертификатов. На самом деле это не так. Standalone CA выгодно отличается от Enterprise, что ему не нужны шаблоны. Совсем. Он собирает сертификат из той информации, которая хранится в запросе сертификата. Если в оснастке certsrv.msc у Enterprise CA есть секция Certificate Templates, то у Standalone CA её нет:
вот, есть Revoked, Issued certificates, Pending, Failed Requests, но никакого Certificate Templates, так что использовать их в такой ситуации нельзя.
Я думаю, что многие шифровали файлы в системе простым, автоматически сгенерированным системой сертификатом. Находится он в оснастке certmgr.msc, контейнере Personal и выглядит примерно так:
Как мы видим, это самоподписанный сертификат, у которого Issuer и Subject одинаковые и соответствуют имени текущего пользователя. Срок действия — 100 лет. Всем известно, чтобы сертификат был доверенный, корневой сертификат из его цепочки (или он сам, если это самоподписанный сертификат) должен находиться в контейнере Trusted Root CA. Но в случае с этим сертификатом EFS такое не наблюдается. И вчера в том же самом блоге узнал, что этот самоподписанный сертификат какой-то волшебный и на него не распространяются правила доверия.
Но на самом деле всё гораздо интересней. PKI — технология очень железная с не менее железной логикой и всё в ней подчиняется простым принципам. Это делает PKI весьма надёжной и непробиваемой. Но давайте вернёмся к нашим баранамсампоподписанным сертификатам EFS. На самом деле его копия тоже установлена в контейнере, который делает сертификаты доверенными. И имя ему Trusted People:
Если удалить ваш сертификат из контейнера Trusted People, он станет недоверенным и мы будем горько плакать по этому поводу. Вот что говорят об этом технеты:
Trusted People (TrustedPeople) — This container keeps certificates issued to people or end entities that are explicitly trusted. Most often, these are self-signed certificates or certificates explicitly trusted in an application such as Microsoft Outlook. To share an EFS–encrypted file with other parties, you must have their certificate in this store.
Иными словами, контейнер Trusted People своего рода аналог контейнера Trusted Root CAs, но только для пользовательских сертификатов.
Предположим, у вас кластер NLB, который несёт роль веб-сервера. Зачастую там будет какой-нибудь HTTPS, которому нужны сертификаты. Бытует весьма устойчивое мнение, будто бы сертификат SSL должен быть одинаковый на всех узлах кластера, иначе что-то не будет работать при отказе какого-то узла. В действительности это тоже весьма далеко от истины. Дело в том, что сессионные ключи не реплицируются между узлами кластера и каждый узел поддерживает свою сессию SSL c клиентом (это реально, когда клиент одновременно подключен к нескольким узлам кластера). И переключение с одного узла на второй (если один отказал) вызывает инициирование новой сессии SSL. Т.е. снова запрос сертификата сервера, проверка на доверие и отзыв, генерация сессии, сессионных ключей и всё остальное.
Следовательно, каждый узел кластера может поддерживать свой собственный сертификат SSL. И это, между прочим, является бест практисом (возможно проплаченный верисайном с тафтом). Бест практис в данном случае заключается в том, что каждый узел кластера запрашивает для себя свой сертификат и нет необходимости экспортировать ключи в PFX. Потому что когда люди копируют ключи в PFX защищая его простеньким паролем. Или сложным, но записывая пароль на бумажке, которая попадает нежелательному пользователю, ну а остальное вы знаете :-)
На сегодня это всё.
В этом посте я постарался собрать наиболее полезные или интересные ссылки на MS Knowledge Base (или просто KB) по теме Software Restriction Policies и AppLocker.
Article name | Article ID |
How to stop an ActiveX control from running in Internet Explorer | KB240797 |
Description of the Software Restriction Policies in Windows XP | KB310791 |
RSoP Tool Incorrectly Displays Software Restriction Policies | KB312325 |
Software Restriction Policies Do Not Recognize 16-Bit Programs | KB319458 |
How To use Software Restriction Policies in Windows Server 2003 | KB324036 |
"Software Restriction Policy Does Not Allow You to Start This Program" Error Message Even Though the Program Is Defined As "Allowed" | KB815471 |
Software Restriction Policies feature does not log events as expected | KB823726 |
Software Restriction Policies Do Not Persist After You Define Them | KB830678 |
"Windows cannot open this program because it has been prevented by a software restriction policy" error message when a user tries to open a file in Windows Server 2003 | KB873419 |
You may receive a "RUNAS ERROR: Unable to run" error message in Windows XP with Service Pack 2 | KB895196 |
The Digital Signatures tab may not appear in the properties dialog box of a digitally signed file that is larger than approximately 400 MB in Windows XP with Service Pack 2 | KB922225 |
FIX: Error message when you try to install a large Windows Installer package or a large Windows Installer patch package in Windows Server 2003 or in Windows XP: "Error 1718. File was rejected by digital signature policy" | KB925336 |
Batch files for which you create a hash rules do not work on a Windows XP-based client computer | KB943854 |
Software Restriction Policy Enforcement set to “All Software Files” causes checks against paths/files that are invalid | KB959074 |
"HTTP Error 404 - File or Directory not found" error message when you request dll or exe files with IIS 6.0 | KB970140 |
You cannot install a Windows Installer package under the Local System context on a Windows XP-based computer that has update KB956572 installed | KB971913 |
Error message when you try to install a large Windows Installer package or a large Windows Installer patch package in Windows Server 2003 Service Pack 2: "Error 1718 File was rejected by digital signature policy" | KB973825 |
The "Run only allowed Windows applications" Group Policy setting displays no entries on a computer that is running Windows Vista, Windows Server 2008, or Windows 7 | KB976922 |
Error message occurs when you use GPMC to view a software restriction Group Policy setting in Windows 7 and in Windows Server 2008 R2: "An error has occurred while collecting data for Software Restriction Policies" | KB981750 |
AppLocker incorrectly calculates the hash of certain files at runtime in Windows 7 or in Windows Server 2008 R2 | KB975449 |
Windows 7 or Windows Server 2008 R2 stops responding at the "Please wait" screen before you are requested to press Ctrl+ALT+DEL | KB983551 |
You cannot access allowed applications that are managed by AppLocker in Windows 7 or in Windows Server 2008 R2 | KB2568041 |
You can circumvent AppLocker rules by using an Office macro on a computer that is running Windows 7 or Windows Server 2008 R2 | KB2532445 |
AppLocker path condition does not work when a file name contains international characters in Windows 7 or in Windows Server 2008 R2 | KB2659440 |
Список будет периодически обновляться по мере выхода новых KB.