CryptoAPI (статья)
Наиболее простой и интуитивно понятный способ состоит в том, чтобы разбить исходный текст на блоки соответствующего размера, а затем отдельно каждый блок подвергнуть шифрующему преобразованию. Такой режим использования блочных шифров называют электронной кодовой книгой (ECB - electronic codebook). Его главный недостаток состоит в том, что одинаковые блоки исходного текста при шифровании дадут одинаковые же блоки шифр-текста - а это может существенно облегчить противнику задачу взлома. Поэтому режим ECB не рекомендуется использовать при шифровании текстов, по длине превышающих один блок - в таких случаях лучше воспользоваться одним из режимов, связывающих различные блоки между собой. По умолчанию в CryptoAPI блочные шифры используются в режиме сцепления блоков шифр-текста (CBC - cipher block chaining). В этом режиме при шифровании очередной блок исходного текста вначале комбинируется с предыдущим блоком шифр-текста (при помощи побитового исключающего ИЛИ), а затем полученная последовательность битов поступает на вход блочного шифра (рис. 14). Образующийся на выходе блок шифр-текста используется для шифрования следующего блока. Самый первый блок исходного текста также должен быть скомбинирован с некоторой последовательностью битов, но "предыдущего блока шифр-текста" еще нет; поэтому режимы шифрования с обратной связью требуют использования еще одного параметра - он называется инициализирующим вектором (IV - initialization vector). Инициализирующий вектор должен генерироваться отдельно с помощью уже известной нам функции CryptGenRandom и, как и солт-значение, передаваться вместе с ключом в открытом виде. Размер IV равен длине блока шифра. Например, для алгоритма RC2, поддерживаемого базовым криптопровайдером Microsoft, размер блока составляет 64 бита (8 байтов). От слов - к делу
Настало время применить все сказанное на практике, создав приложение, предназначенное для шифрования и расшифровки файлов с использованием случайных сеансовых ключей. Окно приложения показано на рис. 15. Для успешной работы программы нужно создать на компьютере собственный ключевой контейнер, экспортировать из него открытый ключ обмена ключами и обменяться открытыми ключами с адресатом. Все эти операции подробно обсуждались ранее. В простейшем случае "отправитель" сообщения может являться и "получателем" - тогда нужно просто экспортировать свой открытый ключ обмена ключами и сохранить его в каком-нибудь файле на диске.
Шифруемый файл помещается в цифровой конверт. Как мы знаем, вместе с ключом необходимо сохранить солт-значение, а при использовании блочного шифра - еще и инициализирующий вектор. В соответствии с этим наш цифровой конверт будет иметь структуру, показанную на рис. 16.
Приведем основные фрагменты процедуры, осуществляющей шифрование и расшифровку файла (обработка ошибок опущена):
var
hProv: HCRYPTPROV;
KeyExchKey, SessionKey: HCRYPTKEY;
flag, keyLen: DWORD;
infile, outfile: file;
tmp: PBYTE;
buf: array [0..511] of byte;
alg: ALG_ID;
stream: boolean;
begin
…
подключение к криптопровайдеру
…
if ActionRadioGroup.ItemIndex = 0 {шифрование}
then
begin
OpenDlg.Title:= 'Укажите файл для шифрования';
if OpenDlg.Execute then AssignFile (infile, OpenDlg.FileName)
else exit;
OpenDlg.Title:= 'Укажите файл с открытым ключом обмена ключами получателя';
if OpenDlg.Execute then
begin
AssignFile (outfile, OpenDlg.FileName);
reset (outfile, 1);
keyLen:= FileSize (outfile);
GetMem (tmp, keyLen);
BlockRead (outfile, tmp^, keyLen);
CloseFile (outfile);
end
else exit;
CryptImportKey (hProv, tmp, keyLen, 0, 0, @KeyExchKey);
FreeMem (tmp, keyLen);
SaveDlg.Title:= 'Задайте имя файла для зашифрованных данных';
if SaveDlg.Execute then AssignFile (outfile, SaveDlg.FileName)
else exit;
rewrite (outfile, 1);
case AlgRadioGroup.ItemIndex of {установка алгоритма шифрования}
0: begin
alg:= CALG_RC2; {алгоритм RC2}
stream:= false; {блочный шифр}
end;
1: begin
alg:= CALG_RC4; {алгоритм RC4}
stream:= true; {поточный шифр}
end;
end;
CryptGenKey (hProv, alg, CRYPT_EXPORTABLE or CRYPT_CREATE_SALT, @SessionKey); {создание сеансового ключа}
keyLen:= 128; {размер буфера "с запасом"}
GetMem (tmp, keyLen);
CryptExportKey (SessionKey, KeyExchKey, SIMPLEBLOB, 0, tmp, @keyLen);
BlockWrite (outfile, keyLen, 4); {запись в файл размера ключа}
BlockWrite (outfile, tmp^, keyLen); {и самого зашифрованного ключа}
CryptDestroyKey (KeyExchKey);
keyLen:= 512; {размер буфера "с запасом"}
CryptGetKeyParam (SessionKey, KP_SALT, @buf, @keyLen, 0);
BlockWrite (outfile, keyLen, 4); {запись в файл размера солта}
BlockWrite (outfile, buf, keyLen); {и самого солт-значения}
if not stream then {если шифр - блочный}
begin
//генерируем IV
keyLen:= 512; {размер буфера "с запасом"}
// запрос IV ради выяснения его размера
CryptGetKeyParam (SessionKey, KP_IV, @buf, @keyLen, 0);
CryptGenRandom (hProv, keyLen, @buf); {генерация IV}
CryptSetKeyParam (SessionKey, KP_IV, @buf, 0);
BlockWrite (outfile, keyLen, 4); {запись в файл размера IV}
BlockWrite (outfile, buf, keyLen); {и самого IV}
end;
reset (infile, 1);
while not eof (infile) do
begin {собственно шифрование и запись в файл}
BlockRead (infile, buf, 496, keyLen);
CryptEncrypt (SessionKey, 0, eof (infile), 0, @buf, @keyLen, 512);
BlockWrite (outfile, buf, keyLen);
end;
CloseFile (infile);
CloseFile (outfile);
CryptDestroyKey (SessionKey);
end
else {расшифровывание}
begin {получаем дескриптор своего ключа обмена ключами}
CryptGetUserKey (hProv, AT_KEYEXCHANGE, @KeyExchKey);
OpenDlg.Title:= 'Укажите файл с зашифрованными данными';
if OpenDlg.Execute then AssignFile (infile, OpenDlg.FileName)
else exit;
reset (infile, 1);
BlockRead (infile, keyLen, 4); {читаем размер ключа}
GetMem (tmp, keyLen);
BlockRead (infile, tmp^, keyLen); {читаем сам ключ}
CryptImportKey (hProv, tmp, keyLen, KeyExchKey, 0, @SessionKey);
FreeMem (tmp, keyLen);
CryptDestroyKey (KeyExchKey);
BlockRead (infile, keyLen, 4); {читаем солт-значение}
BlockRead (infile, buf, keyLen);
CryptSetKeyParam (SessionKey, KP_SALT, @buf, 0);
keyLen:= 4; {выясняем алгоритм шифрования}
CryptGetKeyParam (SessionKey, KP_ALGID, @alg, @keyLen, 0);
case alg of
CALG_RC2: stream:= false;
CALG_RC4: stream:= true;
end;
if not stream then {если шифр - блочный}
begin
//читаем и устанавливаем IV
BlockRead (infile, keyLen, 4);
BlockRead (infile, buf, keyLen);
CryptSetKeyParam (SessionKey, KP_IV, @buf, 0);
end;
SaveDlg.Title:= 'Задайте имя файла для расшифрованных данных';
if SaveDlg.Execute then
begin
AssignFile (outfile, SaveDlg.FileName);
rewrite (outfile, 1);
while not eof (infile) do
begin {собственно расшифровывание}
BlockRead (infile, buf, 512, keyLen);
CryptDecrypt (SessionKey, 0, eof (infile), 0, @buf, @keyLen);
BlockWrite (outfile, buf, keyLen);
end;
CloseFile (outfile);
end;
CloseFile (infile);
CryptDestroyKey (SessionKey);
end;
CryptReleaseContext (hProv, 0);
end;
В рассмотренной нами процедуре обмена шифрованными сообщениями остается одно слабое звено - обмен открытыми ключами. Ведь при этом мы не обеспечиваем подлинность полученного ключа - во время пересылки его может подменить злоумышленник. CryptoAPI для решения этой проблемы предполагает использование сертификатов.
Отправить комментарий