Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline AndrewKostousov  
#1 Оставлено : 29 апреля 2008 г. 14:35:22(UTC)
AndrewKostousov

Статус: Участник

Группы: Участники
Зарегистрирован: 29.12.2007(UTC)
Сообщений: 22
Мужчина
Откуда: Екатеринбург

Для того, чтобы сформировать PKCS10-запрос на сертификат, необходимо заполнить ASN1-структуру
Код:
CertificationRequest ::= SEQUENCE {
   certificationRequestInfo  CertificationRequestInfo,
   signatureAlgorithm        AlgorithmIdentifier,
   signature                 BIT STRING
}
AlgorithmIdentifier ::= SEQUENCE {
   algorithm   OBJECT IDENTIFIER,
   parameters  ANY DEFINED BY algorithm OPTIONAL
}

Затруднение вызывает заполнение поля signatureAlgorithm.

Понятно, что идентификатор алгоритма подписи для заданного криптопровайдера однозначно выводится из пары (<идентификатор алгоритма открытого ключа>, <идентификатор алгоритма хеширования>). <Идентификатор алгоритма открытого ключа> указывается при создании ключевой пары функцией CryptGenKey() и может быть в дальнейшем восстановлен при помощи функции CryptExportPublicKeyInfo(), а <идентификатор алгоритма хеширования> указывается при создании хеша функцией CryptCreateHash(). Таким образом, в момент генерации ЭЦП эти идентификаторы мне известны и доступны.

Для каждой конкретной пары идентификаторов открытого ключа и алгоритма хеширования идентификатор соответствующего алгоритма подписи можно определить, порывшись в различных RFC. Например, для КриптоПро CSP паре (1.2.643.2.2.19 (GOST R 34.10-2001), 1.2.643.2.2.9 (GOST R 34.11-94)) соответствует алгоритм подписи 1.2.643.2.2.3 (GOST R 34.11 / 34.10-2001). Но вот как решить эту задачу в общем виде при помощи универсального кода, обращающегося к CryptoApi, для заранее неизвестных криптопровайдеров?


P.S.: По ряду причин при формировании запроса на сертификат мне хотелось бы избежать использования более "высокоуровневых" CryptoApi-функций типа CryptSignCertificate(), которым идентификатор алгоритма подписи передается явно. Кроме того, я все равно должен как-то узнать, какой идентификатор в эту функцию можно передавать для данного криптопровайдера...
С уважением,
Андрей Костоусов
СКБ Контур
Offline Kirill Sobolev  
#2 Оставлено : 29 апреля 2008 г. 15:18:51(UTC)
Кирилл Соболев

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 25.12.2007(UTC)
Сообщений: 1,732
Мужчина
Откуда: КРИПТО-ПРО

Поблагодарили: 177 раз в 168 постах
Надо смотреть, какие алгоритмы поддерживает выбранный криптопровайдер и уже из них выбирать - так например реализована генерация запросов в веб-интерфейсе MS СА.
Техническую поддержку оказываем тут
Наша база знаний
Offline AndrewKostousov  
#3 Оставлено : 30 апреля 2008 г. 23:47:44(UTC)
AndrewKostousov

Статус: Участник

Группы: Участники
Зарегистрирован: 29.12.2007(UTC)
Сообщений: 22
Мужчина
Откуда: Екатеринбург

Kirill Sobolev написал:
Надо смотреть, какие алгоритмы поддерживает выбранный криптопровайдер и уже из них выбирать - так например реализована генерация запросов в веб-интерфейсе MS СА.
Ну, это не метод...

Ведь подпись-то функцией CryptSignHash() создается без явного указания идентификатора алгоритма подписи. Сигнатура этой функции просто не предусматривает передачу в нее этой информации. Вместо этого она сама как-то определяет алгоритм, который надо использовать. Для этого в ее распоряжении есть хэндл хеша, созданного при помощи функции CryptCreateHash(), в которую передается идентификатор алгоритма хеширования и хендл крипто-контекста ключевого контейнера, созданного и наполненного ключами при помощи функций CryptAcquireContext() и CryptGenKey(). Короче говоря, нигде в этой вполне штатной цепочке вызовов CryptoApi-функций, приводящей к созданию ЭЦП, идентификатор используемого алгоритма подписи явно не фигурирует.

Кстати, COM-объект XEnroll, который, насколько я знаю, используется в веб-интерфейсе MS СА успешно справляется с решеним данной задачи. Для того, чтобы с его помощью создать запрос на сертификат, ему достаточно указать тип и имя криптопровайдера (которые нужны функции CryptAcquireContext()) и идентификатор алгоритма хеширования (который нужен функции CryptCreateHash()):
Код:
//set the CSP
xenroll_.ProviderName = opts.CspName;
xenroll_.ProviderType = opts.CspType;

