21.12.2006 12:54:56Симметричный ключ, созданный в 3.0, не импортируется в 1.1? Ответов: 11
Vlad Stepanov
Здравствуйте. Не подскажите, как в 3.0 (используем 3293) сгенерить симметричный ключ, чтобы он потом импортировался у клиентов с 1.1.129.1?
Используем, естественно, ГОСТ-94.
В прошлый раз (когда добивался совместимости 2.0 с 1.1) проблема решалась вызовом CryptSetKeyParam с KP_ALGID - CALG_PRO_EXPORT.
(подробнее здесь, с 4-го поста: http://www.cryptopro.ru/cryptopro/forum/view.asp?q=1659)
Спасибо
 
Ответы:
21.12.2006 13:46:45Василий
А сейчас какая ситуация - контейнер с ключом ГОСТ 94 сделан на 3.0 или нет?
И второе 3.0-3.0 3.0-2.0 2.0-3.0 работает?
21.12.2006 17:03:21Vlad Stepanov
Контейнер с секретным ключом сделан в 1.1.
Связки 3.0-3.0 3.0-2.0 2.0-3.0 работают.

Оказалось, не работает и связка 2.0(сервер) - 1.1 (клиент), где симметричные ключи создаются только на сервере (а у клиентов только импортируются).

Раньше нужна была только связка 1.1 (сервер) - 2.0 (клиент), и там импорт симметричного ключа (создаваемого всегда в 1.1) у клиента 2.0 заработал, когда я стал делать CryptSetKeyParam(..KP_ALGID.. CALG_PRO_EXPORT..) комбинированному ключу, на котором импортировал потом симметричный.

Сейчас получаю ошибку "Invalid type specified (8009000A)" в CryptImportKey, если, например:
- беру серверный секретный ключ, созданный в 1.1,
- генерирую в 2.0 симметричный ключ (и зашифровываю его серверной парой)
- дальше в 1.1 пытаюсь перешифровать этот симметричный ключ на публичном ключе клиента, созданном в 1.1 - и при импорте симметричного получаю ошибку.

Может, какой-то параметр провайдера нужно подкрутить, чтобы симметричный ключ, создаваемый в 2.0, стал совместим/успешно импортировался в 1.1?
09.01.2007 11:32:30Vlad Stepanov
Идей по решению проблемы, описанной в последнем сообщении, не возникло?
09.01.2007 16:26:01Василий
Честно говоря, не очень понятно:
"- беру серверный секретный ключ, созданный в 1.1,
- генерирую в 2.0 симметричный ключ (и зашифровываю его серверной парой)
- дальше в 1.1 пытаюсь перешифровать этот симметричный ключ на публичном ключе клиента, созданном в 1.1 - и при импорте симметричного получаю ошибку"

Можно чуть подробнее - какие ключи (AT_KEYEXCHANGE, Диффи-Хеллмана, сессионные) и какие функции вызываются?
10.01.2007 9:12:02Vlad Stepanov
1) Генерирую ключи на машине с КриптоПро 1.1 (1.1.129.1):

CryptAcquireContext(..., 'Crypto-Pro Cryptographic Service Provider', PROV_GOST_DH, ...); // создание контейнера ГОСТ-94
CryptGenKey(..., AT_KEYEXCHANGE, ...); // генерация секретного и публичного ключей (в реестре)
CryptExportPublicKeyInfo(..., AT_KEYEXCHANGE, ...); // экспорт публичного ключа в формате публичного ключа сертификата в файл (в этом формате публичный ключ храню и передаю, так он совпадает с тем, что видно при просмотре сертификата стандартными средствами)
CryptExportKey(...PUBLICKEYBLOB...); // обычный экспорт публичного ключа (в принципе, не нужен, далее не используется, вызов остался исторически)


2) Переношу фрагмент реестра с контейнером секретнго и публичного ключей (HKEY_LOCAL_MACHINE\SOFTWARE\Crypto Pro\Settings\USERS\<SID_пользователя>\Keys\<контейнер>) на машину с КриптоПро 2.0 (Build 2091) и на ней генерирую симметричный ключ:

