| ||||
| ||||
Здравствуйте! Почему параметр ключа KP_BLOCKLEN для ключа полученного с помощью CryptDeriveKey(рProv, 26142, hHash, CRYPT_EXPORTABLE or CRYPT_CREATE_SALT, hKey), то есть для симметричного алгоритма ГОСТ 28147-89 равен 8192, хотя по идее он должен быть по умолчанию равен 64? Привожу всю последовательность действий: // Запрос контекста if not CryptAcquireContext(hProv, nil, nil, 2, CRYPT_VERIFYCONTEXT) then begin MessageDlg('Ошибка запроса контекста (' + ErrToStr(GetLastError) + ')', mtError, [mbOK], 0); Exit; end; if not CryptCreateHash( hProv, 32798, 0, 0, hHash) then begin MessageDlg('Ошибка создания хеша (CryptCreateHash) (' + ErrToStr(GetLastError) + ')', mtError, [mbOK], 0); CryptReleaseContext(hProv, 0); Exit; end; szPwd := 'Пароль'; pszPwd := @szPwd[1]; if not CryptHashData( hHash, pszPwd, Length(szPwd), 0) then begin MessageDlg('Ошибка получения хеша (CryptHashData) (' + ErrToStr(GetLastError) + ')', mtError, [mbOK], 0); CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); Exit; end; if not CryptDeriveKey( hProv, 26142, hHash, CRYPT_EXPORTABLE or CRYPT_CREATE_SALT, hKey) then begin MessageDlg('Ошибка получения ключа из хеша (CryptDeriveKey) (' + ErrToStr(GetLastError) + ')', mtError, [mbOK], 0); CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); Exit; end; CryptDestroyHash(hHash); BlockCSize := 4; if not CryptGetKeyParam(hKey, KP_BLOCKLEN, @BlockLen, BlockCSize, 0) then begin MessageDlg('Ошибка: ' + ErrToStr(GetLastError) + ' ' + IntToStr(IVLen), mtError, [mbOK], 0); exit; end; MessageDlg('Длина блока: ' + IntToStr(BlockLen), mtInformation, [mbOK], 0); // Выводится 8192 !!! | ||||
Ответы: | ||||
| ||||
Цитата из доки на CSP (*.chm): KP_BLOCKLEN Длина блока шифрования, обрабатываемого провайдером. Задается величиной DWORD. В случае сессионного ключа если для ключа установлен режим блочного шифрованя (CRYPT_MODE_ЕСB, CRYPT_MODE_CBС), в параметре pbData устанавливается размер блока алгоритма ГОСТ 28147-89, если установлен режим поточного шифрования (CRYPT_MODE_OFB, CRYPT_MODE_CFB), в параметре pbData устанавливается значение оптимального с точки зрения скорости шифрования размера блока, принимаемого от приложения (в настоящее время 8Кбит). В случае ключевой пары в параметре pbData устанавливается число бит модуля q как для ключей на экспонециальной логике (ГОСТ 34.10-94), так и для ключей на базе эллиптической кривой (ГОСТ 34.10-2001). | ||||
| ||||
Спасибо за быстрый ответ. Тем не менее вопрос остается: Устанавливая параметр ключа KP_MODE равным CRYPT_MODE_CBC CryptSetKeyParam(hKey, KP_MODE, @Mode, 0) и получая после этого размер блока CryptGetKeyParam(hKey, KP_BLOCKLEN, @BlockLen, BlockCSize, 0) снова выводится 8192. Далее при шифрации видно, что длина шифротекста не увеличивается, чтобы быть кратной длине блока (8). Может быть нужно установить еще какой-нибудь параметр? | ||||
| ||||
Можно попросить вас проверить, что будет в случае создания нового сессионного ключа ф-ей CryptGenKey для алгоритмс 26142 - после изменения режима этого ключа на CBC что скажут про BLOCKLEN? | ||||
| ||||
После CryptGenKey(hProv, 26142, CRYPT_EXPORTABLE or CRYPT_CREATE_SALT, hKey) KP_BLOCKLEN все равно 8192. В результате CryptEncrypt(hKey, 0, True, 0, pszText, inoutDataLength, 8) длина шифротекста возвращается равная 8 при длине открытого текста 5, хотя szText почему-то содержит 9 символов. Последующее расшифрование приводит к ошибке NTE_BAD_DATA. Привожу полный код процедуры на Delphi без обработчиков ошибок. Цель процедуры: сгенерить ключ, IV, зашифровать и расшифровать данные, получив исходный текст. В качестве обертки использую WinCrypt.pas. Данная процедура замечательно работает для базового криптопровайдера Microsoft (симметричный алгоритм RC2). function TestCryptEncrypt3; var cbPwd, cbData : DWORD; hHash : HCRYPTHASH; hKey : HCRYPTKEY; hProv : HCRYPTPROV; cont: PAnsiChar; pszPwd : PByte; szPwd : array [0..255] of char; pszText : PByte; szText : array [0..255] of char; inoutDataLength : Cardinal; IV : array [0..7] of char; pIV : PByte; IVLen : Cardinal; Mode : cardinal; BlockLen : cardinal; BlockCSize : cardinal; begin // Запрос контекста if not CryptAcquireContext(hProv, nil, nil, 75, CRYPT_VERIFYCONTEXT) then begin end; if not CryptGenKey(hProv, 26142, // CALG_RC2, CRYPT_EXPORTABLE, hKey) then begin MessageDlg('Ошибка генерации ключа (' + ErrToStr(GetLastError) + ')', mtError, [mbOK], 0); Exit; end; Mode := CRYPT_MODE_CBC; if not CryptSetKeyParam(hKey, KP_MODE, @Mode, 0) then begin end; if Mode = CRYPT_MODE_CBC then MessageDlg('Режим: CRYPT_MODE_CBC', mtInformation, [mbOK], 0); szText := 'Текст'; pszText := @szText[1]; inoutDataLength := 5; pIV := @IV[0]; BlockCSize := 4; if not CryptGetKeyParam(hKey, KP_BLOCKLEN, @BlockLen, BlockCSize, 0) then begin end; MessageDlg('Длина блока: ' + IntToStr(BlockLen), mtInformation, [mbOK], 0); //создание IV IVLen := 8; if not CryptGetKeyParam(hKey, KP_IV, pIV, IVLen, 0) then begin end; MessageDlg('IV: ' + IV + '(' + IntToStr(IVLen) + ')', mtInformation, [mbOK], 0); if not CryptGenRandom(hProv, IVLen, pIV) then begin end; if not CryptSetKeyParam(hKey, KP_IV, pIV, 0) then begin end; MessageDlg('IV: ' + IV + '(' + IntToStr(IVLen) + ')', mtInformation, [mbOK], 0); // Зашифровать данные if not CryptEncrypt( hKey, 0, True, 0, pszText, inoutDataLength, 8) then begin end; MessageDlg('Длина шифротекста: ' + IntToStr(inoutDataLength) + '(' + szText + ')', mtInformation, [mbOK], 0); // Расшифровать данные if not CryptDecrypt( hKey, 0, True, 0, pszText, inoutDataLength) then begin end; MessageDlg('Длина открытого текста: ' + IntToStr(inoutDataLength) + '(' + szText + ')', mtInformation, [mbOK], 0); CryptDestroyKey(hKey); CryptReleaseContext(hProv,0); Result := True; end; | ||||
| ||||
Синхровектор IV меняется после процедуры зашифрования. Для корректного расшифрования надо выставить его значение в то же самое ПЕРЕД CryptDecrypt. Итак, схема такая: 1. Создание сессионного ключа. Если ключ создаётся ф-ей CryptGenKey, то синхровектор уже случайный. 2. Установка режима CBC. 3. Сохранение значения IV - CryptGetKeyParam(hKey, KP_IV,...) 4. Шифрование данных. Если шифруется 5 байт, то на выходе будет 8 байт (размер блока). 5. Восстановление ранее сохранённого значения IV - CryptSetKeyParam(hKey, KP_IV,...) 6. Расшифрование данных. Длина на входе - 8 байт, на выходе - 5 байт. | ||||
| ||||
Вот работающий пример (на С) - только что проверил: HCRYPTPROV hProv=0; HCRYPTKEY hKey=0; DWORD dwFlags=0; DWORD dwDataLen,dwBufLen=100,ParamLen=4,Param=1,IVLen=8,i; BYTE *buf,*bufIV; CHAR *txt="Текст"; buf = (BYTE*)malloc(dwBufLen); bufIV = (BYTE*)malloc(IVLen); if(CryptAcquireContext( &hProv, NULL, NULL, 75, CRYPT_VERIFYCONTEXT)) printf("CSP verifycontext acquired.\n"); else HandleError("Error during CryptAcquireContext."); { dwDataLen=strlen(txt); memcpy(buf,txt,dwDataLen); dwFlags=CRYPT_EXPORTABLE;// | CRYPT_CREATE_SALT; if(CryptGenKey( hProv, CALG_G28147, dwFlags, &hKey)) { printf("The session key has been created. \n"); } else { HandleError("Error during CryptGenKey."); } Param = CRYPT_MODE_CBC; if(!CryptSetKeyParam(hKey,KP_MODE,(LPBYTE)&Param,0)) if(!CryptGetKeyParam(hKey,KP_IV,bufIV,&IVLen,0)) HandleError("Error during CryptGetKeyParam."); if(CryptEncrypt(hKey, 0, TRUE, 0, buf, &dwDataLen, dwBufLen)) { printf("Data encrypted. \n"); } else { HandleError("Error during CryptEncrypt."); } if(!CryptSetKeyParam(hKey,KP_IV,bufIV,0)) HandleError("Error during CryptSetKeyParam."); if(CryptDecrypt( hKey, 0, TRUE, 0, buf, &dwDataLen )) { printf("Data decrypted. \n"); } else { HandleError("Error during CryptDecrypt."); } if(strcmp(txt,(CHAR*)buf)==0) printf("decryption - ok"); else printf("Error in decryption"); if(!CryptDestroyKey(hKey)) printf("Error number %x.\n", GetLastError()); } // Release the provider handle. if(hProv) if(!CryptReleaseContext(hProv, 0)) printf("Error number %x.\n", GetLastError()); return 0; | ||||
| ||||
Опечатка небольшая: После генерации ключа: if(!CryptGetKeyParam(hKey,KP_BLOCKLEN,(LPBYTE)&Param,&ParamLen,0)) HandleError("Error during CryptGetKeyParam."); //Здесь возвращается 8192 Param = CRYPT_MODE_CBC; if(!CryptSetKeyParam(hKey,KP_MODE,(LPBYTE)&Param,0)) HandleError("Error during CryptGenKey for signkey."); if(!CryptGetKeyParam(hKey,KP_BLOCKLEN,(LPBYTE)&Param,&ParamLen,0)) HandleError("Error during CryptGetKeyParam."); //Здесь возвращается 64 Далее сохранение синхровектора и по тексту. | ||||