| ||||
| ||||
Добрый день! Никак не получается экспортировать сессионный ключ. Вроде сделал все так же как в 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 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; } | ||||