| ||||
| ||||
На сколько я понимаю, симметричные ключи в КриптоПро - это временные ключи, которые нельзя сохранить для последующего использования, например, на токене. В частности, у меня есть задача шифрования/расшифрования файлов с даными, используемых моей программой. Эти файлы должны храниться неопределённо долго. Если я не могу сохранить (экспортировать) сессионный ключ, то эта задача невыполнима. Конечно, можно воспользоваться двумя парами ключей для схемы Д-Х для экспорта, но это как-то некрасиво... PS. Как, в таком случае, работает аладиновский SecretDisk совместно с CryptoPro CSP ? | ||||
Ответы: | ||||
| ||||
Экспорт сессионного ключа в КриптоПро CSP возможен. Т.е. сделав сессионный ключ (например, CryptGenKey (hProv, CALG_G28147, CRYPT_EXPORTABLE, &hSessionKey)), пошифровав на нем, его можно экспортировать (CryptExportKey) в ключевой блоб. А дальше уже сами работайте с этим блобом (передавайте, сохраняйте и т.д.) Про аладиновский SecretDisk нуно спрашивать у его производителя. | ||||
| ||||
если порыться в этом форуме, то неоднократно писалось, что экспорт сессионного ключа реализуется только по схеме DH. В любом случае, этот код возвращает при экспорте ошибку NTE_BAD_KEY_STATE : // создание ключевого контейнера CPAcquireContext( &crtmgr_win->prov, asCont1.c_str(), CRYPT_NEWKEYSET + CRYPT_MACHINE_KEYSET, &crtmgr_win->pTable); // генерируем сессионный ключ CPGenKey( crtmgr_win->prov, CALG_G28147, CRYPT_EXPORTABLE, &crtmgr_win->key_sess ); CPExportKey( crtmgr_win->prov, crtmgr_win->key_sess, 0/*crtmgr_win->key_agree*/, SIMPLEBLOB, 0, pbSessionKey, &cbSessionKey ); Подскажите, где ошибка ? | ||||
| ||||
Вот готовая работающая процедура по простоту экспорту сессионного ключа: //*************************************************************************** // Простой экспорт сессионного ключа // void Base_ExportKey() { std::cout<<std::endl; std::cout<<" Simple export and import key "<<std::endl; std::cout<<"================================"<<std::endl; HCRYPTPROV hProv; HCRYPTKEY hKey,hPublicKey,hNewKey; // Инициализация контекста криптопровайдера (с указанием имени ключевого контейнера) if(!CryptAcquireContext(&hProv, "{EB57ED8A-CCCC-4bf5-8659-9DF2F05F24AD}", NULL, PROV_RSA_FULL, 0)) return; std::cout<<"Cryptographic provider initialized"<<std::endl; // Генарация ключа для тестирования if(!CryptGenKey(hProv, CALG_RC4, CRYPT_EXPORTABLE | CRYPT_ENCRYPT | CRYPT_DECRYPT, &hKey)) return; std::cout<<"Session key generated"<<std::endl; // Данные для тестирования char string[]="Test"; DWORD count=strlen(string); // Пробное шифрование данных if(!CryptEncrypt(hKey,0,true,0,(BYTE*)string,&count,strlen(string))) return; std::cout<<"Encryption completed"<<std::endl; // Получение ключа для экспорта ключа шифрования if(!CryptGetUserKey(hProv,AT_KEYEXCHANGE ,&hPublicKey)) return; std::cout<<"Public key is received"<<std::endl; count=0; // Первичное получение размера массива для экспорта ключа if(!CryptExportKey(hKey,hPublicKey,SIMPLEBLOB,0,NULL,&count)) return; // Инициализация массива для экспорта ключа BYTE* data=static_cast<BYTE*>(malloc(count)); ZeroMemory(data,count); // Окончательный экспорт ключа шифрования if(!CryptExportKey(hKey,hPublicKey,SIMPLEBLOB,0,data,&count)) return; std::cout<<"Key’s export completed"<<std::endl; // Импорт ключа шифрования из полученного массива данных if(!CryptImportKey(hProv,data,count,hPublicKey,0,&hNewKey)) return; std::cout<<"Key’s import completed"<<std::endl; // Проверочное расшифрование на полученном ключе count=strlen(string); if(!CryptDecrypt(hNewKey,0,true,0,(BYTE*)string,&count)) return; std::cout<<"Decryption completed"<<std::endl; // Тестовый вывод на экран std::cout<<"Result string: "<<string<<std::endl; // Освобождение контекстов локальных переменных free(data); CryptDestroyKey(hKey); CryptDestroyKey(hPublicKey); CryptDestroyKey(hNewKey); CryptReleaseContext(hProv,0); } //*************************************************************************** | ||||
| ||||
Кстати, в приведенном коде (не моем) используется алгоритм SIMPLEBLOB, который предполагает использование публичного ключа для шифрования сессионного. А самого публичного ключа нет. Если хочеться не использовать публичный ключ можно посоветовать использование типа PUBLICKEYBLOB. | ||||
| ||||
> Кстати, в приведенном коде (не моем) используется алгоритм SIMPLEBLOB, который предполагает использование публичного ключа для шифрования сессионного. А самого публичного ключа нет. Если хочеться не использовать публичный ключ можно посоветовать использование типа PUBLICKEYBLOB. Т.е. Вы хотите сказать, что публичного ключа в приведенном в примере контейнере нет ? Тогда почему функция не вылетает на этом: // Получение ключа для экспорта ключа шифрования if(!CryptGetUserKey(hProv,AT_KEYEXCHANGE ,&hPublicKey)) return; И я не понял, что означает эта строка: "Если хочеться не использовать публичный ключ можно посоветовать использование типа PUBLICKEYBLOB." не могли бы Вы пояснить? Спасибо. | ||||
| ||||
Хм... Короче, для хранения публичных ключей используется так называемый ключевой контейнер. А его нужно предварительно создать и записать в него собственно ключевую пару (пары). В приведенном коде этот этап опущен (он делается в другом месте). Вот код функции, собственно создающей новый ключевой контейнер и генерирующий ключевую пару: //****************************************************************************** // Подготовка ключевого контейнера. // void FirstAction() { HCRYPTPROV hProv; HCRYPTKEY hExKey, hSignKey; // Инициализация контекста криптопровайдера (с попыткой создания нового ключевого контейнера) if(!CryptAcquireContext(&hProv,"{EB57ED8A-CCCC-4bf5-8659-9DF2F05F24AD}",NULL,PROV_RSA_FULL,0)) { if(!CryptAcquireContext(&hProv,"{EB57ED8A-CCCC-4bf5-8659-9DF2F05F24AD}",NULL,PROV_RSA_FULL,CRYPT_NEWKEYSET)) return; // Генерация нового публичного ключа, предназначенного для обмена сессионными ключами if(!CryptGenKey(hProv,AT_KEYEXCHANGE,CRYPT_EXPORTABLE | CRYPT_ENCRYPT | CRYPT_DECRYPT,&hExKey)) return; // Генерация нового публичного ключа, предназначенного для создания цифровой подписи if(!CryptGenKey(hProv,AT_SIGNATURE,CRYPT_EXPORTABLE | CRYPT_ENCRYPT | CRYPT_DECRYPT,&hSignKey)) return; // Освобождение контекстов локальных переменных CryptDestroyKey(hExKey); CryptDestroyKey(hSignKey); } CryptReleaseContext(hProv,0); } //****************************************************************************** То есть просто напросто ключевого контейнера с именем "{EB57ED8A-CCCC-4bf5-8659-9DF2F05F24AD}" нет на исполняемой машине. Его первично нужно создать. | ||||
| ||||
А про PUBLICKEYBLOB можно (и нужно) почитать в MSDN. | ||||
| ||||
А насчет отсутсвия публичного ключа, то его нет в коде: CPExportKey( crtmgr_win->prov, crtmgr_win->key_sess, 0/*crtmgr_win->key_agree*/, SIMPLEBLOB, 0, pbSessionKey, &cbSessionKey ); А должен быть. | ||||
| ||||
Спасибо, я понял, что Вы имеете ввиду, но всё это мне известно. Дело в том, что экспорт сессионного ключа в криптопровайдере КриптоПро, возможен лишь по алгоритму D-H. Пример, приводимый Вами, содержит в себе обращение с майкрософтовскими базовыми провайдерами, которые позволяют выполниить экспорт на одной паре ключей - это механизм у меня не вызывает проблем в реализации. Меня интересует только CryptoPro CSP! | ||||
| ||||
Да, экспорт сессионного ключа A возможен только на ключе Диффи-Хеллмана К. Но, есть вариант получения ключа К из другого сессионного ключа Б заменой алгоритма на CALG_PRO_EXPORT или CALG_SIMPLE_EXPORT. Теперь вопрос - как сообщить получателю ключ Б для возможности расшифрования ключа А. Вариант - вообще не передавать ключ Б, а генерить его из пароля (или некоторой постоянной последовательности, в качестве которой можно взять значение некоторого открытого ключа)функцией CryptDeriveKey как на стороне отправителя, так и на стороне получателя. | ||||
| ||||
...Жалко, нет с собой исходников первой версии "КриптоАРМ"... Там я делал экспорт сессионного ключа в свой формат данных для всех криптопровайдеров. В особенности для криптопровайдера от КриптоПРО :) А вот как делал - уже не помню :) Возможно, в понедельник смогу предоставить примеры такого кода. | ||||