Чтобы
тайное не стало явным
В настоящее время многие
приложения используют для обмена данными открытые каналы связи, и прежде
всего Internet. Однако в ряде случаев, например в банковской и финансовой
сферах, требуется ограничить доступ к конфиденциальной информации,
передаваемой по таким каналам. При этом важно иметь возможность проверить, от
кого исходят принятые получателем конфиденциальные данные и не были ли они
искажены при пересылке. Для безопасной передачи конфиденциальных данных по
открытым каналам используется криптография.
Интерфейс CryptoAPI
В сфере защиты компьютерной информации
криптография применяется в основном для: шифрования и дешифровки данных; а
также создания и проверки цифровых подписей. (Прим. автора: в
русскоязычной литературе применяется термин «ЭЦП» – электронно-цифровая
подпись. В этой статье для краткости используется термин «цифровая подпись».) Шифрование данных позволяет ограничить доступ к
конфиденциальной информации, сделать ее нечитаемой и непонятной для
посторонних. Применение цифровых подписей оставляет данные открытыми, но дает
возможность верифицировать отправителя и проверять целостность полученных
данных. Для защиты информации специалистами Microsoft
был разработан интерфейс CryptoAPI, который позволяет создавать приложения,
использующие криптографические методы. Структура CryptoAPI
В Crypto API существует понятие
«криптопровайдер» (Cryptography Service Provider, CSP). Криптопровайдер – это
независимый модуль, содержащий библиотеку криптографических функций со стандартизованным
интерфейсом. Криптопровайдер отвечает за реализацию функций интерфейса, а
также играет роль хранилища для ключей всех типов. Подобная архитектура
позволяет переходить от одного провайдера к другому с минимальными
изменениями исходного кода, так как интерфейс (т. е. сами функции) не
меняется. В операционную систему Windows включен
криптопровайдер Microsoft RSA Base Provider. Криптографические ключи
В CryptoAPI существуют ключи двух типов:
Сессионные ключи. Это симметричные ключи, так как один и тот же ключ
применяется и для шифрования, и для расшифровки. Сессионные ключи меняются.
Алгоритмы, использующие сессионные ключи (так называемые симметричные
алгоритмы), – RC2, RC4, DES. Microsoft RSA Base Provider работает с
40-разрядными сессионными ключами. Пары ключей используются в так называемых
асимметричных алгоритмах шифрования. Если шифрование выполнялось одним ключом
из пары, то дешифровка производится другим. Открытые (public) ключи могут
передаваться другим лицам для проверки цифровых подписей и шифрования
пересылаемых данных. Длина открытого ключа в Microsoft RSA Base Provider
составляет 512 разрядов. Закрытые (private) ключи не могут быть
экспортированы; они используются для создания цифровых подписей и дешифровки
данных. Закрытый ключ должен быть известен только его владельцу. Хранение ключей
Криптопровайдер отвечает за хранение и
разрушение ключей. Программист не имеет доступа непосредственно к двоичным
данным ключа, за исключением операций экспорта открытых ключей. Вся работа с
ключами производится через дескрипторы (handle). В CryptoAPI ключи для шифрования/дешифровки и
создания/проверки подписей разделены. Называются они соответственно «пара для
обмена ключами» и «пара для подписи». База данных ключей состоит из контейнеров, в
каждом из которых хранятся ключи, принадлежащие определенному пользователю.
Контейнер ключей имеет уникальное имя и содержит пару для обмена и пару для
подписи. Все ключи хранятся в защищенном виде. По умолчанию для каждого пользователя создается
контейнер с именем этого пользователя. Можно создавать дополнительные
контейнеры и назначать им произвольные имена, которые обязательно должны быть
уникальными (см. Рисунок 1).
Работа с CryptoAPI
Ниже приводятся примеры работы с некоторыми
функциями CryptoAPI. Все примеры написаны на языке С++ в среде MS Visual C++
6.0. Приведены прототипы наиболее интересных функций с пояснениями. Чтобы не
перегружать читателя деталями, обработка ошибок в листинги не включена. Названия функций CryptoAPI имеют префикс Crypt.
Как правило, все они возвращают результат типа BOOL – TRUE при успешном
завершении и FALSE, если произошла ошибка. В последнем случае для получения
сведений об ошибке необходимо вызвать GetLastError(). Прототипы функций описаны в файле wincrypt.h.
Для использования этих функций в свойствах проекта нужно определить константу
_WIN32_WINNT и задать ей значение 0x0400 (или больше). Данная константа
применяется в файле wincrypt.h для проверки версии Windows. Для некоторых функций CryptAPI также требуются
библиотеки crypt32.lib и advapi32.lib. Криптопровайдер
Прежде чем использовать какие-либо функции
Crypto API, необходимо запустить криптопровайдер. Делается это с помощью
функции CryptAc-quireContext: BOOL CRYPTFUNC CryptAcquireContext( HCRYPTPROV* hCryptProvider,//дескриптор провайдера, out-параметр LPCTSTR pszContainer, // имя контейнера ключей LPCTSTR pszProvider, // имя провайдера DWORD dwProvType, // тип провайдера DWORD dwFlags // флаги )
Кроме инициализации криптопровайдера данную
функцию можно использовать для создания и удаления контейнеров ключей. Для
этого параметру dwFlags присваивается значение, соответственно,
CRYPT_NEW-KEYSET и CRYPT_DELETEKEYSET. Функция CryptAcquireContext работает в два
этапа: сначала она ищет криптопровайдер по имени и типу, указанному в
аргументах, а затем контейнер ключей с заданным именем. По окончании работы с криптопровайдером
необходимо вызвать функцию CryptReleaseContext (см. Листинг 1). Работа с ключами
Генерация ключей. В CryptoAPI имеются функции для генерации ключей любого
типа. Функция CryptGenKey генерирует сессионные ключи и пары для обмена и
подписи на основе случайного числа. BOOL CRYPTFUNC CryptGenKey( HCRYPTPROV hProv ALG_IG Algid DWORD dwFlags HCRYPTKEY* phKey )
Сессионные ключи можно сгенерировать также на
основе некоторого заданного значения (пароля) при помощи функции
CryptDeriveKey. Это стоит делать, например, в том случае, когда нужно
избежать пересылки сессионного ключа вместе с зашифрованными данными (см.
Рисунок 2). Получатель данных, зная пароль и алгоритм, может сам
сгенерировать сессионный ключ и с его помощью расшифровать данные (см.
Листинг 2).
Получение дескриптора ключа. Дескриптор открытого ключа можно получить, вызвав
функцию CryptGet-UserKey. Экспорт ключей. Операция экспорта выполняется при сохранении сессионных
ключей и при передаче ключей третьим лицам. Двоичные данные ключа могут быть получены при
помощи функции CryptExportKey: BOOL CRYPTFUNC CryptExportKey ( HCRYPTKEY hKeyToExport HCRYPTKEY hCryptKey DWORD dwBlobType DWORD dwFlags BYTE* pbData, // указатель на буфер DWORD* pdwDataLen ) // длина буфера Поясним значения аргументов. Первый аргумент –
дескриптор экспортируемого ключа. Второй аргумент – дескриптор ключа, которым
шифруется экспортируемый ключ. Открытые ключи экспортируются в незашифрованном
виде. В этом случае hCryptKey = 0. При экспорте сессионных и закрытых ключей
необходимо их предварительно зашифровать. Аргумент hCryptKey должен содержать
дескриптор открытого ключа получателя. При вызове с аргументом pbData = NULL функция
вернет необходимую длину буфера по адресу, на который указывает аргумент
pdwDataLen (см. Листинг 3). После успешного завершения переменная
dwSessionKeyLen будет содержать действительную длину BLOB-структуры ключа.
Это значение необходимо сохранить для обратной операции – импорта ключа в
криптопровайдер. Импорт ключей. Ключи импортируются функцией CryptImportKey: CryptImportKey ( HCRYPTPROV hProv BYTE* pbData DWORD dwDataLen HCRYPTKEY hCryptKey DWORD dwFlags HCRYPTKEY* phImportedKey )
hCryptKey = 0 в том случае, если импортируемый
ключ был зашифрован асимметричным ключом или не был зашифрован вообще. Если импортируемый ключ шифровали сессионным
ключом, то hCryptKey должен содержать дескриптор этого ключа. По окончании работы с ключом необходимо для его
дескриптора вызвать функцию CryptDestroyKey(HCRYPTKEY hKey). Шифрование и дешифровка
данных
В CryptoAPI для шифрования и дешифровки
используются и симметричный, и асимметричный алгоритмы. Симметричный алгоритм менее надежен, но работает
намного быстрее, чем асимметричный. Поэтому в CryptoAPI применяется
комбинация алгоритмов. Данные шифруются с помощью симметричного алгоритма с
сессионным ключом, а сам сессионный ключ шифруется по асимметричному
алгоритму открытым ключом получателя. Дешифровка происходит в обратном
порядке: сначала закрытым ключом получателя дешифруется сессионный ключ,
затем этим сессионным ключом дешифруются сами данные (см. Рисунки 2 и 3).
Таким образом, расшифровать данные можно только,
имея закрытый ключ из той же ключевой пары, что и открытый ключ, которым
данные были зашифрованы. Для шифрования и дешифровки применяется функция
(см. Листинг 4). BOOL CRYPTFUNC CryptEncrypt ( HCRYPTKEY hKey, // дескриптор ключа для шифрования HCRYPTHASH hHash BOOL bFinal BYTE* pbData, // параметр [in, out DWORD* pdwDataLen, // параметр [in, out DWORD dwBufferLen )
Последний и предпоследний параметры являются
одновременно входными и выходными. Это означает, что зашифрованные данные
записываются поверх исходных в буфер pbData, а длина данных, соответственно,
в dwBufferLen. Как и в случае с функцией Crypt-ExportKey,
CryptEncrypt позволяет предварительно определить необходимый размер буфера
под зашифрованные данные. Для этого нужно вызвать функцию с аргументами: pbData = NULL; pdwDataLen = длина исходных данных.
Длину буфера функция вернет по адресу:
pdwDataLen. Дешифровка данных производится аналогично
функцией CryptDecrypt. Цифровые подписи
Цифровая подпись – это двоичные данные
небольшого объема, обычно не более 256 байт. Цифровая подпись есть не что
иное, как результат работы хеш-алгоритма над исходными данными, зашифрованный
закрытым ключем отправителя. Проще говоря, берем исходные данные, получаем из
них хеш и шифруем хеш своим закрытым ключом (с помощью асимметричного
алгоритма). Полученные в результате шифрования хеша двоичные
данные и есть наша цифровая подпись (см. Рисунок 4).
Получатель, чтобы удостовериться, что данные
присланы именно от нас и не были искажены, производит следующие операции:
Подобный алгоритм позволяет любому лицу
проверить подлинность подписи отправителя. Напомню, что открытый ключ
отправителя, с помощью которого собственно и производится проверка,
распространяется (как правило) свободно. Для работы с цифровыми подписями используются
функции CryptCreate-Hash, CryptHashData, CryptSignHash, CryptVerifySignature,
CryptDestroy-Hash. Создание цифровой подписи. Процесс создания подписи состоит из следующих этапов:
Аналогично функциям CryptEncrypt, проверка длины
буфера для подписи производится вызовом функции CryptSignHash с нулем вместо
указателя на данные. Как и у функции CryptEncrypt, указатели на данные и их
длину являются параметрами типа [in, out] (см. Листинг 5). Проверка цифровой подписи. Проверка подписи (см. Листинг 6) выполняется так:
ВИТАЛИЙ ЛИ – разработчик ПО в компании «Киберплат.Ком». С ним можно связаться по
адресу: [email protected]. |