CryptAcquireContext(..., 'Crypto-Pro Cryptographic Service Provider', PROV_GOST_DH, ...); // доступ к контейнеру ГОСТ-94 [пробуя с 3.0 вместо 2.0, естественно, использовал 'Crypto-Pro GOST R 34.10-94 Cryptographic Service Provider', PROV_GOST_94_DH]
CryptGeKey(... CALG_G28147, CRYPT_EXPORTABLE, ...); // генерирую симметричный ключ
CryptGetKeyParam(... KP_IV ...); // читаю инициализационный вектор для симметричного ключа, чтобы посмотреть его длину
CryptSetKeyParam(... KP_IV, <буфер с нулями для инициализационного вектора>, ...); // задаю инициализационный вектор из нулей
CryptGetUserKey(..AT_KEYEXCHANGE...&selfkey); // получаю дескриптор секретного и публичного ключей контейнера
CryptImportPublicKeyInfo(..X509_ASN_ENCODING or PKCS_7_ASN_ENCODING..); // импортирую публичный ключ из файла (в данном случае - это тот же публичный ключ, что и в контейнере)
CryptExportKey(... PUBLICKEYBLOB...&selfpubstr); // экспортирую только что проимпортированный публичный ключ - в формате, требуемом для формирования совместного ключа
CryptImportKey(...selfpubstr...selfkey...); // формирую совместный ключ (в данном случае - из одних и тех же - собственных - ключей)
CryptSetKeyParam(...KP_ALGID, CALG_PRO_EXPORT...); // добавлялось для совместимости 1.1 с 2.0, когда на сервере было 1.1, а у некоторых клиентов появилось 2.0 [задание CALG_PRO_EXPORT для совместного ключа у клиентов с 2.0 позволило импортировать у них симметричный ключ, формируемый только на сервере, где тогда было 1.1; сейчас, когда на сервере хотим сделать 2.0, а у некоторых клиентов осталось 1.1 - это не спасает]
CryptExportKey(..симметричный ключ, совместный ключ, SIMPLEBLOB); // экспортируем созданный симметричный ключ на совместном, в файл - в таком виде симметричный ключ хранится на сервере (а пользователям раздается перешифрованный на их публичных ключах)


3) Теперь для эксперимента переношу контейнером секретного и публичного ключей обратно на машину с КриптоПро 1.1 и попробую на ней перешифровать симметричный ключ (созданный в 2.0), на публичном ключе одного из клиентов (до которого дело не доходит, ошибка еще на этапе импорта симметричного):

CryptAcquireContext(..., 'Crypto-Pro Cryptographic Service Provider', PROV_GOST_DH, ...); // доступ к контейнеру ГОСТ-94
CryptGetUserKey(..AT_KEYEXCHANGE...&selfkey); // получаю дескриптор секретного и публичного ключей контейнера
CryptImportPublicKeyInfo(..X509_ASN_ENCODING or PKCS_7_ASN_ENCODING..); // импортирую свой публичный ключ из файла (в данном случае - это тот же публичный ключ, что и в контейнере)
CryptExportKey(... PUBLICKEYBLOB...&selfpubstr); // экспортирую только что проимпортированный публичный ключ - в формате, требуемом для формирования совместного ключа
CryptImportKey(...selfpubstr...selfkey...); // формирую совместный ключ (в данном случае - из одних и тех же - собственных - ключей)
CryptSetKeyParam(...KP_ALGID, CALG_PRO_EXPORT...); // добавлялось для совместимости 1.1 на сервере с 2.0 на клиенте...
CryptImportKey(...симметричный ключ...совместный ключ...); // пытаюсь импортировать симметричный ключ из файла
Ошибка - Invalid type specified (8009000A)


Если этап 3) сделать в 2.0, то все хорошо. Но если полученный перешифрованный на клиентском публичном симметричный ключ отдать клиенту с 1.1 - то у него он не импортируется, с аналогичной ошибкой в CryptImportKey(...симметричный ключ...совместный ключ...). Что не так?

P.S. Подозреваю, что либо CryptGeKey в 2.0 создает симм. ключ, несовместимый с 1.1,
либо формат симметричного ключа, экспортированного при CALG_PRO_EXPORT, в 2.0 и 1.1 отличаются - в 2.0 какие-то новшества, не понимаемые 1.1. Можно ли их вычистить? (и заодно можно ли по формату экспорта симм. ключа определить, создан ли он в 2.0?)

