2.4.3 Шифрование на основе пользовательских данных или пароля

Для шифрования в CryptoAPI используются симметричные алгоритмы, ключ для которых может быть получен двумя путями: случайным образом или на основе каких-либо пользовательских данных, например пароля. Причем к последнему варианту генерации ключа есть одно важное требование: при использовании одних и тех же паролей должны получаться идентичные ключи. Такая возможность предусмотрена в CryptoAPI.

function CryptDeriveKey(hProv :HCRYPTPROV;

Algid :ALG_ID;

hBaseData :HCRYPTHASH;

dwFlags :DWORD;

phKey :PHCRYPTKEY) :BOOL; stdcall;

 

В параметре hProv нужно указать дескриптор провайдера, полученный с помощью CryptAcquireContext. Algid - идентификатор алгоритма, для которого генерируется ключ. Для Microsoft Base Cryptographic Provider может принимать следующие значения: CALG_RC2 и CALG_RC4. Пользовательские данные (пароль) предварительно хэшируются и дескриптор хэш-объекта передается в функцию в качестве параметра hBaseData. Старшие 16 бит параметра dwFlags могут содержать размер ключа в битах или быть нулевыми (в этом случае будет создан ключ с размером по умолчанию). Младшие 16 бит могут быть нулевыми или принимать следующие значения или их комбинации: CRYPT_EXPORTABLE, CRYPT_CREATE_SALT, CRYPT_USER_PROTECTED, CRYPT_UPDATE_KEY. К первым двум мы еще вернемся, а со смыслом остальных вы можете ознакомиться самостоятельно. В параметре phKey возвращается дескриптор созданного ключа.

В случае успеха, функция возвращает true, в противном случае - false. GetLastError вернет код ошибки.

Когда ключ есть, можно приступать непосредственно к шифрованию. Для этого нам понадобятся функции CryptEncrypt и CryptDecrypt.

function CryptEncrypt(hKey  :HCRYPTKEY;

hHash :HCRYPTHASH;

Final :BOOL;

dwFlags :DWORD;

pbData :PBYTE;

pdwDataLen :PDWORD;

dwBufLen :DWORD) :BOOL; stdcall;

 


В параметре hKey передается дескриптор ключа, необходимый для шифрования. Этот ключ также определяет алгоритм шифрования. Параметр hHash используется, если данные одновременно шифруются и хэшируются (шифроваться и хэшироваться будут исходные данные). В этом случае в параметре hHash передается дескриптор заранее созданного хэш-объекта. Эту возможность удобно использовать, если необходимо одновременно зашифровать и подписать сообщение. Иначе этот параметр следует установить в ноль. Параметр Final следует установить в true, если переданный в функцию блок данных является единственным или последним. В этом случае он будет дополнен до необходимого размера. Параметр dwFlags не используется в Microsoft Base Cryptographic Provider и на его месте следует указать ноль. pbData - указатель на буфер, в котором содержаться данные для зашифрования. Зашифрованыые данные помещаются в тот же буфер. pdwDataLen - размер данных, которые будут зашифрованы. dwBufLen - размер выходного буфера, для блочных шифров может быть больше, чем pdwDataLen. Узнать необходимый размер, можно передав в параметре pbData nil, в параметре pdwDataLen - размер данных, которые необходимо зашифровать, а в параметре dwBufLen - что угодно, например ноль. После такого вызова, необходимый размер буфера будет содержаться в параметре pdwDataLen (именно pdwDataLen, а не dwBufLen, немного нелогично, ну да ладно). Чтобы не было путаницы, приведем пример:

var

Message: String;

BufLen, DataLen: DWORD;

...

begin

...

Message:='Hello World!';

BufLen:=Length(Message);

DataLen:=Length(Message);

// Вычисляем необходимый размер выходного буфера

CryptEncrypt(Key,0,true,0,nil,@BufLen,0);

// Выделяем память для буфера и шифруем

SetLength(Message,BufLen);

CryptEncrypt(Key,0,true,0,PByte(Message),@DataLen,BufLen);

 


Теперь, рассмотрим функцию, которая позволяет расшифровать сообщение.


