Рутокен ЭЦП, расшифровка сообщения.

Добрый день. Пытаюсь разобраться со следующей проблемой.
Есть сообщение, которое было зашифрованно через CryptEncryptMessage(...) следующим образом

DWORD CProviderCSP::Encrypt(const BYTE* pbtData, UINT nSize, const CERT_CONTEXT* pRecipientCert,BYTE*& pdtEncrypted, DWORD& dwEncryptedSize)
{
    ASSERT(m_pcryptoAPI!=NULL);
//заполняем параметры 
    CRYPT_ENCRYPT_MESSAGE_PARA pMsgParam;
    pMsgParam.cbSize=sizeof(CRYPT_ENCRYPT_MESSAGE_PARA);
    memset(&pMsgParam, 0, sizeof(CRYPT_ENCRYPT_MESSAGE_PARA));
    pMsgParam.cbSize =  sizeof(CRYPT_ENCRYPT_MESSAGE_PARA);
    pMsgParam.dwMsgEncodingType = PKCS_7_ASN_ENCODING|X509_ASN_ENCODING;
    pMsgParam.hCryptProv = NULL;
    pMsgParam.ContentEncryptionAlgorithm.pszObjId = "1.2.643.2.2.21";  //szOID_CP_GOST_28147
    const CERT_CONTEXT* pCert[1];
    pCert[0]=pRecipientCert;
    pdtEncrypted=NULL;
    DWORD dwErr=ERROR_SUCCESS;
    BOOL bRes=CryptEncryptMessage(&pMsgParam,1,pCert,pbtData,nSize,NULL,&dwEncryptedSize);
    if(bRes)
    {
        pdtEncrypted=new BYTE[dwEncryptedSize];
        if(pdtEncrypted==NULL)
            dwErr=E_OUTOFMEMORY;
    }
    else
        dwErr=GetLastError();
    
    if(dwErr==ERROR_SUCCESS)
    {
        if(!CryptEncryptMessage(&pMsgParam,1,pCert,pbtData,nSize,pdtEncrypted,&dwEncryptedSize))
        {
            dwErr=GetLastError();
            delete [] pdtEncrypted;
            pdtEncrypted=NULL;
        }
    }
    return dwErr;


}

Необходимо расшифровать его при помощи PKCS#11 библиотеки. Пытаюсь сделать так:

....
//Инициализация библиотеки, С_Logon -ОК
....
        CK_OBJECT_CLASS     lPrivateKeyClass = CKO_SECRET_KEY;
    CK_KEY_TYPE        lKeyType = CKK_GOST28147;
    CK_BBOOL bDesrypt=CK_TRUE;
    CK_ATTRIBUTE    stAttrFind[] = 
    {
        { CKA_DECRYPT, &bDesrypt, sizeof( CK_BBOOL ) },
        //{ CKA_KEY_TYPE, &lKeyType, sizeof( lKeyType ) }, 
        //Если раскомментировать строку то C_FindObjects вернет 0 найденных объектов
        { CKA_ID, ( CK_CHAR* )(( const char* )astrID ), ( CK_ULONG )astrID.GetLength() },
    };

    lRetCode = pstFL->C_FindObjectsInit( m_hSession, &stAttrFind[0], sizeof( stAttrFind ) / sizeof( stAttrFind[0] ) );
    ASSERT( lRetCode == CKR_OK );

    unsigned long    lCount;
    CK_OBJECT_HANDLE    hObject;
    lRetCode = pstFL->C_FindObjects(m_hSession, &hObject, 1, &lCount );

    pstFL->C_FindObjectsFinal( m_hSession );
    
    if( lRetCode == CKR_OK && lCount == 0 )
        lRetCode = 0x0000FFFD;    // Ключевой объект не найден
    if( lRetCode == CKR_OK )
    {
        ASSERT( hObject != ( CK_OBJECT_HANDLE )NULL );
        
        CK_MECHANISM    stMechanizm;
        CK_BYTE encIV[8];    
        CK_ULONG encIVLength = sizeof(encIV);
        stMechanizm.mechanism=CKM_GOSTR3410;
        //stMechanizm.mechanism=CKM_GOST28147; 
        //Если подставить механизм CKM_GOST28147 то C_DecryptInit 
        //возвращает 0x00000063 CKR_KEY_TYPE_INCONSISTENT 
        stMechanizm.pParameter=NULL;
        stMechanizm.ulParameterLen=0;
        lRetCode = pstFL->C_DecryptInit(m_hSession, &stMechanizm, hObject );
        if( lRetCode == CKR_OK )
        {
            lRetCode=pstFL->C_Decrypt(m_hSession, (CK_BYTE_PTR)pbtData, nSize, pbtDecrypted, &dwDecryptedSize);
            if(lRetCode == CKR_OK)
            {
                pbtDecrypted=new BYTE[dwDecryptedSize];
                memset(pbtDecrypted,0,dwDecryptedSize);
                //Тут получаю код ошибки 0x00000041 CKR_ENCRYPTED_DATA_LEN_RANGE
                lRetCode=pstFL->C_Decrypt(m_hSession, (CK_BYTE_PTR)pbtData, nSize, (CK_BYTE_PTR)pbtDecrypted, &dwDecryptedSize);
            }
        }
    }

