17572

Использование криптографического интерфейса Windows при разработке приложений. Создание приложений для создания ключей и ключевого материала

Лабораторная работа

Информатика, кибернетика и программирование

Лабораторная работа № 4.2 Тема: Использование криптографического интерфейса Windows при разработке приложений. Создание приложений для создания ключей и ключевого материала. Обмен ключами. Функции CryptoAPI для работы с ключевым материалом. Цель: изучить принципы построе...

Русский

2013-07-04

2.41 MB

23 чел.

Лабораторная работа № 4.2

Тема: Использование криптографического интерфейса Windows при разработке приложений. Создание приложений для создания ключей и ключевого материала. Обмен ключами. Функции CryptoAPI для работы с ключевым материалом.

Цель: изучить принципы построения и использования CryptoAPI. Ознакомиться с базовыми криптографическими функциями. На практике усвоить принципы организации программ, обеспечивающих создание и управления ключами шифрования.

Задание:

1) Ознакомиться с теоретическими положениями, посвященными принципам работы с криптографическим интерфейсом Windows.

2) Написать программу которая обеспечивает:

  •  получение информации о криптопровайдерах и реализованных ими алгоритмах;
  •  обеспечивает создание сеансового ключа шифрования;
  •  обеспечивает создание пары ключей ассиметричного шифрования;
  •  проверяет факт создания ключей ассиметричного шифрования;

средствами CryptoAPI.

Структура отчета

  1.  Титульный лист.
  2.  Тема и цель работы.
  3.  Задание и номер варианта.
  4.  Краткие теоретические сведения.
  5.  Ход работы.
  6.  Выводы.

Теоретические сведения

Принципы построения и использования криптографического интерфейса Windows (CryptoAPI)

Криптографический интерфейс приложений операционной системы Windows представляет собой набор констант, типов данных и функций, предназначенных для выполнения операций шифрования, расшифрования, получения и проверки ЭЦП, генерации, хранения и распределения ключей шифрования. Эти услуги для приложений предоставляют провайдеры криптографического обслуживания (Cryptographic Service Provider, CSP) - динамически компонуемые библиотеки (DLL), экспортирующие единый набор объектов, определяемый интерфейсом CryptoAPI.

Взаимодействие между приложением и CSP строится на основе следующих принципов:

  1.  приложение не имеет прямого доступа к изготовлению и хранению ключей шифрования (нет риска их потери из-за ошибок в приложении);
  2.  приложение не определяет деталей выполнения криптографических операций, а лишь указывает на требуемые от CSP действия (например, зашифровать по заданному алгоритму данные и получить для них ЭЦП);
  3.  приложение не обрабатывает данных, по которым проводится аутентификация пользователя, а предоставляет это CSP, который имеет лучшие возможности для проверки подлинности пользователя (например, может использовать аутентификацию на основе биометрических признаков или ключевой информации, размещенной на смарт-картах или других элементах аппаратного обеспечения); в этом случае из-за ошибок в приложении не может произойти утечка конфиденциальной информации о пользователе (например, его пароле).

На рис. 1 приведена архитектура криптографической подсистемы Windows. Вызовы функций CryptoAPI обрабатываются модулем операционной системы advapi32.dll, который преобразует их в вызовы функций интерфейса провайдера криптографического обслуживания (Cryptographic Service Provider Interface, CryptoSPI). Для обеспечения аутентичности и подлинности CSP он снабжается ЭЦП, которая периодически проверяется операционной системой в ходе сеанса работы пользователя. Получение ЭЦП производится корпорацией Microsoft на основе хеш-значения CSP, представленного изготовителем этой библиотеки.

Каждый CSP характеризуется своим присвоенным производителем именем (строкой символов) и типом (целым числом от 1 до 999), определяющим поддерживаемые этим провайдером криптографические алгоритмы

Рис. 1. Взаимодействие приложения и криптопровайдера 

 

В табл. 1 приведены некоторые предопределенные типы криптопровайдеров.

Основные атрибуты CSP

К основным атрибутам CSP относятся:

  •  реализованный (обязательно) алгоритм ЭЦП (всегда единственный);
  •  реализованная (обязательно) функция хеширования;
  •  длина ключей асимметричного шифрования;
  •  формат ЭЦП;
  •  форматы блоков, в которых открытый и (возможно) секретный ключи асимметричного шифрования экспортируются из CSP;
  •  реализованный (возможно) алгоритм обмена сеансовыми ключами симметричного шифрования (всегда единственный);
  •  реализованные (возможно) алгоритмы симметричного шифрования (конкретный CSP может поддерживать часть таких алгоритмов, определенных для соответствующего типа криптопровайдера);
  •  алгоритм генерации сеансового ключа из хеш-значения;
  •  длины сеансовых ключей (в зависимости от алгоритма);
  •  формат блока сеансового ключа при его экспорте из CSP;

Таблица 1

Символическая константа типа (ее численное значение)

Алгоритм обмена сеансовыми ключами

Алгоритм ЭЦП

Алгоритмы симметричного шифрования

Функции хеширования

PROV_RSA_FULL (1)

RSA

RSA

RC2, RC4, DES, 3-DES

MD2, MD4, MD5, SHA

PROV_RSA_SIG (2)

RSA

MD5, SHA

PROV_DSS (3)

DSS

SHA

PROV_FORTEZZA (4)

DH

DSS

Skipjack

SHA

PROV_MS_EXCHANGE (5)

RSA

RSA

CAST, RC2, DES, 3-DES

MD5

PROV_SSL (6)

RSA

RSA

Разные

Разные

PROV_RSA_SCHANNEL (12)

RSA

RSA

RC2, RC4, DES, 3-DES

MD5, SHA

PROV_DSS_DH (13)

DH

DSS

CYLINK MEK, RC2, RC4, DES, 3-DES

MD5, SHA

PROV_EC_ECDSA_SIG (14)

ЕС

SHA

PROV_DH_SCHANNEL (18)

DH

DSS

CYLINK MEK, RC2, RC4, DES, 3-DES

MD5, SHA

PROV_RSA_AES (24)

RSA

RSA

RC2, RC4, DES, 3-DES, AES

MD2, MD4, MD5, SHA

Информация об установленных на компьютере криптопровай-дерах содержится в реестре Windows в разделе HKEY_LOCAL_MACHINE \ Software \ Microsoft \ Cryptography \ Defaults. В подразделе Provider размещается информация о пути к библиотеке, ЭЦП и типе каждого CSP (по его имени), а в подразделе Provider Types - сведения о полных именах криптопровайдера и его типа.

