CryptoAPI (статья)

Наиболее простой и интуитивно понятный способ состоит в том, чтобы разбить исходный текст на блоки соответствующего размера, а затем отдельно каждый блок подвергнуть шифрующему преобразованию. Такой режим использования блочных шифров называют электронной кодовой книгой (ECB - electronic codebook). Его главный недостаток состоит в том, что одинаковые блоки исходного текста при шифровании дадут одинаковые же блоки шифр-текста - а это может существенно облегчить противнику задачу взлома. Поэтому режим ECB не рекомендуется использовать при шифровании текстов, по длине превышающих один блок - в таких случаях лучше воспользоваться одним из режимов, связывающих различные блоки между собой. По умолчанию в CryptoAPI блочные шифры используются в режиме сцепления блоков шифр-текста (CBC - cipher block chaining). В этом режиме при шифровании очередной блок исходного текста вначале комбинируется с предыдущим блоком шифр-текста (при помощи побитового исключающего ИЛИ), а затем полученная последовательность битов поступает на вход блочного шифра (рис. 14). Образующийся на выходе блок шифр-текста используется для шифрования следующего блока. Самый первый блок исходного текста также должен быть скомбинирован с некоторой последовательностью битов, но "предыдущего блока шифр-текста" еще нет; поэтому режимы шифрования с обратной связью требуют использования еще одного параметра - он называется инициализирующим вектором (IV - initialization vector). Инициализирующий вектор должен генерироваться отдельно с помощью уже известной нам функции CryptGenRandom и, как и солт-значение, передаваться вместе с ключом в открытом виде. Размер IV равен длине блока шифра. Например, для алгоритма RC2, поддерживаемого базовым криптопровайдером Microsoft, размер блока составляет 64 бита (8 байтов). От слов - к делу

Настало время применить все сказанное на практике, создав приложение, предназначенное для шифрования и расшифровки файлов с использованием случайных сеансовых ключей. Окно приложения показано на рис. 15. Для успешной работы программы нужно создать на компьютере собственный ключевой контейнер, экспортировать из него открытый ключ обмена ключами и обменяться открытыми ключами с адресатом. Все эти операции подробно обсуждались ранее. В простейшем случае "отправитель" сообщения может являться и "получателем" - тогда нужно просто экспортировать свой открытый ключ обмена ключами и сохранить его в каком-нибудь файле на диске.

Шифруемый файл помещается в цифровой конверт. Как мы знаем, вместе с ключом необходимо сохранить солт-значение, а при использовании блочного шифра - еще и инициализирующий вектор. В соответствии с этим наш цифровой конверт будет иметь структуру, показанную на рис. 16.
Приведем основные фрагменты процедуры, осуществляющей шифрование и расшифровку файла (обработка ошибок опущена):

procedure TMainForm.BitBtn1Click (Sender: TObject);

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 для решения этой проблемы предполагает использование сертификатов.

Отправить комментарий

Проверка
Антиспам проверка
Image CAPTCHA
...