//set the hash algorithm     
xenroll_.HashAlgID = opts.HashAlgorithmId;
У XEnroll'а есть еще куча свойств, которые позволяют задать различные флаги, влияющие на свойства создаваемого ключевого контейнера и ключей в нем, но прямого отношения к рассматриваемому вопросу они не имеют. В результате вызова метода xenroll_.createRequest(XECR_PKCS10_V2_0, ...) создается корректно заполненная ASN1-структура CertificationRequest, в частности, содержащая информацию об алгоритме подписи. При этом XEnroll в процессе создания ключей и запроса на сертификат у пользователя не спрашивает какой алгоритм подписи ему следует использовать.
С уважением,
Андрей Костоусов
СКБ Контур
Offline AndrewKostousov  
#4 Оставлено : 1 мая 2008 г. 0:13:28(UTC)
AndrewKostousov

Статус: Участник

Группы: Участники
Зарегистрирован: 29.12.2007(UTC)
Сообщений: 22
Мужчина
Откуда: Екатеринбург

Мне кажется, я нашел "правильный" CryptoApi-способ вычисления объектного идентификатора алгоритма подписи по имеющимся идентификаторам алгоритмов хеширования и открытого ключа. Он заключается в вызове функции CryptFindOIDInfo():
Код:
ALG_ID pair[2];
pair[0] = hashAlgId;
pair[1] = publicKeyAlgId;
PCCRYPT_OID_INFO pSgnAlgOidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_SIGN_KEY, &pair, CRYPT_SIGN_ALG_OID_GROUP_ID);
//в результате pSgnAlgOidInfo->pszOID содержит интересующий нас оид алгоритма подписи
Тесты показали, что для ГОСТовских алгоритмов при использовании криптопровайдера КриптоПро CSP, а также на обычных RSAшных алгоритмах, встроенных в винду, метод выдает правильный идентификатор алгоритма подписи.

Однако данный метод не дает ответа на вопрос, откуда взять параметры алгоритма подписи, которые нужно поместить в поле CertificationRequest.signatureAlgorithm.parameters запроса на сертификат. Правда, насколько я понял, для ГОСТ-алгоритма 1.2.643.2.2.3 (ГОСТ Р 34.11/34.10-2001) и для RSA-алгоритма 1.2.840.113549.1.1.5 (sha1RSA) и вообще для большинства распространенных алгоритмов подписи параметры не указываются, но все-таки...

Будут какие-нибудь дополнительные соображения на эту тему?
С уважением,
Андрей Костоусов
СКБ Контур
Offline Максим Коллегин  
#5 Оставлено : 1 мая 2008 г. 3:26:07(UTC)
Максим Коллегин

Статус: Сотрудник

Группы: Администраторы
Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,374
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 32 раз
Поблагодарили: 704 раз в 613 постах
Мыслите в правильную сторону
Знания в базе знаний, поддержка в техподдержке
Offline Максим Коллегин  
#6 Оставлено : 1 мая 2008 г. 3:27:50(UTC)
Максим Коллегин

Статус: Сотрудник

Группы: Администраторы
Зарегистрирован: 12.12.2007(UTC)
Сообщений: 6,374
Мужчина
Откуда: КРИПТО-ПРО

Сказал «Спасибо»: 32 раз
Поблагодарили: 704 раз в 613 постах
Если бы MS так делал везде...
Знания в базе знаний, поддержка в техподдержке
Offline Kirill Sobolev  
#7 Оставлено : 4 мая 2008 г. 15:37:03(UTC)
Кирилл Соболев

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 25.12.2007(UTC)
Сообщений: 1,732
Мужчина
Откуда: КРИПТО-ПРО

Поблагодарили: 177 раз в 168 постах
Цитата:
Мне кажется, я нашел "правильный" CryptoApi-способ вычисления объектного идентификатора алгоритма подписи по имеющимся идентификаторам алгоритмов хеширования и открытого ключа. Он заключается в вызове функции CryptFindOIDInfo():

Он безусловно правильный, но алгоритмы хеширования и ОК все равно придется определять перебором из доступных для выбранного провайдера.
Техническую поддержку оказываем тут
Наша база знаний
Offline Serge3leo  
#8 Оставлено : 8 мая 2008 г. 8:27:36(UTC)
Serge3leo

Статус: Активный участник

Группы: Участники
Зарегистрирован: 28.01.2008(UTC)
Сообщений: 40
Мужчина
Откуда: Москва