Контейнеры ключей

Для каждого зарегистрированного у него пользователя или конкретного приложения CSP хранит контейнер ключей асимметричного шифрования (key set), который может включать в себя две пары ключей - открытый и секретный ключи для обмена сеансовыми ключами, а также открытый и секретный ключи для ЭЦП. Ключи симметричного шифрования (сеансовые ключи) не сохраняются CSP и об их сохранении (или правильной повторной генерации) должно позаботиться приложение.

Контейнер ключей может храниться криптопровайдером в реестре Windows, в файле на диске или на защищенном от несанкционированного чтения съемном носителе (смарт-карте, USB-ключе и т.п.).

Криптопровайдеры от Microsoft

Вместе с операционной системой Windows корпорация Microsoft поставляет несколько встроенных криптопровайдеров типа PROV_RSA_FULL:

Microsoft Base Cryptographic Provider v1.0 - поддерживает алгоритмы симметричного шифрования RC2 и RC4 с длиной ключа от 40 (по умолчанию) до 64 бит, DES, алгоритм ЭЦП с длиной ключа от 384 до 16384 бит (по умолчанию 512), алгоритм обмена сеансовыми ключами с длиной ключа от 384 до 1024 бит (по умолчанию 512);

Microsoft Enhanced Cryptographic Provider v1.0 - поддерживает алгоритмы симметричного шифрования RC2 и RC4 с длиной ключа от 40 до 128 (по умолчанию) бит, DES, 3-DES, 3-DES Two Key, алгоритмы ЭЦП и обмена сеансовыми ключами с длиной ключа от 384 до 16384 бит (по умолчанию 1024);

Microsoft Strong Cryptographic Provider - поддерживает алгоритмы симметричного шифрования RC2 и RC4 с длиной ключа от 40 (по умолчанию в Windows 2000) до 128 (по умолчанию в Windows МЕ/ХР) бит, DES, 3-DES, 3-DES Two Key, алгоритмы ЭЦП и обмена сеансовыми ключами с длиной ключа от 384 до 16384 бит (по умолчанию 512 в Windows 2000 и 1024 в Windows МЕ/ХР).

Первый из указанных выше криптопровайдеров является криптопровайдером по умолчанию. Все эти криптопровайдеры хранят контейнеры ключей пользователей в реестре Windows.

С операционными системами Windows также поставляется еще ряд криптопровайдеров:

Microsoft Exchange Cryptographic Provider v1.0 (типа PROV_MS_EXCHANGE) – поддерживает алгоритмы симметричного шифрования RC2 и RC4 с длиной ключа от 40 до 128 (по умолчанию) бит, DES, 3-DES, 3-DES Two Key, алгоритм ЭЦП с длиной ключа 512 бит, алгоритм обмена сеансовыми ключами с длиной ключа 512 бит;

Microsoft Base DSS Cryptographie Provider (типа PROV_DSS) -поддерживает алгоритмы ЭЦП и обмена сеансовыми ключами с длиной ключа от 512 до 1024 (по умолчанию) бит;

Microsoft Base DSS and Diffie-Hellman Cryptographie Provider (типа PROV_DSS_DH) - поддерживает алгоритмы симметричного шифрования CYLINK МЕК с длиной ключа 40 бит, RC2 и RC4 с длиной ключа от 40 (по умолчанию) до 56 бит, DES, алгоритм ЭЦП с длиной ключа от 512 до 1024 (по умолчанию) бит, алгоритм обмена сеансовыми ключами с длиной ключа от 512 (по умолчанию) до 1024 бит;

Microsoft Enhanced DSS and Diffie-Hellman Cryptographie Provider (типа PROV_DSS_DH) - поддерживает алгоритмы симметричного шифрования CYLINK МЕК с длиной ключа 40 бит, RC2 и RC4 с длиной ключа от 40 (по умолчанию в Windows ME и 2000) до 128 (по умолчанию в Windows ХР) бит, DES, 3-DES, 3-DES Two Key, алгоритм ЭЦП с длиной ключа от 512 до 1024 (по умолчанию) бит, алгоритм обмена сеансовыми ключами с длиной ключа от 512 (по умолчанию в Windows ME и 2000) до 4096 бит (в Windows ХР длина ключа обмена по умолчанию 1024 бит).

Вместе с операционными системами Windows 2000/ХР дополнительно поставляются криптопровайдеры:

Microsoft DH SChannel Cryptographie Provider (типа PROV_DH_SCHANNEL) - поддерживает алгоритмы симметричного шифрования CYLINK МЕК с длиной ключа 40 бит, RC2 и RC4 с длиной ключа от 40 (по умолчанию) до 128 бит, DES, 3-DES, 3-DES Two Key, алгоритм ЭЦП с длиной ключа от 512 до 1024 (по умолчанию) бит (в Windows 2000 только 1024 бит), алгоритм обмена сеансовыми ключами с длиной ключа от 512 (по умолчанию) до 4096 бит;

Microsoft RSA SChannel Cryptographie Provider (типа PROV_RSA_SCHANNEL) - поддерживает алгоритмы симметричного шифрования RC2 и RC4 с длиной ключа от 40 до 128 (по умолчанию) бит, DES, 3-DES, 3-DES Two Key, алгоритмы ЭЦП и обмена сеансовыми ключами с длиной ключа от 384 до 16384 бит (по умолчанию 1024 бита);

Microsoft Enhanced RSA and AES Cryptographie Provider (Prototype) (типа PROV_RSA_AES) - поддерживает алгоритмы симметричного шифрования RC2 и RC4 с длиной ключа от 40 до 128 (по умолчанию) бит, DES, 3-DES, 3-DES Two Key, AES (с длиной ключа128, 192 и 256 бит), алгоритмы ЭЦП и обмена сеансовыми ключами с длиной ключа от 384 до 16384 бит (по умолчанию 1024 бит).

Версии CryptoAPI

Существуют две версии CryptoAPI: 1.0 и 2.0. В CryptoAPI 1.0 поддерживаются базовые функции для выполнения всех основных криптографических операций. В CryptoAPI 2.0 (библиотека crypt32.dll) введены дополнительные функции для поддержки инфраструктуры открытых ключей.

Для доступа к криптографическим функциям из программ и сценариев на языках Visual Basic, VBScript, JavaScript и аналогичных им в операционной системе Windows предназначен набор многокомпонентных объектов CAPICOM (CryptoAPI Component Object Model, библиотека capicom.dll).

