04.01.2001 13:11:28Експорт сессионного ключа Ответов: 1
Maks
Добрый день!
Никак не получается экспортировать сессионный ключ. Вроде сделал все так же как в MSDN'овых примерах:
на передающей стороне (A)
- создал ключ обмена;
- экспортировал его;
на принимающей стороне (B)
- импортировал этот ключ обмена;
- создал сессионный ключ;
- попытался его экспортировать (от B к A) на полученном ключе обмена.
Возвращается ошибка NTE_BAD_KEY_STATE (Key not valid for use in specified state).
Может кто подскажет, как с этим бороться?
 
Ответы:
05.01.2001 11:20:55Игорь Курепкин
Из приведенного ниже примера ясно что экспортируется совсем не сессионный ключ.

Попробую рассказать.
AT_SIGNATURE долговременный секретный ключ ЭЦП пользователя
AT_EXCHANGE долговременный секретный ключ шифрования (обмена) пользователя

Как известно, существует две разновидности шифрования с открытими ключами:
RSA и Diffy-Helman

В первом:
шифрование данных (или сессионного ключа) производится только с использованием открытого ключа получателя. Соответственно для их расшифрования требуется только секретный ключ получателя.

Во втором алгоритме используется совместный ключ, который получается из секретного отправителя и открытого получателя (если не принимать во внимание эфемрный DH).

Значит схема должа выглядеть так (для функций самого низкого уровня - Base, о чем в MSDN есть примеры)

GenKey - генерит долговременные ключ AT_SIGNATURE или AT_EXCHANGE в контейнер (ключевой носитель).

GetUserKey - (AT_SIGNATURE или AT_EXCHANGE) возвращает соответствующий им handle

sessionKey = GenKey (ALG_ID=CALG_G28147) еще раз генерит сессионный ключ, на котором с симметричным алгоритмом будут шифроваться данные.

для RSA
CALG_RC2 RC2 block cipher
CALG_RC4 RC4 stream cipher

Для Diffie-Hellman
CALG_DH_EPHEM Specifies an "Ephemeral" Diffie-Hellman key.
CALG_DH_SF Specifies a "Store and Forward" Diffie-Hellman key.

Для ГОСТ ALG_ID=CALG_G28147

Теперь именно handle сессионного ключа должен использоваться для его экспорта.

Для этого нужно импортнуть чужой открытый ключ (соответственно на другой стороне он должен быть экспорчен ExportKey (PUBLICKEYBLOB))
T.e.
ret = CryptImportKey (hProv,(BYTE*) pub_key_blob,(DWORD) pub_key_blob_len,0,0,&hPubKey)

После этого имеем:
-сессионный ключ
-открытый ключ получателя
(в схеме RSA и эфемерного DH больше ничего не нужно)

Делаем экспорт сессионного ключа ExportKey (..sessionKey... чужой открытый)

Соответственно на приемной стороне обратная.

Для диффи-хелмана с нашим провайдером (да и ихним провйдером схема немного другая).
Ниже привожу пример.
Но мне кажется лучше пользоваться функция low level message functions или simplefied, там гораздо все проще. Почти что один вызов: CryptEncryptMessage.
Правда для их использования нужет сертификат открытого ключа.

Вот примеры:

BOOL
ProCSPEncryptFile(
LPBYTE ContainerName,
LPBYTE pbRecipientPubKeyBlob,
DWORD dwLenRecipientPubKeyBlob,
LPBYTE *pbSeanseKeyBlob,
LPDWORD pdwLenSeanseKeyBlob,
LPBYTE FileNamePlainText,
LPBYTE FileNameCipherText,
DWORD ContainerMode,
DWORD dwModLen,
DWORD dwCipherMode,
DWORD dwExprtMode,
DWORD dwMixMode,
LPBYTE pbIV,
LPBYTE pbSV
)
{ // Имена ключей
FILE *hSource = NULL;
FILE *hDst = NULL;
HCRYPTPROV hCreateContainer=0;
HCRYPTKEY hTempKey=0,hExchangeKeyPair=0, hSessionKey=0, hUserAgreeKey=0;
PVTableProvStruc pVTable=NULL;
LPBYTE pExchPubKeyBlob=NULL;
LPBYTE pbSecretKeyBlob=NULL;
DWORD dwFlags=0, dwError, dwBlobLen, dwPubBlobLen, Param, dwDataLen, dwCount;
LPWORD pFlag=(LPWORD)&dwFlags;
BYTE pbBuffer[BUFFER_SIZE];
int eof = 0;

if(!CPAcquireContext(&hCreateContainer,ContainerName,ContainerMode,pVTable)) {
dwError=GetLastError();
if (dwError!=NTE_BAD_KEYSET) {
printf("Error %x during CryptAcquireContext(OpenKeySet)!\n", GetLastError());
return FALSE;
}
else {
if(!CPAcquireContext(&hCreateContainer,ContainerName,CRYPT_NEWKEYSET,pVTable)) {
printf("Error %x during CryptAcquireContext(CreateKeySet)!\n", GetLastError());
return FALSE;
}
}
}

*(pFlag+1)=(WORD)dwModLen;
if(!CPGetUserKey(hCreateContainer, USERKEY_KEYEXCHANGE, &hExchangeKeyPair)) {
if (GetLastError()==NTE_NO_KEY) {
if(!CPGenKey(hCreateContainer, AT_KEYEXCHANGE,dwFlags,&hExchangeKeyPair)) {
printf("Error %x during CryptGenKey!\n", GetLastError());
goto done;
}
}
else {
printf("Error %x during CryptGetUserKey!\n", GetLastError());
goto done;
}
}

if(!CPExportKey(hCreateContainer, hExchangeKeyPair, hTempKey, PUBLICKEYBLOB, 0, NULL, &dwPubBlobLen)) {
printf("Error %x computing blob length!\n", GetLastError());
goto done;
}
pExchPubKeyBlob = malloc(dwPubBlobLen);
if(!pExchPubKeyBlob) {
SetLastError(NTE_NO_MEMORY);
goto done;
}
dwBlobLen = dwPubBlobLen;
// Export key into a PUBLICKEYBLOB key blob.
if(!CPExportKey(hCreateContainer, hExchangeKeyPair, hTempKey, PUBLICKEYBLOB, 0, pExchPubKeyBlob , &dwBlobLen)) {
printf("Error %x during CPExportKey!\n", GetLastError());
goto done;
}

//Создадим сессионный ключ
if(!CPGenKey(hCreateContainer, CALG_G28147,CRYPT_EXPORTABLE ,&hSessionKey)) {
goto done;
}

if(pbIV) {
if(!CPSetKeyParam(hCreateContainer,hSessionKey,KP_IV,(LPBYTE)pbIV,0)) {
printf("Error %x during CryptSetKeyParam!\n", GetLastError());
goto done;
}
}
else {
//Знаем, что pbIV состоит из 8 байт
pbIV=malloc(8);
if(!pbIV)
goto done;
if(!CPGetKeyParam(hCreateContainer,hSessionKey,KP_IV,(LPBYTE)pbIV,&dwDataLen,0)) {
printf("Error %x during CryptSetKeyParam!\n", GetLastError());
goto done;
}
}
Param=dwCipherMode;
if(!CPSetKeyParam(hCreateContainer,hSessionKey,KP_MODE,(LPBYTE)&Param,0)) {
printf("Error %x during CryptSetKeyParam!\n", GetLastError());
goto done;
}
if(!dwMixMode) {
Param=dwMixMode;
if(!CPSetKeyParam(hCreateContainer,hSessionKey,KP_MIXMODE,(LPBYTE)&Param,0)) {
printf("Error %x during CryptSetKeyParam!\n", GetLastError());
goto done;
}
}

// создаём Agreed Key
// Вырабатывается ключ парной связи
if(!CPImportKey(hCreateContainer, pbRecipientPubKeyBlob, dwLenRecipientPubKeyBlob,
hExchangeKeyPair, 0, &hUserAgreeKey)) {
goto done;
}

if(pbSV) {
if(!CPSetKeyParam(hCreateContainer, hUserAgreeKey, KP_IV, pbSV, 0)) {
goto done;
}
}
else {
//Знаем, что pbIV состоит из 8 байт
pbSV=malloc(8);
if(!pbSV)
goto done;
if(!CPGetKeyParam(hCreateContainer,hUserAgreeKey,KP_IV,(LPBYTE)pbSV,&dwDataLen,0)) {
printf("Error %x during CryptSetKeyParam!\n", GetLastError());
goto done;
}
}

Param=dwExprtMode;
if(!CPSetKeyParam(hCreateContainer, hUserAgreeKey,KP_ALGID,(LPBYTE)&Param,0)) {
goto done;
}

if(!CPExportKey(hCreateContainer, hSessionKey, hUserAgreeKey, SIMPLEBLOB, 0, NULL, &dwBlobLen)) {
printf("Error %x computing blob length!\n", GetLastError());
goto done;
}
pbSecretKeyBlob = malloc(dwBlobLen);
if(!pbSecretKeyBlob) {
SetLastError(NTE_NO_MEMORY);
goto done;
}
// Экспорт ключа шифрования
if(!CPExportKey(hCreateContainer ,
hSessionKey, hUserAgreeKey, SIMPLEBLOB , 0, pbSecretKeyBlob, &dwBlobLen)) {
goto done;
}
*pbSeanseKeyBlob = pbSecretKeyBlob;

// Open source file.
if((hSource=fopen(FileNamePlainText,"rb"))==NULL) {
printf("Error opening source file!\n");
goto done;
}
// Open destination file.
if((hDst=fopen(FileNameCipherText,"wb+"))==NULL) {
printf("Error opening destination file!\n");
goto done;
}
//ШИФРОВАНИЕ
// Encrypt source file and write to destination file.
do {
// Read up to BLOCK_SIZE bytes from source file.
dwCount = fread(pbBuffer, 1, BUFFER_SIZE, hSource);
if(ferror(hSource)) {
printf("Error reading data!\n");
goto done;
}
eof=feof(hSource);


if(!CPEncrypt(hCreateContainer,hSessionKey, 0, eof, 0, pbBuffer, &dwCount, BUFFER_SIZE)) {
printf("Error %x during CryptEncrypt!\n", GetLastError());
goto done;
}

fwrite(pbBuffer, 1, dwCount, hDst);
if(ferror(hDst)) {
printf("Error writing data!\n");
goto done;
}
} while(!feof(hSource));

// Close source file.
if(hSource != NULL) fclose(hSource);
if(hDst != NULL) fclose(hDst);
if((hCreateContainer)!=0) CPReleaseContext(hCreateContainer,0);
return TRUE;
done:
if(hSource != NULL) fclose(hSource);
if(hDst != NULL) fclose(hDst);
if((hCreateContainer)!=0) CPReleaseContext(hCreateContainer,0);
if(pbSecretKeyBlob) free(pbSecretKeyBlob);
if(pExchPubKeyBlob) free(pExchPubKeyBlob);
return FALSE;

}