P.P.S. Вы когда-то рекомендовали использовать CALG_SIMPLE_EXPORT, но он, во-первых, не очень безопасен вроде, а во-вторых уже созданные на сервере в 1.1 симметричные ключи - в 2.0 у клиентов понимаются только при задании CALG_PRO_EXPORT.
10.01.2007 11:02:07Василий
Можно попросить Вас проверить такую модификацию (на этапах 2 и 3):
исключить вызов CryptImportPublicKeyInfo, блоб PUBLICKEYBLOB получать экспортом самого ключа AT_KEYEXCHANGE.
10.01.2007 12:40:37Vlad Stepanov
Заменил в 1) использование результата CryptExportPublicKeyInfo на использование результата CryptExportKey, а в 2) и 3) заменил CryptImportPublicKeyInfo на вызов CryptImportKey - не помогло.

Также пробовал менять CALG_PRO_EXPORT на CALG_SIMPLE_EXPORT, в том числе вместе с переходом на CryptImportKey - не помогало.

Симметричный ключ в 2.0 всегда получается 71 байт, а в 1.1 - 67 байт.
Первый имеет структуру: #1#0#0#0#30'f'#0#0<далее 4 байта>#30'f'<остаток данных ключа, в конце 10 постоянные символов>
Второй имеет структуру:
#1#0#0#0#30'f'#0#0#30'f'<остаток данных ключа той же длины, в конце те же 10 постоянные символов>
В общем, визуально отличие на 4 байта с 9-го байта. Пробовал их убрать, чтоб получить 67-байтный ключ - такой ключ не воспринялся с ошибкой при импорте Bad Data (80090005). [такая же ошибка, если использовать CALG_SIMPLE_EXPORT]...
10.01.2007 15:41:39Vlad Stepanov
Если использовать CryptImportKey вместо CryptImportPublicKeyInfo (а также задавать CALG_PRO_EXPORT совместному ключу)
и при этом при переносе симметричного ключа из 2.0 в 1.1 вручную убирать из него те 4 байта с 9-й позиции (эти байты - это сигнатура #define G28147_MAGIC 0x374a51fd), то симметричный ключ, созданный в 2.0, - воспринимается версией 1.1

А нельзя ли не отказываться от использования CryptImportPublicKeyInfo?
А то у меня все ключи клиентов уже в формате CryptImportPublicKeyInfo (так понимаю, в этом формате что-то теряется по сравнению с форматом CryptImportKey) [формат CryptImportPublicKeyInfo был выбран, т.к. такой формат публичного ключа - совпадает с тем, что видно в сертификате у клиента, в поле публичного ключа, а из формата CryptImportKey не умею получать формат CryptImportPublicKeyInfo, не имея секретного ключа).
11.01.2007 16:10:07Василий
После консультаций с разработчиками выработаны рекомендации:
1) можно оставить CryptExportPublicKeyInfo/CryptImportPublicKeyInfo
2) блобы типа SIMPLEBLOB не обязаны быть совместимыми между разными версиями CSP, поэтому требуется самостоятельно контролировать версию CSP и перед импортом сессионного ключа из блоба преобразовывать этот блоб в соответствии с особенностями версии CSP.
24.01.2007 0:19:23Vlad Stepanov
Для информации.

Для совместимости симметричных ключей, созданных в КриптоПро 2.0, 3.0 - с КриптоПро 1.1 у клиента мне пришлось: а) при импорте ключа у клиента с 1.1 удалять в нем сигнатуру 374A51FDh с байта со смещением 8, уменьшая ключ с 71 до 67 байтов; б) исправлять байт версии в публичном ключе со смещением 1 - с 20h (версия 2) на 00h, если следующий (описанный в документации как зарезервированный) байт равен 0 (как оказалось, КриптоПро 2.0 и выше при перешифрации делает симметричные ключи, совместимые с 1.1 только если публичный ключ клиента - от 1.1; а при импорте ключа из формата хранения в сертификате (CryptImportPublicKeyInfo) и экспорте потом в обычный формат (CryptExportKey) - в КриптоПро 2.0 и выше байт версии публичного ключа меняется с 00h на 20h, в результате ключ не считается созданным в 1.1; правда, в следующем байте (т.е. том, что описан в документации как зарезервированный) для ключей от 2.0 и выше помечается 1, а для ключей от 1.1 - нет - поэтому их все же можно вручную отличить и исправить версию). Провел более 200 разных вариантов тестов (вроде таких: ключи сервера созданы в КриптоПро 1.1, симметричные в 2.0, перешифррованы на публичных клиента в 3.0, у клиента 1.1, 2.0 или 3.0) – у меня все работало.
24.01.2007 16:12:06Василий
Всё верно - отличие в версии блоба и в наличии MAGIC.