Обзор функций СгурtоАРI 1.0

Основные функции CryptoAPI 1.0 приведены в табл. 2.

Таблица 2.

Название функции

Краткое описание функции

CryptEncrypt CryptDecrypt

Симметричное шифрование и расшифрование данных

CryptCreateHash CryptDestroyHash

Создание пустого хеш-значения и освобождение его дескриптора

CryptHashData CryptHashSessionKey

Хеширование(добавление к хеш-значению) произвольных данных и сеансового ключа

CryptGetHashParam CryptSelHashParam

Получение и установка параметров хеш-значения

CryptSignHash CryptVerifySignature

Получение и проверка ЭЦП

CryptGenRandom

Генерация случайного значения

При создании приложения на языке С++ в системах программирования Borland С++ Builder и Microsoft Visual С++ для доступа к прототипам этих функций, а также к определениям констант и типов данных, используемых в CryptoAPI 1.0, потребуется заголовочный файл wincrypt.h. При использовании Microsoft Visual С++ оператор #include для подключения этого файла можно поместить в автоматически создаваемый конструктором приложений (AppWizard) файл stdafx.h (перед заключительным оператором #endіf). При использовании Borland С++ Builder файл wincrypt.h подключается автоматически.

Получение информации о криптопровайдерах

Приложение может получить следующую информацию.

1. Об установленных в системе криптопровайдерах:

  •  тип криптопровайдера (числовое значение и, возможно, текстовое описание);
  •  название криптопровайдера;
  •  список имен поддерживаемых криптоалгоритмов;
  •  список имен созданных контейнеров ключей.

2. Для каждого алгоритма, поддерживаемого криптопровайдером:

  •  тип алгоритма (шифрования данных или сообщений, ЭЦП, об
    мена сеансовыми ключами, хеширования);
  •  для алгоритмов шифрования - поддерживаемую длину ключей (значение по умолчанию, минимальное и максимальное значение);
  •  для алгоритмов хеширования - длину хеш-значения;
  •  для алгоритмов ЭЦП и обмена сеансовыми ключами - шаг изменения длины ключа;
  •  для алгоритмов блочного шифрования - длину блока.

3.Для каждого созданного криптопровайдером контейнера ключей можно определить:

  •  созданы ли в нем пары ключей ЭЦП и обмена;
  •  какие права доступа к этому контейнеру ключей имеют различные Пользователи системы (только для операционных систем Windows NT / 2000 / ХР / 2003).

Для получения этой информации предназначены следующие функции CryptoAPI 1.0:

BOOL WINAPI CryptEnumProviderTypes(DWORD dwlndex, DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, LPTSTR pszTypeName, DWORD *pcbTypeName); /* получение информации о типе криптопровайдера с относительным номером dwlndex: числовом значении (*pdwProvType), описании (pszTypeName), длине строки с описанием (*pcbTypeName); pdwReserved=NULL, dwFlags=0; если криптопровайдера с таким относительным номером нет, то функция возвращает FALSE, а вызванная сразу после нее функция GetLastError - ERROR_NO_MORE_ITEMS; если криптопровайдер не поддерживает получение описания своего типа, то в параметре *pcbTypeName возвращается 0. */

BOOL WINAPI CryptEnumProviders(DWORD dwlndex, DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, LPTSTR pszProvName, DWORD *pcbProvName);  получение информации о криптопровайдере с относительным номером dwlndex; числовом значении типа (*pdwProvType), имени (pszProvName), длине имени (*pcbProvName); pdwReserved = NULL, dwFlags = 0; если криптопровайдера с таким относительным номером нет, то функция возвращает FALSE, а вызванная сразу после нее функция GetLastError -ERROR_NO_MORE_ITEMS. */

BOOL CryptAcquireContext(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags); /* получение в *phProv дескриптора криптопровайдера с именем pszProvider и типом dwProvType для существующего контейнера ключей pszContainer (dwFlags=0) или (если ранее вызванная функция CryptAcquireContext возвратила FALSE с кодом ошибки NTE_BAD_KEYSET от функции GetLastError; для создаваемого нового контейнера ключей (dwFlags= CRYPT_NEWKEYSET); если dwFlags=CRYPT_VERIFYCONTEXT, то приложение не будет иметь доступа к секретным ключам асимметричного шифрования в контейнере (значение pszContainer в этом случае должно быть установлено в NULLj; если dwFlags=CRYPT_DELETEKEYSET, то контейнер ключей в криптопровайдере будет удален (значение параметра *phProv при этом не определено); функция возвращает TRUE при успешном завершении и FALSE - в случае ошибки. */

Если при вызове функции CryptAcquireContext не указывается имя контейнера ключей (параметр pszContainer), то подразумевается контейнер с именем по умолчанию. Встроенные в операционную систему Windows криптопровайдеры в качестве имени контейнера ключей по умолчанию используют имя учетной записи пользователя, от лица которого выполняется данное приложение. Хранение секретных ключей в контейнерах с именами по умолчанию ненадежно, так как одно приложение может разрушить ключи, созданные другой прикладной программой.

Если параметр dwFlags не содержит значения CRYPT_MACHINE_KEYSET, то встроенные в Windows криптопровайдеры хранят создаваемый или ищут открываемый контейнер ключей в профиле выполнившего эту операцию пользователя (в разделе реестра HKEY_CURRENT_USER). Если параметр dwFlags содержит значение CRYPT_MACHINE_KEYSET, то встроенные в Windows криптопровайдеры хранят создаваемый или ищут открываемый контейнер ключей в разделе реестра HKEY_LOCAL_MACHINE. Это может потребоваться, если пользователь получает доступ к контейнеру ключей с помощью системной службы или не входит в систему интерактивно.