Я сильно подозреваю что проблема в моем недопонимании. Видимо зашифрованное сообщение содержит что то еще помимо непосредственно данных. Но вопрос тогда, как мне выдернуть из него сами данные, которые я должен передать на вход функции C_Decrypt
Заранее спасибо за ответ.

P.S.
Когда сообщение шифруется в качестве сертификата получателя передается сертификат, который установлен на токене.

Re: Рутокен ЭЦП, расшифровка сообщения.

Давайте начнем с того, что вы расскажете какой криптопровайдер (для интерфейса CryptoAPI) вы используете?

А потом, конечно, вам надо будет разобраться как получаются секретные ключи из асимметричных ключевых пар (сразу подскажу, что через механизм выработки ключей согласования).

Когда вы разберетесь, вы скорее всего согласуете параметры  выработки ключей между CryptoAPI и PKCS#11 и скорее всего добьетесь того, чего вы хотите.

Но сразу скажу, что путь вы выбрали далеко не самый простой :)
А что вам мешает использовать одинаковый интерфейс на обеих сторонах?

(2015-10-07 13:32:22 отредактировано SvetovidovDM)

Re: Рутокен ЭЦП, расшифровка сообщения.

С CryptoAPI я использую КриптоПро. Однако по условиям задачи вместо него может быть любой другой криптопровайдер с поддержкой ГОСТ криптографии (Vipnet и пр.)
Путь естественно не самый простой, но по условиям задачи должна быть обеспечена как работа с "обычными" криптопровайдерами (через CryptoAPI), так и работа с PKCS#11. Причем должна быть обратная совместимость версий продукта, а поскольку предыдущая версия использовала CAPICOM (EnvelopedData) и библиотека расшифровки на сервере ожидает данные именно в таком формате то выбор у меня совсем не богатый.
По поводу получения секретных ключей. Возможно я чего то недопонимаю, но при расшифровке у меня есть только закрытый ключ, а открытый находится у клиента. Или открытый ключ можно вытащить из зашифрованных данных?

UPD.
Возможно будет интересно:
Скачал SDK и попытался выполнить пример на генерацию ключа GOST 28147-89
Опять таки инициализация проходит без проблем, а вот дальше на вызове

rv = pFunctionList->C_GenerateKey(hSession,                                 // Хэндл открытой сессии
                                 &ckmGOST28147_89_KeyGenMech,              // Используемый механизм генерации ключа
                                 attrGOST28147_89SecKeyTmpl,               // Шаблон для создания секретного ключа
                                 arraysize(attrGOST28147_89SecKeyTmpl),    // Размер шаблона секретного ключа
                                 &hGOST28147_89SecKey);                    // Хэндл секретного ключа
if (rv != CKR_OK)
{
         printf(" -> Failed\n"
                   "Generation GOST key failed!\n");
         break;
}
         printf(" -> OK\n");

получаю ошибку 0x000000d1 CKR_TEMPLATE_INCONSISTENT
и это вгоняет меня в еще больший ступор...

Re: Рутокен ЭЦП, расшифровка сообщения.

Смотрите:

Через рутокеновский интерфейс pkcs#11 вы сможете работать только с ключами ViPNet CSP и SignalCOM CSP.
Да и то, это только в случае использования моделей Рутокен ЭЦП.

Если вы используете КриптоПро CSP то вы можете использовать pkcs#11 от Крипто-ПРО.

Это в случае, если вы хотите видеть одни и те же объекты на одном и том же устройстве через разные интерфейсы.
Я так изначально вас понял.
Может быть у вас и нет такой задачи - тогда это несколько попроще.

Перед шифрованием данных должен быть произведен обмен открытыми ключами.
Если у вас есть цифровой сертификат другого абонента - считайте что обмен проведен. Сертификат содержит открытый ключ.

Re: Рутокен ЭЦП, расшифровка сообщения.

Кирилл Мещеряков пишет:

Смотрите:

Через рутокеновский интерфейс pkcs#11 вы сможете работать только с ключами ViPNet CSP и SignalCOM CSP.
Да и то, это только в случае использования моделей Рутокен ЭЦП.

