|
| ||||||||||||
| ||||||||||||
|
2006 г.
Создание криптографического провайдера (CSP) для WindowsЗырянов Юрий СергеевичOOO "ЛИССИ" ВведениеЭта статья для тех, кто по тем или иным причинам решил написать собственный криптопровайдер для OC семейства Windows. Если Вы хотите реализовать в вашем провайдере нестандартные алгоритмы, то вам предстоит столкнуться с определенными трудностями. Трудности могут возникнуть, например, при попытках использования вашего криптопровайдера для проверки сертификатов в MS Internet Explorer. Под нестандартными алгоритмами здесь понимаются не всемирные DES, RSA, DSA и т.д., а, например, алгоритмы семейства ГОСТ. Дело в том, что для RSA и подобных алгоритмов все необходимые идентификаторы уже зашиты в систему, а для ГОСТ-ов (или многих других алгоритмов) надо отдельно позаботиться о том, чтобы система их "увидела". Для примеров кода используется Си. Все примеры кода служат только для иллюстрации принципов, изложенных в статье и не являются полноценными рабочими программами. Также подразумевается, что у читателя есть базовые знания в области прикладной криптографии и термины "открытый ключ", "ASN.1" и подобные для него не являются загадкой. Интеграция провайдера в Windows
Только после выполнения этих действий провайдер нормально интегрируется в систему и вы сможете, например, генерировать сертификаты при помощи вашего провайдера на основе стандартного компонента ОС Windows Server - Сertification services или на тестовом УЦ КриптоПро. Корректно будет отображаться статус проверки подписи у сертификатов, подписанных вашим "нестандартным" алгоритмом и т.п. Далее подробно рассмотрим каждый из упомянутых выше шагов, предполагая при этом, что библиотека с Вашим CSP уже имеется и корректно работают все функции провайдера. Регистрация криптопровайдера и алгоритмов в системеКогда у вас уже есть готовая библиотека с реализацией функций CSP, необходимо зарегистрировать ее в системе, для того чтобы новый криптопровайдер стал доступен различным приложениям. Процесс регистрации самого CSP подробно описан в MDSN, и повторять эту информацию здесь смысла нет. Все это подробно описано в [1]. Также здесь мы не будем останавливаться на проблеме подписи нового CSP в Microsoft и путях ее обхода. Эта проблема уже многократно обсуждалась на различных форумах, например смотрите [3]. Гораздо интереснее рассмотреть регистрацию криптографических алгоритмов. Каждый алгоритм имеет свой уникальный ASN.1 идентификатор Оbject Identifier - OID. Например, алгоритм подписи ГОСТ-34.10-2001 имеет такой OID (представленный в виде строки) - "1.2.643.2.2.3". Идентификатор каждого поддерживаемого вашим CSP алгоритма следует занести в реестр. Помимо OID у каждого криптоалгоритма в Windows существует еще идентификатор в виде четырехбайтового числа - AlgID, по которому алгоритмы идентифицируются в провайдере. Этот идентификатор заносится в CSP и его можно узнать, перечислив алгоритмы посредством вызова CPGetProvParam. В КриптоПро, например, для алгоритма хеширования ГОСТ-34.11-94 AlgID используется значение 0x801e. Пусть нам необходимо зарегистрировать алгоритм подписи ГОСТ-34.10-2001. Тогда в реестре необходимо прописать следующие идентификаторы:
Рисунок 1. Создание необходимых разделов в реестре Windows Далее приведен пример кода регистрации OID алгоритма ГОСТ-34.11-94
// Регистрация GOST-3411-94 HASH OID
//
CRYPT_OID_INFO oidInfo;
int rc = 0;
memset(&oidInfo, 0, sizeof(CRYPT_OID_INFO));
oidInfo.cbSize = sizeof(CRYPT_OID_INFO);
oidInfo.pszOID="1.2.643.2.2.9";
oidInfo.pwszName= L"GOST-3411-94 HASH";
oidInfo.dwGroupId = CRYPT_HASH_ALG_OID_GROUP_ID;
oidInfo.Algid = 0x801e;
oidInfo.ExtraInfo.cbData=0;
oidInfo.ExtraInfo.pbData=0;
rc = CryptRegisterOIDInfo(
&oidInfo,
0
);
if(rc)
printf("\nHash algorithm registered");
else
printf("\nError registering hash algorithm");
Аналогично регистрируются и остальные алгоритмы. Подробную информацию о структуре CRYPT_OID_INFO можно найти в MSDN.
Для того, чтобы провайдер вызывался для проверки сертификата, подписанного нашим "нестандартным" алгоритмом, необходимо еще одно дополнительное действие. Дело в том, что Windows определяет, какой провайдер использовать для проверки по полю ExtraInfo (см. ссылку в предыдущем абзаце для описания этого поля) в ключе реестра, соответствующем алгоритму подписи - такой ключ мы создаем, вызывая функцию CryptRegisterOIDInfo. Поэтому надо указать системе наш провайдер в качестве провайдера по умолчанию для типа, который занесен в ExtraInfo алгоритма подписи. Следующий код устанавливает провайдер по умолчанию для определенного типа. #define YOUR_PROV_NAME "MY_PROV" #define YOUR_PROV_TYPE 75 rc = CryptSetProvider( YOUR_PROV_NAME, YOUR_PROV_TYPE ); Сценарии применения нашего CSPРассмотрим здесь два сценария применения CSP. Первый сценарий - проверка подписи сертификата. Для проверки подписи система загружает открытый ключ из сертификата, которым подписан проверяемый. Затем по OID алгоритма подписи проверяемого сертификата, так как описано в предыдущем разделе статьи, определяется требуемый провайдер. Чтобы проверить подпись, нужно импортировать открытый ключ в CSP и можно было бы подумать, что Windows сразу вызывает функцию нашего провайдера CPImportKey. Но не тут-то было! Второй сценарий - генерация ключевой пары и отправка запроса на сертификат на Удостоверяющий Центр. Windows загружает наш CSP, генерирует ключевую пару и экспортирует к себе наверх открытый ключ, вызывая функцию CPExportKey. Все хорошо. Вроде бы надо взять и поместить полученный буфер с ключом в PKCS#10 запрос, который затем будет отправлен на УЦ. И тут все опять не совсем так. Рисунок 2. Создание сертификата при помощи веб-интерфейса Оказывается, существуют промежуточные функции для экспорта/импорта открытых ключей, и без их реализации ничего хорошего с нашим CSP, в упомянутых выше двух сценариях, не получится. Беда еще и в том, что функции эти недокументированные и найти информацию по ним крайне сложно. Их описанию посвящен следующий раздел. Функции конвертирования ключейАрхитектура круговорота открытых ключей для "нестандартных" алгоритмов в Windows представлена на рисунке 3. Рисунок 3. Архитектура круговорота открытых ключей для "нестандартных" алгоритмов Функция A_ConvertPublicKeyInfo - на входе принимает ключ в формате ASN.1 DER и должна преобразовать его в формат, который "понимает" функция CSP CPImportKey Функция A_EncodePublicKeyInfoAndParameters на входе принимает ключ в том формате, Вот сигнатуры и краткие описания параметров этих функций: BOOL WINAPI A_ConvertPublicKeyInfo( DWORD dwCertEncodingType, // IN - VOID *EncodedKeyInfo, // IN - буфер с ключом - указатель // на структуру CERT_PUBLIC_KEY_INFO DWORD dwAlg, // IN - AlgId ключа DWORD dwFlags, // IN - обычно 0 BYTE **ppStructInfo, // OUT - двойной указатель на структуру // в заголовке структуры идет сначала PUBLICKEYSTRUC, затем DSSPUBKEY, // а затем сам ключ с параметрами DWORD *StructLen // OUT - длина структуры ); BOOL WINAPI A_EncodePublicKeyAndParameters( DWORD dwCertEncodingType, // IN LPCSTR lpszStructType, // IN - OID алгоритма const void* pvStructInfo, // IN - такая же структура как // на выходе ConvertPublicKeyInfo DWORD nStructLen, // IN - длина входной структуры DWORD dwFlags, // IN - обычно 0 DWORD Unk, // ? - неизвестно BYTE **pbPubKey, // OUT - открытый ключ в ASN.1 DER DWORD* pcPubKeyLen, // OUT - длина открытого ключа BYTE **pbParams, // OUT - параметры открытого ключа DWORD* pcParamsLen // OUT - длина параметров ); Форматы ключей зависят от криптопровайдера и используемых алгоритмов. Функция I_CryptGetDefaultCryptProvider из crypt32.dllПо непонятной для меня причине Windows часто не пытается искать нужный криптопровайдер по идентификатору алгоритма, с которым требуется работать. В таких случаях она просто вызывает недокументированную функцию I_CryptGetDefaultCryptProvider, и если тот провайдер, который вернула эта функция, не умеет работать с данным алгоритмом, то процесс завершается с ошибкой. Так происходит, например, при разборе в Internet Explorer PKCS#7 ответа в сценарии с запросом сертификата на тестовом УЦ. HCRYPTPROV WINAPI I_CryptGetDefaultCryptProv(ALG_ID algid); Необходимо заменить эту функцию таким образом, чтобы при нулевом параметре algid на входе она возвращала наш провайдер, который уже в отличие от штатного провайдера легко справится с "нестандартными алгоритмами". Обсуждение способов замены функций в системной dll выходит далеко за рамки данной статьи. Могу лишь, как один из способов решения, предложить библиотеку Microsoft Detours. Привязка закрытого ключа к сертификатуВ отличие от открытого ключа, закрытые ключи никогда не покидают криптопровайдер и поэтому, когда вы видите, что для данного сертификата есть закрытый ключ (как на рисунке), это значит, что в контексте этого сертификата существует явная ссылка на закрытый ключ.
Контекст сертификата - это набор дополнительных атрибутов сертификата, которые находятся не в теле сертификата, а хранятся отдельно от него. Одним из таких атрибутов и является ссылка на закрытый ключ, которая состоит из имени провайдера и имени ключевого контейнера. Пример кода для привязки закрытого ключа к сертификату: PCCERT_CONTEXT pCert; CRYPT_KEY_PROV_INFO prov_info; … prov_info.cProvParam = 0; prov_info.rgProvParam = 0; prov_info.dwFlags = 0; prov_info.dwKeySpec = AT_SIGNATURE; prov_info.dwProvType = 0; prov_info.pwszContainerName = L"key-kont-name"; prov_info.pwszProvName = L"A-CSP"; CertSetCertificateContextProperty( pCert, CERT_KEY_PROV_INFO_PROP_ID, 0, &prov_info ); Успехов в разработке криптопровайдера! Дополнительная информация
|
|
CITForum © 1997–2025