Если параметр dwFlags содержит значение CRYPT_VERIFYCONTEXT, то пользователь получает доступ к контейнеру ключей только для работы с открытым ключом (для проверки ЭЦП или экспорта сеансового ключа шифрования) и хеширования. BOOL WINAPI CryptGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE pbData, DWORD pdwDataLen, DWORD dwFlags); /* получение информации о криптопровайдере с дескриптором hProv: именах созданных в нем контейнеров ключей (dwParam=PP_ENUMCONTAINERS, dwFlags устанавливается в CRYPT_FIRST для первого контейнера и 0 для всех следующих), именах и характеристиках реализованных криптоалгоритмов (dwParam=PP_ENUMALGS_EX, dwFlags устанавливается в CRYPT_FIRST для первого алгоритма и 0 для всех следующих), шаге изменения длины асимметричного ключа обмена в битах (dwParam= PP_KEYX_KEYSIZE_INC), шаге изменения длины ключа ЭЦП в битах (dwParam=PP_SIG_KEYSIZE_INC), дескрипторе безопасности контейнера ключей (dwParam=PP_KEYSET_SEC_DESCR, только для Windows NT/2000/ХР/2003) со сведениями о создателе контейнера (dwFlags = OWNER_SECURITY_INFORMATION) и правах доступа к нему различных субъектов (dwFlags= =DACL_SECURITY_INFORMATION) и др.; запрашиваемая информация помещается в буфер pbData длиной *pdwDataLen; функция возвращает TRUE при успешном завершении и FALSE е случае ошибки (при достижении конца списка имен контейнеров ключей или криптоалгоритмов функция GetLastError возвращает ERROR_NO_MORE_ITEMS. */

Структура буфера с информацией возвращаемой функцией CryptGetProvParam зависит от ее типа:

  1.  если запрашивается информация о контейнерах ключей, то их имена помещаются в строку символов, оканчивающуюся 0;
  2.  если запрашивается информация о реализованных криптоалгоритмах, то она помещается в структуру типа PROV_ENUMALGS_EX, имеющую поля:

ALGJD aiAlgid; // код алгоритма

DWORD dwDefaultLen; // длина ключа по умолчанию

DWORD dwMinLen; // минимально возможная длина ключа

DWORD dwMaxLen; // максимально возможная длина ключа

DWORD dwProtocols; // количество реализованных криптоалгоритмов и протоколов

DWORD dwNameLen; // длина сокращенного названия алгоритма

CHAR szName[20]; // сокращенное название алгоритма 

DWORD dwLongNameLen; // длина полного названия алгоритма 

GHAR szLongName[40]; // полное название алгоритма

3) если запрашивается информация о шаге изменения длины асимметричных ключей, то она возвращается в переменной типа DWORD.

Для определения типа криптоалгоритма по его коду следует использовать макрос GET_ALG_CLASS, который может возвращать значения ALG_CLASS_HASH (хеширование), ALG_CLASS_SIGNATURE (получение и проверка ЭЦП), ALG_CLASS_KEY_EXCHANGE (обмен сеансовыми ключами), ALG_CI_ASS_DATA_ENCRYPT (шифрование произвольных данных), ALG_CLASS_MSG_ENCRYPT (шифрование сообщений в формате ASN.1).

Для алгоритмов хеширования вместо длины ключа возвращается длина хеш-значения.

Получение информации о криптографических ключах

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

BOOL WINAPI CryptGetKeyParam(HCRYPTKEY hKey, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags); Г получение параметров ключа с дескриптором hKey, полученном после выполнения функции CryptGenKey: длины блока сеансового ключа (dwParam= KP_BLOCKLENj, фактической длины ключа с учетом битов четности (dwParam=KP_KEYLEN, для алгоритмов DES, 3-DES и 3-DES Two Key будет соответственно возвращаться 64, 192 и 128 бит), добавляемых к ключу симметричного шифрования случайных значений (dwParam=KP_SALT/), вектора инициализации для некоторых режимов алгоритмов симметричного шифрования (dwParam = KPJVj, режима симметричного шифрования (dwParam=KP_MODE, поддерживаются значения CRYPT_MODE_CBC, CRYPT_MODE_CFB, CRYPT_MODE_ECB и CRYPT_MODE_OFB, последний режим не поддерживается встроенными в Windows крипто-провайдерами), длины порции данных в режимах симметричного шифрования с использованием регистра замены (tiwParam= =KP_MODE_BITSj, эффективной длины сеансового ключа (dwParam=KP_EFFECTIVE_KEYLEN, равной количеству случайных битов ключа) и др.; dwFlags=0; запрашиваемая характеристика возвращается в 6yqbepe*pbData длиной *pdwDataLen; функция возвращает TRUE при успешном завершении и FALSE в случае ошибки. */

Структура буфера зависит от типа запрашиваемой с помощью функции CryptGetKeyParam информации: если запрашивается добавляемое к ключу случайное значение или вектор инициализации, то возвращается массив байт, а в остальных случаях, как правило, возвращается значение типа DWORD.

Для освобождения дескриптора криптопровайдера, полученного с помощью функции CryptAcquireContext, предназначена следующая функция CryptoAPI 1.0:

BOOL CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags); /* освобождение дескриптора криптопровайдера hProv (dwFlags=0) */

Рассмотрим пример получения информации о криптопровайдерах и реализованных ими криптоалгоритмах в программе на языке С++, созданной в системе программирования Borland С++ Builder. Окно, в котором будет отображаться получаемая информация, приведено на рис. 2.

На глобальном уровне объявим следующие переменные:

DWORD Len; // длина буфера для получения информации

char pTN[MAX_PATH]; // описание типа криптопровайдера

DWORD рТуре, рТ[МАХ_РАТН]; // тип и имя криптопровайдера

HCRYPTPROV hP=0; // дескриптор криптопровайдера

ALG_ID рА[МАХ_РАТН]; // коды криптоалгоритмов

UINT і; // относительный номер криптопровайдера

DWORD Def[MAX_PATH], Min[MAX_PATH], Мах[МАХ_РАТН]; /* длина ключа: по умолчанию, минимальная и максимальная */

AnsiString Tmp; // вспомогательная строка HCRYPTKEY hK=0; // дескриптор ключа

LPVOID MsgBuf; /* указатель на буфер с текстом сообщения об ошибке */

При создании окна выполним следующий код:

AnsiString cpt; // вспомогательная строка

//  установка длины буфера Len=sizeof(pTN);

// подготовка к получению типов криптопровайдеров

i=0;

// цикл получения информации о типах криптопровайдеров

while(CryptEnumProviderTypes(i,NULL,0,&pT[i],pTN,&Len)&&GetLastError()!=ERROR_NO_MORE_ITEMS)

{ // если описание типа доступно

if(Len) Tmp=AnsiString(pTN)+cpt.sprintf(" (%d)",pT[i]);

// если описание типа недоступно

else Tmp.printf("(%d)",pT[i]);

/* добавление информации о типе к списку «Типы криптопровайдеров»*/

Рис. 2. Окно программы для получения информации о криптопровайдерах

Туреs->Items->Аddmр); 

// увеличение индекса типа криптопровайдера

i++;

// установка длины буфера

Len=sizeof(pTN);    }

// установка длины буфера

Len=sizeof(pTN);

// подготовка к получению имен криптопровайдеров

i=0;

//  цикл получения имен криптопровайдеров while(CryptEnumProviders(i,NULL,0,&pT[i],pTN,&Len) && Len)

{ // добавление имени к списку «Криптопровайдеры»

Names->ltems->Add(pTN);

// увеличение индекса криптопровайдера

i++;

// установка длины буфера Len=sizeof(pTN);    }

В обработку изменения выбора в списке «Криптопровайдеры» поместим код:

PROV_ENUMALGS_EX pAlg; // информация о криптоалгоритме

AnsiString nt; // описание типа

int j; // индекс типа криптоалгоритма

//  сброс дескриптора криптопровайдера

if(hP) CryptReleaseContext(hP,0);

hP=0;

/* очистка списков «Криптоалгоритмы», «Контейнеры ключей» и «Параметры алгоритма» */

Algs->Clear();

Conts->Clear();

Key->ltems->Clear();

// получение типа выбранного провайдера 

pType=pT[Names-ltemlndex];

// установка длины буфера

Len=sizeof(pAlg);

// получение дескриптора выбранного криптопровайдера 

if(!CryptAcquireContext(&hP,NULL,Names->Text.c_str(),pType,0))

CryptAcquireContext(&hP,NULL,Names->Text.c_str(),pType, CRYPT_VERIFYCONTEXT);

/* разрешение выбора в списках «Криптоалгоритмы» и «Контейнеры ключей» */ 

Algs->Enabled=true;

Conts->Enabled=true;

/* получение строки для поиска в списке «Типы криптопровайдеров»*/

Tmp.printf("(%d)",pType);

// поиск в списке «Типы криптопровайдеров»

for(j=0;j<Types->ltems->Count;j++)

{        nt=Types->ltems->Strings[j];

if(nt.Pos(Tmp)) break; }

// выделение строки в списке «Типы криптопровайдеров»

if(j<Types->Items->Count) Types->Itemlndex=j;

// начало получения информации о криптоалгоритмах

i=0;

// цикл получения информации о крипто алгоритмах

 if(CryptGetProvParam(hP,PP_ENUMALGS_EX,(BYTE*)&pAlg,&Len, CRYPT_FIRST))

do

{ // добавление имени к списку «Криптоалгоритмы»

Algs->ltems->Add(pAlg.szName);

// сохранение информации об очередном алгоритме

pA[i]=pAlg.aiAlgid;

Def[i]=pAlg.dwDefaultLen;

Min[i]=pAlg.dwMinLen;

Max[i]=pAlg.dwMaxLen;

// увеличение индекса алгоритма

i++;

// установка длины буфера

Len=sizeof(pAlg);     }

while(CryptGetProvParam(hP,PP_ENUMALGS_EX, (BYTE*)&pAlg,&Len,0));

/* вывод сообщения об ошибке, если информация об алгоритмах недоступна */

 else

{FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |

FORMAT_MESSAGE_FROM_SYSTEM, NULL,

GetLastError(),

MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),

(LPTSTR)&MsgBuf,0,NULL);

ShowMessage(AnsiString("Ошибка при получении информации об алгоритмах :\n") +(char*)MsgBuf); LocalFree(MsgBuf);

// запрет выбора в списке «Криптоалгоритмы»

Algs->Enabled=false;}

