Методы криптографической защиты информации Windows

Непосредственно создание ключа выполняет функция CryptDeriveKey (провайдер, ID_алгоритма, хеш-объект, флаги, ключ), которая принимает хеш-объект в качестве исходных данных и строит подходящий ключ для алгоритма шифрования, заданного своим ID. Результатом будет дескриптор ключа, который можно использовать для шифрования (рис. 3). Следует обратить внимание, что при работе с CryptoAPI мы все время имеем дело не с самими объектами или их адресами, а с дескрипторами — целыми числами, характеризующими положение объекта во внутренних таблицах криптопровайдера. Сами таблицы располагаются в защищенной области памяти, так что программы-«шпионы» не могут получить к ним доступ. Алгоритмы шифрования, поддерживаемые CryptoAPI, можно разделить на блочные и поточные: первые обрабатывают данные относительно большими по размеру блоками (например, 64, 128 битов или более), а вторые — побитно (теоретически, на практике же — побайтно). Если размер данных, подлежащих шифрованию, не кратен размеру блока, то последний, неполный блок данных, будет дополнен необходимым количеством случайных битов, в результате чего размер зашифрованной информации может несколько увеличиться. Разумеется, при использовании поточных шифров размер данных при шифровании остается неизменным. Шифрование выполняется функцией CryptEncrypt (ключ, хеш, финал, флаги, данные, рамер_данных, размер_буфера): через параметр ключ передается дескриптор ключа шифрования; параметр хеш используется, если одновременно с шифрованием нужно вычислить хеш-значение шифруемого текста; параметр финал равен true, если шифруемый блок текста — последний или единственный (шифрование можно осуществлять частями, вызывая функцию CryptEncrypt несколько раз); значение флага должно быть нулевым; параметр данные представляет собой адрес буфера, в котором при вызове функции находится исходный текст, а по завершению работы функции — зашифрованный; следующий параметр, соответственно, описывает размер входных/выходных данных, последний параметр задает размер буфера — если в результате шифрования зашифрованный текст не уместится в буфере, возникнет ошибка. Для расшифровки данных используется функция CryptDecrypt (ключ, хеш, финал, флаги, данные, рамер_данных), отличающаяся от шифрующей функции только тем, что размер буфера указывать не следует: поскольку размер данных при расшифровке может только уменьшиться, отведенного под них буфера наверняка будет достаточно. Приведем лишь фрагменты программы, реализующей шифрование файла с использованием заданного пароля, опустив громоздкие проверки успешности выполнения криптографических операций (что в реальной программе делать крайне нежелательно).

{«описание» используемых переменных}
hProv: HCRYPTPROV;
hash: HCRYPTHASH;
password: string;
key: HCRYPTKEY;
plaintext, ciphertext: string;
inFile, outFile: file;
data: PByte;
l: DWORD;
{получаем контекст криптопровайдера}
CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
{создаем хеш-объект}
CryptCreateHash(hProv, CALG_SHA, 0, 0, @hash);
{хешируем пароль}
CryptHashData(hash, @password[1], length(password), 0);
{создаем ключ на основании пароля для потокового шифра RC4}
CryptDeriveKey(hProv, CALG_RC4, hash, 0, @key);
{уничтожаем хеш-объект}
CryptDestroyHash(hash);
{открываем файлы}
AssignFile(inFile, plaintext);
AssignFile(outFile, ciphertext);
reset(inFile, 1);
rewrite(outFile, 1);
{выделяем место для буфера}
GetMem(data, 512);
{шифруем данные}
while not eof(inFile) do
begin
 BlockRead(inFile, data^, 512, l);
 CryptEncrypt(key, 0, eof(inFile), 0, data, @l, l);
 BlockWrite(outFile, data^, l);
end;
{освобождаем место и закрываем файлы}
FreeMem(data, 512);
CloseFile(inFile);
CloseFile(outFile);
{освобождаем контекст криптопровайдера}
CryptReleaseContext(hProv, 0);

Конечно, шифрование вами всех файлов одним и тем же паролем облегчает «противнику» задачу их расшифровки, запоминание огромного числа паролей сильно усложняет жизнь, а их записывание в незашифрованном виде создает опасность раскрытия всей системы. CryptoAPI может предложить на этот случай ряд решений. О них поговорим в следующий раз.
Проблема распределения ключей
В прошлый раз при помощи CryptoAPI мы решали такую "классическую" задачу как шифрование на основе пароля. Напомним, что пароль использовался для создания ключа шифрования какого-либо симметричного алгоритма. В таком случае расшифровать файл может лишь тот, кто знает пароль. А значит, для обеспечения конфиденциальности нужно держать пароль в строжайшем секрете - желательно, чтобы его знали лишь отправитель и получатель информации. (А еще лучше, если отправитель и получатель - одно и то же лицо.)
Предположим, что отправитель и получатель при личной встрече договорились использовать для конфиденциальной переписки определенный пароль. Но если они будут шифровать все свои сообщения одним и тем же ключом, то возможный противник, перехватив корреспонденцию, будеть иметь хорошие шансы взломать шифр: при современных методах криптоанализа наличие нескольких шифртекстов, полученных путем использования одного и того же ключа, почти гарантирует успешный результат. Поэтому при использовании симметричных алгоритмов шифрования настоятельно рекомендуется не применять один и тот же ключ дважды!
Однако помнить отдельный пароль для каждого зашифрованного сообщения - задача достаточно трудоемкая. А для корреспондентов, не имеющих возможности встретиться лично для согласования ключей шифрования, конфиденциальный обмен сообщениями вообще становится недоступным. Такая практическая трудность называется проблемой распределения ключей.
Спасительный способ, позволяющий шифровать сообщения, обмениваясь ключами по открытым каналам связи, был придуман в середине 70-х годов прошлого столетия, а в начале восьмидесятых появился первый реализующий его алгоритм - RSA. Теперь пользователь может сгенерировать два связанных между собой ключа - ключевую пару. Один из этих ключей по несекретным каналам рассылается всем, с кем пользователь хотел бы обмениваться конфиденциальными сообщениями. Этот ключ называют открытым (англ. public key). Зная открытый ключ пользователя, можно зашифровать адресованное ему сообщение, но вот расшифровать его позволяет лишь вторая часть ключевой пары - закрытый ключ (private key). При этом открытый ключ не дает "практической" возможности вычислить закрытый: такая задача, хоть и разрешима в принципе, но при достаточно большом размере ключа требует многих лет машинного времени. Для сохранения конфиденциальности получателю необходимо лишь хранить в строгом секрете свой закрытый ключ, а отправителю - убедиться, что имеющийся у него открытый ключ действительно принадлежит адресату.

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

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