function CryptDecrypt(hKey :HCRYPTKEY;

hHash :HCRYPTHASH;

Final :BOOL;

dwFlags :DWORD;

pbData :PBYTE;

pdwDataLen :PDWORD) :BOOL; stdcall;

 

 


В параметр pdwDataLen нужно передать число байт шифротекста, а после вызова в него будет помещена длина открытого сообщения. Если используется параметр hHash, то данные после расшифровки хэшируются. Это удобно использовать, если нужно одновременно расшифровать сообщение и проверить подпись.

После того, как работа с ключом закончена, необходимо освободить дескриптор:

function CryptDestroyKey(hKey :HCRYPTKEY) :BOOL; stdcall;

 

 


Если hKey относится к сеансовому ключу или импортированному открытому ключу (об этом ниже), то дескриптор освобождается, а ключ уничтожается. Если hKey относится к паре открытый/закрытый ключ, то дескриптор освобождается, а ключевая пара сохраняется в контейнере ключей.

Только что мы рассмотрели случай, когда для зашифровки и расшифровки сообщения отправитель и получатель использовали пароль, известный только им. Сейчас рассмотрим другой: отправитель генерирует ключ случайно и передает его получателю в зашифрованном виде вместе с сообщением. При этом для шифрования сеансового ключа используется открытый ключ получателя. А где отправитель его возьмет?

Как уже было сказано, при создании ключевого контейнера с помощью функции CryptAcquireContext, ключи в контейнере не создаются, их нужно сгенерировать отдельно. Рассмотрим функцию:

function CryptGenKey(hProv  :HCRYPTPROV;

Algid :ALG_ID;

dwFlags :DWORD;

phKey :PHCRYPTKEY) :BOOL; stdcall;

 

 


Функция предназначена для генерации случайных сеансовых ключей и ключевых пар. Параметры этой функции аналогичны одноименным параметрам функции CryptDeriveKey, за исключением того, что Algid может также принимать значения AT_KEYEXCHANGE и AT_SIGNATURE. В этом случае будут сгенерированы ключевые пары соответственно для обмена ключами и цифровой подписи. Создание нового ключевого контейнера должно выглядеть примерно так:

uses Wcrypt2;

...

var

Prov: HCRYPTPROV;

ExchangeKey, SignKey: HCRYPTKEY;

begin

CryptAcquireContext(@Prov,'My_Container',nil,PROV_RSA_FULL,CRYPT_NEWKEYSET);

// Создаем ключевые пары

CryptGenKey(Prov,AT_KEYEXCHANGE,0,@ExchangeKey);

CryptGenKey(Prov,AT_SIGNATURE,0,@SignKey);

// Работаем с функциями CryptoAPI

...

// Освобождаем дескрипторы ключевых пар. Сами ключи сохраняются в контейнере

CryptDestroyKey(SignKey);

CryptDestroyKey(ExchangeKey);

CryptReleaseContext(Prov,0);

end;

 


Созданные таким образом ключевые пары, впоследствии можно извлечь из контейнера, воспользовавшись функцией


 

function CryptGetUserKey(hProv :HCRYPTPROV;

dwKeySpec :DWORD;

phUserKey :PHCRYPTKEY) :BOOL; stdcall;

 
 

 


Параметр dwKeySpec может принимать два значения: AT_KEYEXCHANGE и AT_SIGNATURE, значения которых очевидны. Дескриптор ключа возвращается в параметре phUserKey.

Теперь ответим на вопрос, как отправитель сможет передать получателю свою открытую часть ключа.

function CryptExportKey(hKey :HCRYPTKEY;

hExpKey :HCRYPTKEY;

dwBlobType :DWORD;

dwFlags :DWORD;

pbData :PBYTE;

pdwDataLen :PDWORD) :BOOL; stdcall;

 