// установка длины буфера

Len=sizeof(pTN);

// цикл получения информации о контейнерах ключей

if(!CryptGetProvParam(hP,PP_ENUMCONTAINERS,pTN, &Len,CRYPT_FIRST))

/* вывод сообщения об ошибке, если информация о контейнерах недоступна */

FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,

NULL,GetLastError(),

MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

(LPTSTR) &MsgBuf,0,NULL);

ShowMessage(AnsiString( "Ошибка при получении информации о контейнерах :\n") +(char*)MsgBuf); LocalFree(MsgBuf);

// запрет выбора в списке «Контейнеры ключей» Conts->Enabled=false;}

else do

{   // добавление имени к списку «Контейнеры ключей»

Conts->ltems->Add(pTN);

// установка длины буфера

Len=size6f(pAlg);     }

while(CryptGetProvParam(hP,PP_ENUMCONTAINERS,pTN, &Len,0));

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

В обработку изменения выбора в списке «Криптоалгоритмы» поместим код:

TListltem *item; // элемент списка «Параметры алгоритма»

DWORD at.bl; // тип алгоритма и длина блока

// получение кода выбранного алгоритма

ALGJD ai=pA[Algs->ltemlndex];

// очистка списка «Параметры алгоритма»

Key->ltems->Clear();

// добавление элемента к списку «Параметры алгоритма» item=Key->ltems->Add();

/* определение и отображение информации о типе выбранного алгоритма */

if(GET_ALG_CLASS(ai)==ALG_CLASS_HASH)

Рис. 3. Сообщения об ошибках при получении информации о контейнерах ключей

iiem->Caption="XeujHpoBaHHe";

else lf(GET_ALG_CLASS(ai)==ALG_CLASS_SIGNATURE)

item->Caption="ЭЦП";

else lf(GET_ALG_CLASS(ai)~ALG_CLASS_KEY_EXCHANGE)

item->Caption ="Обмен сеансовыми ключами";

else if(GET_ALG_CLASS(ai)==ALG_CLASS_DATA_ENCRYPT)

item->Caption ="Шифрование данных";

else if(GET_ALG_CLASS(ai)==ALG_CLASS_MSG_ENCRYPT)

item->Caption ="Шифрование сообщений";

else item->Caption ="Неизвестный";

if(item->Caption!="Hen3BecTHb^")

// получение и отображение информации о длинах ключа

