07.03.2006 14:08:14Проблемы с расшифровкой/проверкой подписи Ответов: 4
Иван
Для двух пользователей выпущены два сертификата КриптоПро. Процедуры подписи/проверки данных работают великолепно.

Написал тестовую программу, использующую сертификаты КриптоПро для шифрования+подписи. Ключевой носитель - реестр.
Вызываю CryptSignAndEncryptMessage - шифрование и подпись выполняются без проблем. Далее, в этой же программе вызываю CryptDecryptAndVerifyMessageSignature. Возникает окно КриптоПро, в котором сказано, что такой-то криптоконтейнер на ключевом носителе не найден.

Эти же процедуры используются для работы под MS Base Crypto-provider (выбор провайдера внутри программы) - все функционирует отлично.

В чем может быть дело?
 
Ответы:
08.03.2006 10:51:32Иван
Приведу код функции, которая нормально работает для eToken CSP, а с КриптоПро выдает окно, в котором в указанном криптоконтейнере "набор ключей не существует":

////////////////////
// расшифровка блока и проверка его подписи
//

bool __fastcall DecryptAndSignBlock(const char* EncryptedBlock, unsigned long EncryptedSize, unsigned long *Size, char* bufferForBlock, unsigned long SizeOfbufferForBlock,
unsigned long UserID,
char* ErrorStringBuffer,
int SizeOfErrorStringBuffer )
{
int Rez = false, fFind = false;
CEncrBufferHeader EncrHeader;
AnsiString asCert_GUID_1, asCert_GUID_2;
int i, iCertType, err;
HCRYPTPROV hCryptProv = NULL;

CERT_KEY_CONTEXT pvData;
CRYPT_KEY_PROV_INFO ppDat;

unsigned long sss = sizeof(pvData);
unsigned long s2 = sizeof(ppDat);

char ErrorString[260], cont[260];
PCCERT_CONTEXT pSignerCertContext = NULL; // проверка подписи
PCCERT_CONTEXT pReceiverCertContext = NULL; // расшифровка

WideString wsContainer, wsProv = "eToken Base Cryptographic Provider";
wchar_t wcProv[512];

wcscpy( wcProv, wsProv.Copy() );


// получим заголовок зашифрованного блока ---
memcpy( &EncrHeader, EncryptedBlock, sizeof(EncrHeader) );

asCert_GUID_1 = AnsiString( EncrHeader.CertGUID ); // отправитель
asCert_GUID_2 = AnsiString( EncrHeader.CertGUID_2 ); // получатель

// -----------
// найдем контекст нужного сертификата для расшифровки

pSignerCertContext = crtmgr_win->GetCertContext( asCert_GUID_1 );

pReceiverCertContext = crtmgr_win->GetCertContext( asCert_GUID_2 );

if( pSignerCertContext == NULL || pReceiverCertContext == NULL )
{
strcpy( ErrorString, "Необходимые сертификаты не найдены!" );
return 0;
}

for( i=0; i < crtmgr_win->pCertList->Count; i++ )
{
if( strcmp( ((ptCards_Store)(crtmgr_win->pCertList->Items[i]))->guid, EncrHeader.CertGUID_2) == 0 )
{
wsContainer = WideString( ((ptCards_Store)(crtmgr_win->pCertList->Items[i]))->conteiner );
strcpy( cont, ((ptCards_Store)(crtmgr_win->pCertList->Items[i]))->conteiner );

fFind = true;
break;
}
}

if( !fFind )
{
strcpy( ErrorString, "Не найден контейнер с секретным ключом !" );
return 0;
}

if( EncrHeader.ProvType[0] == 0 ) // CryptoPro
{
if( !CryptAcquireContext(
&hCryptProv,
NULL,//cont,
crtmgr_win->tCrypto.asProviderName.c_str(),
PROV_GOST_DH, // PROV_GOST_DH = 2
CRYPT_VERIFYCONTEXT ) )
{
err = GetLastError();
}
}

if( EncrHeader.ProvType[0] == 1 ) // eToken
{
if( !CryptAcquireContext(
&hCryptProv,
NULL,//cont,
"eToken Base Cryptographic Provider",
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT ) )
{
err = GetLastError();
}
}

// Зададим пароль для криптопровайдера, чтоб не спрашивать его по несколько раз
bool r2 = CryptSetProvParam( hCryptProv,
32, //PP_EXCHANGE_PIN,
crtmgr_win->asTokenPIN.c_str(),
0 );

// первый набор параметров -------------
if( !CertGetCertificateContextProperty(
pReceiverCertContext,
CERT_KEY_CONTEXT_PROP_ID,
&pvData,
&sss ) )
{
err = GetLastError();

pvData.cbSize = sizeof(pvData);
pvData.hCryptProv = hCryptProv;
pvData.dwKeySpec = AT_KEYEXCHANGE;

Rez = CertSetCertificateContextProperty(
pReceiverCertContext,
CERT_KEY_CONTEXT_PROP_ID,
CERT_STORE_NO_CRYPT_RELEASE_FLAG,
&pvData );

}

// второй набор параметров ------
if( !CertGetCertificateContextProperty(
pReceiverCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
&ppDat,
&s2 ) )
{
err = GetLastError();

ppDat.pwszContainerName = wsContainer.Copy();

if( EncrHeader.ProvType[0] == 1 ) // eToken
{
wsProv = "eToken Base Cryptographic Provider";
wcscpy( wcProv, wsProv.Copy() );
ppDat.pwszProvName = wcProv;
ppDat.dwProvType = PROV_RSA_FULL;
}

if( EncrHeader.ProvType[0] == 0 ) // CryptoPro
{
wsProv = WideString( crtmgr_win->tCrypto.asProviderName );
wcscpy( wcProv, wsProv.Copy() );
ppDat.pwszProvName = wcProv;
ppDat.dwProvType = PROV_GOST_DH;//2;
}

ppDat.cProvParam = 0;
ppDat.rgProvParam = NULL;
ppDat.dwFlags = CERT_SET_KEY_PROV_HANDLE_PROP_ID;
ppDat.dwKeySpec = AT_KEYEXCHANGE;

Rez = CertSetCertificateContextProperty(
pReceiverCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
0,
&ppDat );
}

//-------------
// Declare and initialize local variables.

CRYPT_DECRYPT_MESSAGE_PARA DecryptPara;
CRYPT_VERIFY_MESSAGE_PARA VerifyPara;
DWORD dwSignerIndex = 0;
BYTE *pbDecrypted;
DWORD cbDecrypted;

//-------------
// Создаем временное хранилище сертификатов

HCERTSTORE hMemStore = NULL;

if( hMemStore = CertOpenStore(
CERT_STORE_PROV_MEMORY, // The memory provider type
0, // The encoding type is not needed
hCryptProv, // Use the default HCRYPTPROV
0, // Accept the default dwFlags
NULL // pvPara is not used
))
{
//printf("The memory store was created successfully.\n");
}
else
{
strcpy( ErrorString, "An error occurred during creation of the memory store!" );
return 0;
}

// добавим сертификат получателя для расшифровки ---
Rez = CertAddCertificateContextToStore(
hMemStore,
pReceiverCertContext,
CERT_STORE_ADD_REPLACE_EXISTING,
NULL );

//-------------
// Инициализируем структуры для дешифрования и проверки ЭЦП

memset( &DecryptPara, 0, sizeof(CRYPT_DECRYPT_MESSAGE_PARA));

DecryptPara.cbSize = sizeof(CRYPT_DECRYPT_MESSAGE_PARA);
DecryptPara.dwMsgAndCertEncodingType = MY_ENCODING_TYPE;
DecryptPara.cCertStore = 1;
DecryptPara.rghCertStore = &hMemStore;

memset( &VerifyPara, 0, sizeof(CRYPT_VERIFY_MESSAGE_PARA));

VerifyPara.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
VerifyPara.dwMsgAndCertEncodingType = MY_ENCODING_TYPE;
VerifyPara.hCryptProv = hCryptProv;
VerifyPara.pfnGetSignerCertificate = CardsCryptGetSignerCertificateCallback;
VerifyPara.pvGetArg = (void *)pSignerCertContext;

pbDecrypted = NULL;
cbDecrypted = 0;

//-------------
unsigned long cbSignedAndEncryptedBlob = EncryptedSize - sizeof(EncrHeader);

if(!(CryptDecryptAndVerifyMessageSignature(
&DecryptPara,
&VerifyPara,
dwSignerIndex,
EncryptedBlock + sizeof(EncrHeader),
cbSignedAndEncryptedBlob,
NULL, // pbDecrypted
Size,
NULL,
NULL)))
{
err = GetLastError();

strcpy( ErrorString, "Ошибка при инициализации дешифрования" );
return 0;
}

//------------

if(!(CryptDecryptAndVerifyMessageSignature(
&DecryptPara,
&VerifyPara,
dwSignerIndex,
EncryptedBlock + sizeof(EncrHeader),
cbSignedAndEncryptedBlob,
bufferForBlock,
Size,
NULL,
NULL)))
{
bufferForBlock = NULL;
}

//------------
CertCloseStore( hMemStore, 0);

CryptReleaseContext( hCryptProv, 0);

return true;
}
10.03.2006 12:02:57Василий
Зашифрование на сертификате получателя не требует секретного ключа получателя. Расшифрование - требует.
(Это гарантирует, что никто, кроме обладателя этого секретного ключа, не сможет расшифровать сообщение. Получателей может быть несколько и для расшифрования будет пригоден любой из соответствующих секретных ключей). Поэтому, контекст сертификата получателя должен быть открыт из хранилища, в котором этот сертификат установлен с привязкой к соответствующему секретному ключу получателя в контейнере.
10.03.2006 13:27:22Иван
> Зашифрование на сертификате получателя не требует секретного ключа получателя. Расшифрование - требует.
(Это гарантирует, что никто, кроме обладателя этого секретного ключа, не сможет расшифровать сообщение. Получателей может быть несколько и для расшифрования будет пригоден любой из соответствующих секретных ключей). Поэтому, контекст сертификата получателя должен быть открыт из хранилища, в котором этот сертификат установлен с привязкой к соответствующему секретному ключу получателя в контейнере.

Я так и делаю, в приведенном коде это есть:
создаю временное хранилище (hMemStore), помещаю туда сертификат с сооотв. опциями, в структуре делаю соотв. запись(DecryptPara.rghCertStore = &hMemStore;)...


PS Вы мне послали на rysev@orel.ru письмо, оно случайно удалилось. Если там были более подробные разъяснения, повторите, пожалуйста его. Мне очень срочно необходимо решить эту проблемую. Наш новый клиент желает видеть везде КриптоПро, необходимо предоставить ему демонстрацию продукта...

Спасибо!
10.03.2006 15:47:57Василий
Можно прокомментировать
pReceiverCertContext = crtmgr_win->GetCertContext( asCert_GUID_2 );
что за зверь crtmgr_win ? Откуда читается сертификат и, главное, как он туда попал ?