Функция позволяет экспортировать ключ в двоичный буфер, который впоследствии можно будет сохранить в файл и передать кому-либо. В параметре hKey должен содержаться дескриптор экспортируемого ключа. Экспортировать можно не только открытые ключи, а также ключевые пары целиком и сеансовые ключи. В последних двух случаях, ключи и ключевые пары должны быть созданы функциями CryptGenKey или CryptDeriveKey с параметрами dwFlags равными CRYPT_EXPORTABLE. Открытые же ключи всегда экспортируемы. Сеансовые ключи и ключевые пары экспортируются только в зашифрованном виде. Параметр hExpKey определяет ключ, которым они будут зашифрованы. Если экспортируется открытая часть ключа, то этот параметр следует установить в ноль, если экспортируется ключевая пара целиком, то здесь обычно передают дескриптор сеансового ключа (обычно полученный с помощью CryptDeriveKey), которым пара будет зашифрована, если экспортируется сеансовый ключ, то обычно он шифруется открытым ключом получателя (обычно используется ключ обмена, но никто не запрещает использовать ключ подписи). Параметр dwBlobType определяет тип экспортируемого ключа и может принимать следующие значения: SIMPLEBLOB - сеансовый ключ, PUBLICKEYBLOB - открытый ключ, PRIVATEKEYBLOB - ключевая пара целиком. Существуют и другие значения, но они не поддерживаются стандартным криптопровайдером. Параметр dwFlags для Microsoft Base Cryptographic Provider должен быть равен нулю. pbData - буфер, куда будут скопированы данные, pdwDataLen - размер этого буфера. Если он заранее не известен, то можно указать в качестве параметра pbData nil, и в pdwDataLen будет получен необходимый размер.

Вот пример экспорта открытого ключа:

procedure ExportPublicKey(FileName: TFileName);

var

Prov: HCRYPTPROV;

SignKey: HCRYPTKEY;

Stream: TMemoryStream;

BufSize: DWORD;

begin

CryptAcquireContext(@Prov,'My_Container',nil,PROV_RSA_FULL,0);

CryptGetUserKey(Prov,AT_SIGNATURE,@SignKey);

Stream:=TMemoryStream.Create;

CryptExportKey(SignKey,0,PUBLICKEYBLOB,0,nil,@BufSize);

Stream.SetSize(BufSize);

CryptExportKey(SignKey,0,PUBLICKEYBLOB,0,PByte(Stream.Memory),@BufSize);

Stream.SaveToFile(FileName);

Stream.Free;

CryptDestroyKey(SignKey);

CryptReleaseContext(Prov,0);

end;

 


Импорт ключа осуществляется с помощью функции

function CryptImportKey(hProv :HCRYPTPROV;

pbData :PBYTE;

dwDataLen :DWORD;

hPubKey :HCRYPTKEY;

dwFlags :DWORD;

phKey :PHCRYPTKEY) :BOOL; stdcall;

 
 

 


В параметре hPubKey необходимо передать дескриптор ключа, которым будет расшифрован импортированный ключ. Если импортируется ключевая пара целиком, то параметр dwFlags можно установить в CRYPT_EXPORTABLE, тогда импортированная пара может быть впоследствии также экспортирована. В параметре phKey вернется дескриптор полученного ключа. Если это ключевая пара, то она будет сохранена в контейнере.

Вот пример импорта открытого ключа:

function ImportPublicKey(FileName: TFileName): HCRYPTKEY;

var

Prov: HCRYPTPROV;

Stream: TMemoryStream;

begin

Stream:=TMemoryStream.Create;

Stream.LoadFromFile(FileName);

CryptImportKey(Prov,PByte(Stream.Memory),Stream.Size,0,0,@Result);

Stream.Free;

end;

 


Теперь, воспользовавшись этой информацией, мы без труда сможем восстановить пропущенные фрагменты в функции проверки цифровой подписи, описанной ранее.

Итак, как же передать собеседнику зашифрованное сообщение:

·          Получатель экспортирует свой открытый ключ обмена в файл и передает его отправителю сообщения.

·          Отправитель генерирует случайный сеансовый ключ и шифрует им сообщение.

·          Отправитель импортирует открытый ключ обмена получателя, экспортирует сеансовый ключ, шифруя его полученным ключом обмена (ключ обмена в параметре hExpKey).

·          Зашифрованное сообщение передается вместе с зашифрованным сеансовым ключом - так называемый цифровой конверт.

·          Получатель импортирует сеансовый ключ, расшифровывая его своим закрытым ключом обмена (его можно получить, вызвав CryptGetUserKey) и с помощью сеансового ключа расшифровывает сообщение.