{item->Subltems->Add(Def[Algs->ltemlndex]);

item->Subltems->Add(Min[Algs->ltemlndex]);

item->Subltems->Add(Max[Algs->ltemlndexj);

// установка длины буфера

Len=sizeof(at);

/* получение и отображение информации о шаге изменения длины ключа алгоритмов ЭЦП и обмена сеансовыми ключами */

Key->Columns->ltems[4]->Caption="lllar изменения длины";

if(GET_ALG_CLASS(ai)==ALG_CLASS_SIGNATURE)

if(CryptGetProvParam(hP,PP_SIG_KEYSIZE_INC,(BYTE*)&at,&Len,0))

item->Subltems->Add(at); else;

else if(GET_ALG_CLASS(ai)==ALG_CLASS_KEY_EXCHANGE)

if(CryptGetProvParam(hP,PP_KEYX_KEYSIZE_INC,(BYTE*)&at,&Len,0))

item->Subltems->Add(at); else;

/* получение и отображение информации о длине блока сеансового ключа */

else if(GET_ALG_CLASS(ai)==ALG_CLASS_DATA_ENCRYPT)

{// освобождение дескриптора ранее созданного ключа

if(hK) CryptDestroyKey(hK);

Key->Columns->ltems[4]->Caption="flnnHa блока";

// создание случайного сеансового ключа

if(CryptGenKey(hP,ai,0,&hK))

{// установка длины буфера Len=sizeof(bl);

if(CryptGetKeyParam(hK,KP_BLOCKLEN,(BYTE*)&bl,&Len,0))

item->Subltems->Add(bl);} else

{

// вывод сообщения об ошибке при получении длины блока

FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |

FORMAT_MESSAGE_FROM_SYSTEM, NULL,

GetLastError(),

MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

(LPTSTR) &MsgBuf,0,NULL);

ShowMessage(AnsiString( "Ошибка при получении длины блока:\n")+ (char*)MsgBuf);

LocalFree(MsgBuf);    }     }

/* очистка заголовка последнего столбца списка «Параметры алгоритмов» для алгоритмов хеширования и шифрования сообщений */

else

if(GET_ALG_CLASS(ai)==ALG_CLASS_HASH ||

GET_ALG_CLASS(ai)==ALG_CLASS_MSG_ENCRYPT)

Рис. 4. Сообщение об ошибке при получении информации о криптоалгоритме

Key->Columns->ltems[4]->Caption="";     }

Некоторые криптопровайдеры неправильно обрабатывают запрос на получение информации о длине блока, поэтому при выполнении этой программы может выводиться сообщение об ошибке (рис. 4).

Создание, хранение и распространение криптографических ключей

Большинство криптопровайдеров не создают автоматически пары ключей асимметричного шифрования при создании контейнера ключей.

1. Для создания пар ключей асимметричного шифрования
должна использоваться следующая функция
CryptoAPI:

BOOL CryptGenKey(HCRYPTPROV hProv, ALGJD Algid, DWORD dwFlags, HCRYPTKEY *phKey); /* создание в контейнере ключей с дескриптором hProv пары ключей ЭЦП (Algid=AT_SIGNATURE) или обмена сеансовыми ключами (Algid=AT_KEYEXCHANGE) и запись дескриптора открытого ключа созданной пары в *phKey; если секретный ключ созданной пары должен иметь возможность экспорта из CSP, то dwFlags=CRYPT_EXPORTABLE (открытые ключи всегда являются экспортируемыми). */

2. Получение дескриптора открытого ключа *phUserKey из соот-
ветствующего контейнера ключей
hProv возможно с помощью
функции:

BOOL CryptGetUserKey(HCRYPTPROV hProv, DWORD dwKeySpec, HCRYPTKEY *phUserKey); I* параметр dwKeySpec определяет тип запрашиваемого ключа - обмена (AT_KEYEXCHANGE,) или ЭЦП fAT_SIGNATURE; */

Если функция CryptGetUserKey возвращает FALSE с кодом ошибки NTE_NO_KEY, то запрашиваемый открытый ключ в контейнере ключей криптопровайдера с дескриптором hProv не существует и соответствующая пара ключей асимметричного шифрования (для обмена сеансовыми ключами или ЭЦП) должна быть создана с помощью функции CryptGenKey.

3. Сеансовый ключ можно создать двумя способами. При первом способе используется функция CryptGenKey со значением параметра Algid, равным коду алгоритма используемого в приложении симметричного шифрования (CALG_DES, CALG_RC2, CALG_3DES, CALG_RC4 или др.). Значение параметра dwFlags может быть объединением следующих флагов: CRYPT_CREATE_SALT (использование случайного значения для модификации ключа шифрования), CRYPT_EXPORTABLE (сеансовый ключ может экспортироваться из CSP) и др.

При создании ключа может быть задана его длина, отличающаяся от длины по умолчанию. В этом случае параметр dwFlags при вызове функции CryptGenKey должен в комбинации с другими допустимыми флагами содержать значение типа DWORD, старшие 16 бит которого содержат требуемую длину ключа (например, для задания длины ключа ЭЦП 2048 бит нужно указать значение 0x08000000).

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

BOOL CryptCreateHash(HCRYPTPROV hProv, ALGJD Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash); /* создание в криптопровайдере с дескриптором hProv пустого хеш-значения с дескриптором *phHash Algid, должен содержаться код алгоритма хеширования CALG_MD2, CALG_MD4, CALG_MD5 или CALG_SHA, параметры hKey и dwFlags не используются и должны быть равны нулю. */

BOOL CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD dwDataLen, DWORD dwFlags); /* добавление к хеш-значению с дескриптором hHash данных (например, парольной фразы) из буфера *pbData длиной dwDataLen; параметр dwFlags не используется и должен быть равен нулю. */

BOOL CryptDeriveKey(HCRYPTPROV hProv, ALGJD Algid, HCRYPTHASH hBaseData, DWORD dwFlags, HCRYPTKEY *phKey); /* создание сеансового ключа с дескриптором *phKey из хеш-значения с дескриптором hBaseData; значения параметров Algid (код алгоритма шифрования) и dwFlags (флаги) могут быть такими же, как при вызове функции CryptGenKey. */

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

BOOL CryptDestroyKey(HCRYPTKEY hKey); /* разрушение дескриптора ключа шифрования hKey. */

BOOL CryptDestroyHash(HCRYPTHASH hHash); /* разрушение дескриптора хеш-значения hHash */

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

Добавим следующий код в обработку изменения выбора в списке «Контейнеры ключей» (см. рис. 2):

// получение имени выбранного контейнера 

AnsiString pN=Conts->ltems->Strings[Conts->ltemlndex];

bool flag=false; // признак создания ключей

/* освобождение ранее полученного дескриптора криптопровайдера */

if(hP) CryptReleaseContext(hP,0);

/* получение дескриптора к,иптопровайдера для выбранного контейнера ключей */

if(CryptAcquireContext(&hP,pN.c_str(),

Names->Text.c_str(),pType,0))