Если вы используете КриптоПро CSP то вы можете использовать pkcs#11 от Крипто-ПРО.

Это в случае, если вы хотите видеть одни и те же объекты на одном и том же устройстве через разные интерфейсы.
Я так изначально вас понял.
Может быть у вас и нет такой задачи - тогда это несколько попроще.

Перед шифрованием данных должен быть произведен обмен открытыми ключами.
Если у вас есть цифровой сертификат другого абонента - считайте что обмен проведен. Сертификат содержит открытый ключ.

Смотрите. На токене лежит ключ, сгенерированный через PKCS#11 библиотеку.
Теперь поясню задачу которую я пытаюсь решить.
На стороне клиента нужно реализовать шифрование и расшифровку данных через PKCS#11 библиотеку, в том случае если у клиента нет КриптоПро и других программных криптопровайдеров. При этом данные зашифрованные через PKCS#11 должны расшифровываться на сервере через CryptDecryptMessage.
Открытый ключ (сертификат получателя в кодировке BASE64) жестко прописан в клиентской части.
То есть у меня нет задачи увидеть одни и тот же объекты на токене через разные интерфейсы. Есть задача тупо зашифровать данные используя заданный открытый ключ.
Почитал спецификацию CryptEncryptMessage и выяснил, что "при шифровании используется эфемерный  ключ отправителя и VKO. Открытая часть эфемерного ключа передается в сообщении". Соответственно нужно аналогичное решение через PKCS#11.

Re: Рутокен ЭЦП, расшифровка сообщения.

понятно, в таком виде задача выглядит вполне реальной.

В нашем SDK найдите пример DeriveWrap - там есть работа с эфемерным ключом.
Есть и его выработка и шифрование.

Re: Рутокен ЭЦП, расшифровка сообщения.

Кирилл Мещеряков пишет:

понятно, в таком виде задача выглядит вполне реальной.

В нашем SDK найдите пример DeriveWrap - там есть работа с эфемерным ключом.
Есть и его выработка и шифрование.

Нашел пример, запустил.
Получил ошибку
0x000000d1 CKR_TEMPLATE_INCONSISTENT тут:

printf(" Creating the GOST 28147-89 key to wrap");
rv = pFunctionList->C_CreateObject(hSession,
                                   attrGOST28147_89KeyToWrap,
                                   arraysize(attrGOST28147_89KeyToWrap),
                                   &hTempKey);

Re: Рутокен ЭЦП, расшифровка сообщения.

запустите пример подложив рядом со скомпилированным примером библиотеку из того же SDK - должно сработать

Re: Рутокен ЭЦП, расшифровка сообщения.

Да, так заработало. Видимо у меня старая версия библиотеки бралась по умолчанию.
Но тем не менее основного вопроса это не сняло. А суть его в следующем:
Как из зашифрованного сообщения выдернуть открытый ключ?
Есть ли какие то штатные средства, или нужно парсить это сообщение руками?

Re: Рутокен ЭЦП, расшифровка сообщения.

SvetovidovDM пишет:

Как из зашифрованного сообщения выдернуть открытый ключ?
Есть ли какие то штатные средства, или нужно парсить это сообщение руками?

в pkcs11 таких инструментов нет, открытый ключ лучше брать из сертификата, а вот остальные параметры придется парсить

(2015-10-08 13:41:13 отредактировано SvetovidovDM)

Re: Рутокен ЭЦП, расшифровка сообщения.

Кирилл Мещеряков пишет:
SvetovidovDM пишет:

Как из зашифрованного сообщения выдернуть открытый ключ?
Есть ли какие то штатные средства, или нужно парсить это сообщение руками?

в pkcs11 таких инструментов нет, открытый ключ лучше брать из сертификата, а вот остальные параметры придется парсить

Ну открытый ключ взять из сертификата не получится потому что он генерится динамически при шифровании и передается вместе с сообщением. То есть механика моих действий для расшифровки должна быть следующей:
1. Получить из сообщения зашифрованный текст
2. Получить из сообщения открытый ключ отправителя.
3. Получить из сообщения зашифрованный сессионный ключ.
3. На основании ключа отправителя и ключа получателя сгенерировать ключ согласования.
4. При помощи него расшифровать сессионный ключ
5. Расшифровать этим ключом сообщение.

Поправьте, пожалуйста, если я не прав.

Re: Рутокен ЭЦП, расшифровка сообщения.

вроде все верно и мне тут коллеги подсказали что вам может вот это помочь, посмотрите http://habrahabr.ru/company/aktiv-company/blog/249723/