Экспорт сессионного ключа

Публикация: 28 Сентябрь 2010 - 20:43, редакция: 19.10.2010 18:14

Не получается экспортировать сессионный ключ. Сделал все так же как в MSDN'овых примерах:

на передающей стороне (A)

  • создал ключ обмена;
  • экспортировал его;

на принимающей стороне (B)

  • импортировал этот ключ обмена;
  • создал сессионный ключ;
  • попытался его экспортировать (от B к A) на полученном ключе обмена. Возвращается ошибка NTE_BAD_KEY_STATE (Key not valid for use in specified state).

Из приведенного ниже примера ясно, что экспортируется совсем не сессионный ключ.

Попробую рассказать. 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 in CryptAcquireContext(OpenKeySet)!\n",
    GetLastError());
return FALSE;
}
else {
if(!CPAcquireContext(&hCreateContainer,
    ContainerName,CRYPT_NEWKEYSET,pVTable)) {
printf("Error %x in 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,&dwData
Len,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;
}