{ /* начало подготовки к формированию сообщения о результатах проверки 7

Тmр="В контейнере "+pN+":\n";

// освобождение дескриптора ранее созданного ключа

if(hK) CryptDestroyKey(hK);

// проверка создания ключей ЭЦП

if(CryptGetUserKey(hP,AT_SIGNATURE,&hK))

{   Тmр+="есть пара ключей ЭЦП \n";

flag=true;    }

// проверка создания ключей обмена 

if(CryptGetUserKey(hP,AT_KEYEXCHANGE,&hK))

{    Тmр+="есть пара ключей обмена \n";

flag=true;    }

if (!flag) Тmр+="не созданы ключи" ;

// вывод сообщения о результатах проверки

ShowMessage(Tmp); }

/* обработка ошибок при открытии контейнера ключей и вывод сообщения */ 

else

{ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|

FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),

MAKELANG ID(LANG_NEUTRAL,SUBLANG_DEFAULT),

(LPTSTR) &MsgBuf,0,NULL);

ShowMessage(AnsiString( "Ошибка при получении информации о контейнере:") +pN+":\n")+(char*)MsgBuf); LocalFree(MsgBuf);

/* получение дескриптора криптопровайдера без доступа к секретному ключу (в режиме проверки) */

CryptAcquireContext(&hP,NULL,Names->Text.c_str(),pType,

CRYPT_VERIFYCONTEXT);     }

На рис. 5 приведены пример окна с сообщением о результатах проверки создания пар ключей в одном из контейнеров (рис. 5,а) и пример вывода сообщения о невозможности получения информации о контейнере ключей (рис. 5,6).

Для изменения режима симметричного шифрования (ECB, СВС, CFB или OFB) используется функция CryptSetKeyParam: BOOL CryptSetKeyParam(HCRYPTKEY hKey, DWORD dwParam, BYTE *pbData, DWORD dwFlags); Л установка режима шифрования для сеансового ключа с дескриптором hKey; dwParam = =KP_MODE, pbData указывает на переменную типа DWORD (unsigned long), в которой записан код устанавливаемого режима, dwFlags = 0 */

По умолчанию режим шифрования устанавливается в CRYPT_MODE_ СВС. Функция CryptSetKeyParam может также применяться и для установки других параметров шифрования: 

                          а)                                          б)

Рис. 5. Примеры сообщений с информацией о контейнере ключей

использование вектора инициализации (синхропосылки) -dwParam = KP_IV, а в буфер *pbData нужно поместить значение вектора инициализации;

использование случайных значений для модификации ключа шифрования - параметр dwParam - KP_SALT, а в буфер *pbData нужно поместить случайное значение;

размер порции данных для режимов шифрования с обратной связью CFB или OFB - параметр dwParam = KP_MODE_BITS , а в буфер *pbData нужно поместить длину порции данных (для встроенных в Windows криптопровайдеров Microsoft это значение по умолчанию равно 8);

дополнительные ограничения на использование сеансового ключа -параметр dwParam=KP_PERMISSIONS и в буфер *pbData нужно поместить комбинацию флагов CRYPT_ENCRYPT (разрешено шифрование на этом ключе), CRYPT_DECRYPT (разрешено расшифрование с помощью этого ключа), CRYPT_EXPORT (ключ может экспортироваться из CSP), CRYPT_READ (параметры ключа могут быть прочитаны), CRYPT_WR!TE (параметры ключа могут быть изменены), CRYPT_MAC (ключ может использоваться для вычисления кода аутентификации сообщений); встроенные в Windows криптопровайдеры поддерживают только флаг CRYPT_EXPORT.

Пример. Рассмотрим пример создания ключа шифрования файла на основе введенной парольной фразы в программе на языке С++, созданной в системе программирования Microsoft Visual С++. Парольная фраза вводится с помощью диалогового окна (рис. 6), которому в приложении соответствует объект Dialog6.

С текстовым редактором для ввода пароля в классе этого объекта связано строковое поле m_Edit1.

На глобальном уровне объявим переменные:

Рис. 6. Окно ввода парольной фразы

HCRYPTPROV hProv=0; // дескриптор криптопровайдера 

HCRYPTKEY hKey; // дескриптор ключа шифрования (расшифрования)

HCRYPTHASH hHash; // дескриптор хеш-значения

DWORD Param=CRYPT_MODE_CBC;// режим симметричного шифрования

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

/* получение дескриптора криптопровайдера по умолчанию типа PROV_RSA_FULL для контейнера ключей с именем по умолчанию (при необходимости создание контейнера) */ 

if(!CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,0))

if((unsigned)GetLastError()==NTE_BAD_KEYSET)

CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET);

// создание пустого хеш-значения 

CryptCreateHash(hProv,CALG_SHA.O,0,&hHash);

// хеширование парольной фразы

CryptHashData(hHash,

(BYTE*)Dialog6.m_Edit1.GetBuffer(

Dialog6.m_Edit1 .GetLength()),

Dialog6.m_Edit1 .GetLength(),0);

// генерация сеансового ключа

CryptDeriveKey(hProv,CALG_RC2,hHash,CRYPT_EXPORTABLE, &hKey);

// установка режима симметричного шифрования 

CryptSetKeyParam(hKey,KP_MODE,(BYTE*)&Param,0);

// разрушение хеш-значения CryptDestroyHash(hHash);

Экспорт криптографического ключа заключается в его получении внутри специального блока данных - блоба (от Bit Large OBject, BLOB) в открытом (тип блоба PUBLICKEYBLOB, предназначен для экспорта открытых ключей асимметричного шифрования) или зашифрованном (типы блоба SIMPLEBLOB для экспорта сеансовых ключей или PRIVATEKEYBLOB для экспорта секретного и открытого ключей асимметричного шифрования). Ключом шифрования секретного ключа при его экспорте должен быть специальный сеансовый ключ, обычно генерируемый на основе парольной фразы. Ключом шифрования экспортируемого сеансового ключа является открытый ключ получателя блоба с сеансовым ключом (т.е. лица с правами расшифрования зашифрованных на этом сеансовом ключе данных). Для экспорта и импорта ключей шифрования предназначены следующие функции CryptoAPI:

BOOL CryptExportKey(HCRYPTKEY hKey, HCRYPTKEY hExpKey, DWORD dwBlobType, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen); /* экспорт криптографического ключа с дескриптором hKey, зашифрованного с помощью ключа с дескриптором hExpKey, в блобе *pbData длиной *pdwDataLen типа dwBlobType; при успешном завершении в *pdwDataLen помещается фактическая длина блоба */