BOOL
ProCSPDecryptFile(
LPBYTE ContainerName,
LPBYTE pbReciverPubKeyBlob,
DWORD dwLenRecipientPubKeyBlob,
LPBYTE pbSeanseKeyBlob,
DWORD dwLenSeanseKeyBlob,
LPBYTE FileNameCipherText,
LPBYTE FileNamePlainText,
DWORD ContainerMode,
DWORD dwModLen,
DWORD dwCipherMode,
DWORD dwExprtMode,
DWORD dwMixMode,
LPBYTE pbIV,
LPBYTE pbSV
)
{ // Имена ключей
FILE *hSource = NULL;
FILE *hDst = NULL;
HCRYPTPROV hCreateContainer=0;
HCRYPTKEY hTempKey=0,hExchangeKeyPair=0, hSessionKey=0, hUserAgreeKey=0;
PVTableProvStruc pVTable=NULL;
LPBYTE pExchPubKeyBlob=NULL;
LPBYTE pbSecretKeyBlob=NULL;
DWORD dwFlags, dwError, dwBlobLen, dwPubBlobLen, Param, dwCount;
LPWORD pFlag=(LPWORD)&dwFlags;
BYTE pbBuffer[BUFFER_SIZE];
int eof = 0;

if(!CPAcquireContext(&hCreateContainer,ContainerName,ContainerMode,pVTable)) {
dwError=GetLastError();
if (dwError!=NTE_BAD_KEYSET) {
printf("Error %x during CryptAcquireContext(OpenKeySet)!\n", GetLastError());
return FALSE;
}
else {
if(!CPAcquireContext(&hCreateContainer,ContainerName,CRYPT_NEWKEYSET,pVTable)) {
printf("Error %x during CryptAcquireContext(CreateKeySet)!\n", GetLastError());
return FALSE;
}
}
}

*(pFlag+1)=(WORD)dwModLen;
if(!CPGetUserKey(hCreateContainer, USERKEY_KEYEXCHANGE, &hExchangeKeyPair)) {
if (GetLastError()==NTE_NO_KEY) {
if(!CPGenKey(hCreateContainer, USERKEY_KEYEXCHANGE,dwFlags,&hExchangeKeyPair)) {
printf("Error %x during CryptGenKey!\n", GetLastError());
goto done;
}
}
else {
printf("Error %x during CryptGetUserKey!\n", GetLastError());
goto done;
}
}

if(!CPExportKey(hCreateContainer, hExchangeKeyPair, hTempKey, PUBLICKEYBLOB, 0, NULL, &dwPubBlobLen)) {
printf("Error %x computing blob length!\n", GetLastError());
goto done;
}
pExchPubKeyBlob = malloc(dwPubBlobLen);
if(!pExchPubKeyBlob) {
SetLastError(NTE_NO_MEMORY);
goto done;
}
dwBlobLen = dwPubBlobLen;
// Export key into a PUBLICKEYBLOB key blob.
if(!CPExportKey(hCreateContainer, hExchangeKeyPair, hTempKey, PUBLICKEYBLOB, 0, pExchPubKeyBlob , &dwBlobLen)) {
printf("Error %x during CPExportKey!\n", GetLastError());
goto done;
}

// Вырабатывается ключ парной связи
if(!CPImportKey(hCreateContainer,
pbReciverPubKeyBlob, // Открытый ключ
dwLenRecipientPubKeyBlob,
hExchangeKeyPair, 0,
&hUserAgreeKey)) {
goto done;
}

if(pbSV ) //&& (dwExprtMode==CALG_PRO_EXPORT)
if(!CPSetKeyParam(hCreateContainer, hUserAgreeKey,KP_IV,pbSV,0)) {
goto done;
}

Param=dwExprtMode;
if(!CPSetKeyParam(hCreateContainer, hUserAgreeKey,KP_ALGID,(LPBYTE)&Param,0)) {
goto done;
}

// Импорт ключа шифрования
if(!CPImportKey(hCreateContainer,
pbSeanseKeyBlob,
dwLenSeanseKeyBlob, hUserAgreeKey, 0, &hSessionKey)) {//SIMPLEBLOB_SIZE
goto done;
}

if(!CPSetKeyParam(hCreateContainer,hSessionKey,KP_IV,pbIV,0)) {
goto done;
}
Param=dwCipherMode;
if(!CPSetKeyParam(hCreateContainer,hSessionKey,KP_MODE,(LPBYTE)&Param,0)) {
printf("Error %x during CryptSetKeyParam!\n", GetLastError());
goto done;
}
if(!dwMixMode) {
Param=dwMixMode;
if(!CPSetKeyParam(hCreateContainer,hSessionKey,KP_MIXMODE,(LPBYTE)&Param,0)) {
printf("Error %x during CryptSetKeyParam!\n", GetLastError());
goto done;
}
}

// Open destination file.
if((hDst=fopen(FileNamePlainText,"wb+"))==NULL) {
printf("Error opening source file!\n");
goto done;
}
// Open source file.
if((hSource=fopen(FileNameCipherText,"rb"))==NULL) {
printf("Error opening destination file!\n");
goto done;
}

//ШИФРОВАНИЕ
// Encrypt source file and write to destination file.
do {
// Read up to BLOCK_SIZE bytes from source file.
dwCount = fread(pbBuffer, 1, BUFFER_SIZE, hSource);
if(ferror(hSource)) {
printf("Error reading data!\n");
goto done;
}
eof=feof(hSource);


if(!CPDecrypt(hCreateContainer,hSessionKey, 0, eof, 0, pbBuffer, &dwCount)) {
printf("Error %x during CryptEncrypt!\n", GetLastError());
goto done;
}

fwrite(pbBuffer, 1, dwCount, hDst);
if(ferror(hDst)) {
printf("Error writing data!\n");
goto done;
}
} while(!feof(hSource));


// Close source file.
if(hSource != NULL) fclose(hSource);
if(hDst != NULL) fclose(hDst);
if((hCreateContainer)!=0) CPReleaseContext(hCreateContainer,0);
return TRUE;
done:
if(hSource != NULL) fclose(hSource);
if(hDst != NULL) fclose(hDst);
if((hCreateContainer)!=0) CPReleaseContext(hCreateContainer,0);
if(pExchPubKeyBlob) free(pExchPubKeyBlob);
return FALSE;
}