Говоря о сеансовых ключах, используемых в Microsoft Base Cryptographic Provider нужно упомянуть об одной неприятности: до начала 2000 года действовал запрет на экспорт программного обеспечения, использующего средства "сильной криптографии" за пределами США и Канады. По этой причине в базовом криптопровайдере не поддерживаются ключи для симметричных алгоритмов длиной более 40 бит. Ключи длиной 56 бит разрешалось использовать только заграничным отделениям американских компаний. Для алгоритмов RC2 и RC4 рекомендуемая длина ключа должна составлять 128 бит, поэтому недостающее количество бит заполняется нулями либо случайными значениями, которые должны передаваться открыто. Надежность защиты из-за этого, разумеется, сильно страдает. В состав Windows XP входит Microsoft Enhanced Cryptographic Provider, в котором этой проблемы нет, но при использовании базового криптопровайдера, необходимо дополнять ключ до нужной длины, используя т.н. солт-значения (salt-values). Сгенерировать salt-value и внести его в ключ можно несколькими способами, но самый простой и очевидный - при вызове CryptGenKey или CryptDeriveKey передать в параметре dwFlags значение CRYPT_CREATE_SALT, примерно так:

CryptGenKey(Prov,CALG_RC2,CRYPT_EXPORTABLE or CRYPT_CREATE_SALT,@Key);

 

При экспорте ключа солт-значение не сохраняется, о нем должен позаботиться сам программист.


 

var

SaltLen: DWORD;

Stream: TMemoryStream;

...

begin

...

// Определяем размер буфера для солт-значения

CryptGetKeyParam(Key,KP_SALT,nil,@SaltLen,0);

// Сохраняем его в файл

Stream:=TMemoryStream.Create;

Stream.SetSize(SaltLen);

CryptGetKeyParam(Key,KP_SALT,PByte(Stream.Memory),@SaltLen,0);

Stream.SaveToFile('Salt.dat');

Stream.Free;

...

 
 

Сохраненное таким образом солт-значение необходимо передать вместе с сеансовым ключом, а на приемной стороне "вживить" его туда снова.

var

Stream: TMemoryStream;

...

begin

...

Stream:=TMemoryStream.Create;

Stream.LoadFromFile('Salt.dat');

CryptSetKeyParam(Key,KP_SALT,PByte(Stream.Memory),Stream.Size);

Stream.Free;

...

 



Информация о работе «Защита персональных данных с помощью алгоритмов шифрования»
Раздел: Информатика, программирование
Количество знаков с пробелами: 50231
Количество таблиц: 14
Количество изображений: 1

Похожие работы

Скачать
62272
1
7

... для блокировки загрузки с FDD; Интерфейс для блокировки загрузки с CD-ROM; Программное обеспечение формирования списков контролируемых программ; Документация. 2. Система защиты информации "Secret Net 4.0" Рис. 2.1. Назначение: Программно-аппаратный комплекс для обеспечения информационной безопасности в локальной вычислительной сети, рабочие ...

Скачать
284992
7
0

... 6.0. – Microsoft Press, 1998. – 260 c. ISBN 1-57231-961-5 ТУЛЬСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ На правах рукописи Карпов Андрей Николаевич ЗАЩИТА ИНФОРМАЦИИ В СИСТЕМАХ ДИСТАНЦИОННОГО ОБУЧЕНИЯ С МОНОПОЛЬНЫМ ДОСТУПОМ Направление 553000 - Системный анализ и управление Программная подготовка 553005 – Системный анализ данных и моделей принятия решений АВТОРЕФЕРАТ диссертации на соискание степени ...

Скачать
66389
0
0

... , в которые преступники хотят вмешаться, так и для планирования методов совершения преступления. Таким образом, осуществляется «оптимизация» способа совершения преступления.[2]   1.3 Методы защиты информации Для решения проблем защиты информации в сетях прежде всего нужно уточнить возможные причины сбоев и нарушений, способные привести к уничтожению или нежелательной модификации данных. К ним ...

Скачать
134036
26
14

... части локальной сети не позволяют останавливаться на известных достигнутых результатах и побуждают на дальнейшее исследование в дипломной работе в направлении разработки локальной сети с беспроводным доступом к ее информационным ресурсам, используя перспективные технологии защиты информации. 2. Выбор оборудования, для перспективных технологий СПД   2.1 Выбор передающей среды Зачастую перед ...

0 комментариев


Наверх