Сообщений 6    Оценка 25 [+0/-1]         Оценить  
Система Orphus

Секреты разработки CSP для Windows

Создание криптографического провайдера для Windows

Автор: Зырянов Юрий Сергеевич
OOO “ЛИССИ”

Источник: RSDN Magazine #3-2006
Опубликовано: 09.08.2006
Исправлено: 09.02.2007
Версия текста: 1.0
Введение
Интеграция провайдера в Windows
Регистрация крипто-провайдера и алгоритмов в системе
Сценарии применения CSP
Функции конвертирования ключей
Функция I_CryptGetDefaultCryptProvider из crypt32.dll
Привязка закрытого ключа к сертификату
Дополнительная информация

Введение

Эта статья для тех, кто по тем или иным причинам решил написать собственный криптопровайдер для OC семейства Windows. Если вы хотите реализовать в вашем провайдере нестандартные алгоритмы, то вам предстоит столкнуться с определенными трудностями. Трудности могут возникнуть, например, при попытках использования вашего криптопровайдера для проверки сертификатов в MS Internet Explorer.

Под нестандартными алгоритмами здесь понимаются не всемирно распространенные DES, RSA, DSA и т.д., а, например, алгоритмы семейства ГОСТ. Дело в том, что для RSA и подобных алгоритмов всё необходимое уже заложено в систему, а для ГОСТ-ов (или многих других алгоритмов) надо отдельно позаботиться о том, чтобы система их “увидела”.

В примерах кода используется Си. Все примеры кода служат только для иллюстрации принципов, изложенных в статье, и не являются полноценными рабочими программами.

Также подразумевается, что у читателя есть базовые знания в области прикладной криптографии, и термины “открытый ключ”, “ASN.1” и им подобные для него не являются загадкой.

Интеграция провайдера в Windows

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

Только после выполнения этих действий провайдер нормально интегрируется в систему, и вы сможете, например, при помощи вашего провайдера генерировать сертификаты с помощью стандартного компонента ОС Windows – Сertification services.

Рассмотрим подробнее каждый из упомянутых выше шагов, предполагая при этом, что библиотека с CSP уже имеется, и что все функции провайдера работают корректно.

Регистрация крипто-провайдера и алгоритмов в системе

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

Процесс регистрации самого CSP подробно описан в MDSN [1], и повторять эту информацию здесь смысла нет. Также здесь мы не будем останавливаться на проблеме подписи CSP и путях ее обхода. Гораздо интереснее рассмотреть регистрацию криптографических алгоритмов. Каждый алгоритм имеет свой уникальный 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. Тогда в реестре необходимо прописать следующие идентификаторы:


Далее приведен пример кода регистрации 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: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/seccrypto/security/crypt_oid_info.asp.

Чтобы провайдер вызывался для проверки сертификата, подписанного “нестандартным” алгоритмом, необходимо еще одно дополнительное действие.

Дело в том, что 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, который затем будет отправлен в УЦ. И тут все опять не совсем так.



Оказывается, существуют промежуточные функции для экспорта/импорта открытых ключей, и без их реализации ничего хорошего с нашим CSP в упомянутых выше двух сценариях не получится. Беда еще и в том, что функции эти недокументированные, и найти информацию по ним крайне сложно. Их описанию посвящен следующий раздел.

Функции конвертирования ключей

Архитектура круговорота открытых ключей для “нестандартных” алгоритмов в Windows представлена на картинке:


Функция A_ConvertPublicKeyInfo – на входе принимает ключ в формате ASN.1 DER и должна преобразовать его в формат, который “понимает” функция CSP CPImportKey.

Функция A_EncodePublicKeyInfoAndParameters на входе принимает ключ в том формате, в котором он был экспортирован функцией CPExportKey. На выходе A_EncodePublicKeyInfoAndParameters должна сформировать ASN.1 DER-структуру с ключом – ту же самую, которая в случае импорта передается в A_ConvertPublicKeyInfo.

Вот сигнатуры и краткое описание параметров этих функций:

BOOL WINAPI A_ConvertPublicKeyInfo(
  DWORD dwCertEncodingType,   // IN - 
  // IN – буфер с ключом – указатель на структуру CERT_PUBLIC_KEY_INFO
  VOID *EncodedKeyInfo, 
  DWORD dwAlg,        // IN – AlgId ключа
  DWORD dwFlags,      // IN – обычно 0
  // OUT  – двойной указатель на структуру. 
  // В заголовке структуры идет сначала PUBLICKEYSTRUC, затем DSSPUBKEY, 
  // а затем сам ключ с параметрами.
  BYTE** ppStructInfo,  
  DWORD* StructLen    // OUT  – длина структуры
);

BOOL WINAPI A_EncodePublicKeyAndParameters(
  DWORD dwCertEncodingType,  // IN
  LPCSTR lpszStructType,     // IN – OID алгоритма
  // IN – такая же структура, как на выходе ConvertPublicKeyInfo
  const void* pvStructInfo,   
  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 она возвращала наш провайдер, который, в отличие от штатного провайдера, легко справится с «нестандартными» алгоритмами.

ПРИМЕЧАНИЕ

Довольно спорное предложение – заниматься изменением системных библиотек Windows. – прим.ред.

Обсуждение способов замены функций в системной dll выходит далеко за рамки данной статьи. Могу лишь, как один из способов решения, предложить библиотеку Microsoft Detours:

http://research.microsoft.com/sn/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
  );

Успехов в разработке крипто-провайдера!

Дополнительная информация

  1. Разработка CSP для Windows http://msdn.microsoft.com/library/default.asp?url=/library/en-us/seccrypto/security/cryptography_portal.asp
  2. Additional Cryptographic Algorithms for Use with GOST 28147-89, GOST R 34.10-94, GOST R 34.10-2001, and GOST R 34.11-94 Algorithms http://www.ietf.org/rfc/rfc4357.txt


Эта статья опубликована в журнале RSDN Magazine #3-2006. Информацию о журнале можно найти здесь
    Сообщений 6    Оценка 25 [+0/-1]         Оценить