BOOL CryptlmportKey(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen, HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY *phKey); /* расшифрование криптографического ключа из блоба *pbData длиной dwDataLen с помощью ключа с дескриптором hPubKey и импорт восстановленного ключа в контейнер ключей hProv; при успешном завершении в *phKey помещается дескриптор импортированного ключа. */

Расшифрование импортируемого сеансового ключа выполняется с помощью секретного ключа получателя блоба, а импортируемого секретного ключа - с помощью специального сеансового ключа.

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

Если к сеансовому ключу шифрования файла имеет доступ только один пользователь, то потеря (например, в результате программной или аппаратной ошибки) пары ключей обмена этого пользователя может привести к невозможности восстановления зашифрованных данных. Для предотвращения подобной угрозы может использоваться процедура резервной авторизации:

  1.  после шифрования файла и экспорта сеансового ключа обычным образом сеансовый ключ экспортируется повторно с помощью открытого ключа резервной авторизации, а полученный блоб посылается сервису резервной авторизации вместе с описанием сеансового ключа и другой идентифицирующей информации;
  2.  при необходимости восстановления сеансового ключа и расшифрования файла потребуется сначала пройти аутентификацию у сервиса резервной авторизации.

Сервис резервной авторизации должен выполняться на защищенном (с помощью организационных и инженерно-технических методов) компьютере и предоставлять память для хранения сеансовых ключей своих клиентов.

Организация обмена

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

  1.  отправитель экспортирует из криптопровайдера свой открытый ключ в форме блоба (типа PUBLICKEYBLOB) с помощью функции Сгур1Ехро11Кеу;
  2.  блоб посылается получателю по защищенному каналу связи (например, в зашифрованном виде с помощью сеансового ключа, генерируемого из специальной парольной фразы, или в открытом виде с возможной конвертацией в текстовый формат);

3) получатель блоба импортирует его в свой криптопровайдер с помощью функции CryptlmportKey.

Для передачи сеансового ключа также могут применяться два способа. В первом случае отправитель создает случайный сеансовый ключ (с помощью функции CryptGenKey), зашифровывает его при помощи ранее полученного открытого ключа получателя (используется функция CryptExportKey) в виде блоба типа SIMPLE-BLOB и отправляет блоб получателю, который расшифровывает сеансовый ключ с помощью своего секретного ключа обмена (используется функция CryptlmportKey). Для снижения риска повторной посылки нарушителем перехваченного им ранее блоба с сеансовым ключом и зашифрованного с помощью этого ключа сообщения можно использовать отметки времени или последовательную нумерацию сообщений на основе случайного начального значения.

Другой способ передачи сеансового ключа заключается в использовании специального трехфазного протокола (предполагается, что отправитель А и получатель В имеют открытые ключи обмена РКА, РКВ и логические имена IDA, Юв друг друга):

Первая фаза протокола.

А: генерация случайного сеансового ключа kA (CryptGenKey); шифрование его с помощью открытого ключа В ЕРКВ (кд) (CryptExportKey).

А->В: Еркв (кд).

В: расшифрование блоба с сеансовым ключом кА=0ЗКв(Еркв (kA)) (CryptlmportKey).

Вторая фаза протокола:

В: генерация случайного сеансового ключа kB (CryptGenKey); шифрование его с помощью открытого ключа A ЕРКА (kB) (CryptExportKey).

В->А: ЕРКА (кв).

В: вычисление хеш-значения H1B=H(kA, Юв, кв, ЮА, «Фаза 2») (CryptCreateHash, CryptHashSessionKey, CryptHashData, CryptGetHashParam).

B->A: H1B.

А: расшифрование блоба с сеансовым ключом kB=DsKA(EpKA (kB)) (CryptlmportKey); вычисление H1A=H(kA, IDB, kB, IDA, «Фаза 2») (CryptCreateHash, CryptHashSessionKey, CryptHashData, CryptGetHashParam); сравнение H1B и H1A; при несовпадении хеш-значений (получатель не является подлинным или произошло ис кажение передаваемых по сети сообщений третьим лицом) выполнение протокола прекращается, а сеанс связи завершается. Третья фаза протокола.

А: вычисляет хеш-значение H2A=H(kB, IDA, IDB, «Фаза 3») (CryptCreateHash, CryptHashSessionKey, CryptHashData, CryptGetHashParam).

A->B: H2A.

В: вычисление хеш-значение Н2в=Н(кв, IDA, IDB, «Фаза 3») (CryptCreateHash, CryptHashSessionKey, CryptHashData, CryptGetHashParam); сравнение H2A и H2B; при совпадении хеш-значений сеансовые ключи кА и кв могут использоваться для обмена зашифрованными с их помощью сообщениями.

При выполнении данного трехфазного протокола потребуется использование еще двух функций CryptoAPI:

BOOL CryptHashSessionKey(HCRYPTHASH hHash, HCRYPTKEY hKey, DWORD dwFlags); /* добавление в хеш-значение с дескриптором hHash сеансового ключа шифрования с дескриптором hKey; значение параметра dwFlags не используется и должно быть равно нулю */

BOOL CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags); /* получение в буфере *pbData длиной *pdwDataLen хеш-значения с дескриптором hHash (dwParam=HP_HASHVALJ; после завершения в *pdwDataLen помещается действительный размер хеш-значения; значение параметра dwFlags не используется и должно быть равно нулю; после получения фактического хеш-значения добавление в него новых данных невозможно 7

Параметр dwParam может принимать еще два значения: HP_HASHSIZE (в *pbData будет содержаться размер в байтах хеш-значения) и HP_ALGID (в буфер *pbData будет помещен код используемого алгоритма хеширования).

Контрольные вопросы

  1.  Дайте определение понятию «Криптографический интерфейс».
  2.  Перечислите принципы взаимодействия приложения и криптопровайдера.
  3.  Какие атрибуты криптопровайдера вам известны?
  4.  Какие типы криптопровайдеров Вы знаете?
  5.  Перечислите основыне функции CryptoAPI 1.0.
  6.  С помощью каких функции CryptoAPI можно получить информацию о криптопровайдере?
  7.  Каким образом можно получить информацию о параметрах ключа?
  8.  С помощью каких функций создаются ключи ассметричного шифровании? Ключ симметричного шифрования?
  9.  Каким образом осуществяелтся экспорт и импорт ключей средствами CryptoAPI?
  10.  Каким образом производится организация обмена открытыми ключами ассиметричного шифрования?