Поблагодарили: 3 раз в 2 постах
AndrewKostousov написал:
По ряду причин при формировании запроса на сертификат мне хотелось бы избежать использования более "высокоуровневых" CryptoApi-функций типа CryptSignCertificate(),

На мой взгляд, это неверное решение, т.к. Вы будете дублировать код выбора необходимых OID функций типа PFN_*, а так же сам этот код.

Изложите эти причины, быть может, есть и не такой, "сложный" путь их удовлетворения.

AndrewKostousov написал:
Однако данный метод не дает ответа на вопрос, откуда взять параметры алгоритма подписи, которые нужно поместить в поле CertificationRequest.signatureAlgorithm.parameters запроса на сертификат. Правда, насколько я понял, для ГОСТ-алгоритма 1.2.643.2.2.3 (ГОСТ Р 34.11/34.10-2001) и для RSA-алгоритма 1.2.840.113549.1.1.5 (sha1RSA) и вообще для большинства распространенных алгоритмов подписи параметры не указываются, но все-таки...

Будут какие-нибудь дополнительные соображения на эту тему?

Алгоритм и параметры ЭЦП - однозначно определяются именем контейнера и спецификацией закрытого ключа AT_* (дескриптором закрытого ключа).

Эти параметры извлекают с помощью CryptGetKeyParam(). Алгоритм (CALG_*) общим для всех образом, а параметры специфичным для провайдера/алгоритма образом, смотрите нашу документацию (CSP_3_*.chm). Для преобразования наших CAPI значений в ASN.1 DER, также смотрите нашу документацию <CPCMS - RFC 4490>, <CPPK - RFC 4491>.

Алгоритм и параметры хэш-функции - однозначно определяются дескриптором хэш-функции.

Эти параметры извлекают с помощью CryptGetHashParam(). Всё аналогично, смотрите нашу документацию.

Остаётся вопрос, как создать хэш-функцию с алгоритмом , которая будет совместима с данным закрытым ключом по операции ЭЦП. По-моему, это можно сделать только перебором всех пар `pair[2]'. Тем не менее, к сожалению, возможна неоднозначность, которая потребует какого-то разрешения, например, выбирать самую длинную.

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

Отредактировано пользователем 8 мая 2008 г. 8:33:47(UTC)  | Причина: Не указана

Offline AndrewKostousov  
#9 Оставлено : 9 июня 2008 г. 19:59:08(UTC)
AndrewKostousov

Статус: Участник

Группы: Участники
Зарегистрирован: 29.12.2007(UTC)
Сообщений: 22
Мужчина
Откуда: Екатеринбург

Serge3leo написал:
На мой взгляд, это неверное решение, т.к. Вы будете дублировать код выбора необходимых OID функций типа PFN_*, а так же сам этот код.

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

В результате, я упростил себе задачу и стал плясать от известных (на стадии разработки) криптопровайдеров и конкретных криптографических алгоритмов, которые нужно поддерживать. Для них все необходимые идентификаторы, параметры и способы их кодирования были заранее собраны и зафиксированы в коде - в конце концов, таких специфических деталей не так уж и много, а излишние обобщения тоже до добра не доводят :-)

Что касается
Serge3leo написал:
AndrewKostousov написал:
По ряду причин при формировании запроса на сертификат мне хотелось бы избежать использования более "высокоуровневых" CryptoApi-функций типа CryptSignCertificate(),

Изложите эти причины, быть может, есть и не такой, "сложный" путь их удовлетворения.

то я имел в виду следующее: поскольку у меня в проекте задача кодирования/декодирования различных ASN.1-структур в DER уже вполне успешно решена при помощи библиотеки BouncyCastle, мне хотелось по-максимуму избежать нудного заполнения CryptoApi-структур наподобие CERT_REQUEST_INFO, которые передаются в "высокоуровневые" функции типа CryptSignAndEncodeCertificate().

Справедливости ради нужно отметить, что конкретно упомянутая мной функция CryptSignCertificate() принимает уже закодированный в DER массив байтов, на что я не сразу обратил внимание. Но от этого она не становится намного более предпочтительной по сравнению с совсем базовой функцией CryptSignHash(). Она лишь скрывает вызовы функций CryptCreateHash(), CryptSignHash() и CryptHashData(), котрые совсем несложно реализовать и самостоятельно, но зато уже не позволяет подписывать произвольные данные, а только корректные ASN.1-объекты в DER-кодировке.

В любом случае всем спасибо за потраченное на мой вопрос время!
С уважением,
Андрей Костоусов
СКБ Контур
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.