Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
защита данных / Криптография в MS Visual Studio.doc
Скачиваний:
56
Добавлен:
31.03.2015
Размер:
743.42 Кб
Скачать

1.3. Непосредственное обращение к криптопровайдерам в приложениях для Microsoft .Net

Как уже отмечалось во введении, среди криптографических классов библиотеки FCL отсутствуют классы, реализующие некоторые широко распространенные криптографические алгоритмы, например, алгоритм потокового симметричного шифрования RC4.

Для создания класса с реализацией алгоритма RC4 потребуется создать новый класс-наследник абстрактного класса SymmetricAlgorithm. В создаваемом классе потребуется переопределить следующие абстрактные методы базового класса SymmetricAlgorithm:

  • void GenerateKey() − генерация случайного ключа симметричного шифрования;

  • void GenerateIV() − генерация случайного вектора инициализации, необходимого при использовании только блочных алгоритмов симметричного шифрования, к которым RC4 не относится;

  • ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) – создание объекта для шифрования данных с заданным значением ключа и неиспользуемым значением начального вектора;

  • ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) – создание объекта для расшифрования данных с заданным значением ключа и неиспользуемым значением начального вектора.

Интерфейс ICryptoTransform (базовый набор криптографических преобразований) включает свойства:

  • bool CanReuseTransform – признак возможности повторного использования криптографического преобразования;

  • bool CanTransformMultipleBlocks – признак возможности преобразования нескольких блоков данных;

  • int InputBlockSize – размер входного блока данных в байтах;

  • int OutputBlockSize – размер выходного блока данных в байтах.

Методы интерфейса ICryptoTransform:

  • void Dispose() – освобождение ресурсов преобразования;

  • int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) – преобразование inputCount байт из буфера inputBuffer со смещением inputOffset и запись результата в буфер outputBuffer со смещением outputOffset (метод возвращает количество записанных байт);

  • byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) – преобразование inputCount байт последнего блока входных данных inputBuffer со смещением inputOffset (метод возвращает указатель на массив преобразованных данных).

Напомним, что интерфейсы в языке программирования C# являются альтернативным по отношению к абстрактным классам способом определения базовых методов для множества объектов. Создаваемый класс для реализации дополнительного алгоритма симметричного шифрования должен быть наследником класса SymmetricAlgorithm и наследником интерфейса ICryptoTransform. В нем должны быть определены абстрактные методы класса SymmetricAlgorithm и все методы интерфейса ICryptoTransform.

При разработке методов, реализующих шифрование и расшифрование по алгоритму RC4 с помощью криптопровайдеров Microsoft Windows, потребуется использовать функции, экспортируемее этими криптографическими модулями и атрибут DllImport, позволяющий вызывать в программном коде, управляемом средой Microsoft .NET Framework, функции, которые будут выполняться в неуправляемом коде.

Например, в программе на языке C# в этом случае потребуется выполнить следующее:

  • определить метод, создаваемый с помощью экспортируемой из криптопровайдера функции с ключевыми словами static и extern;

  • добавить к определению метода атрибут DllImport, позволяющий определить имя динамически компонуемой библиотеки (DLL) криптопровайдера;

  • при необходимости определить дополнительную информацию о параметрах и возвращаемом значении экспортируемой функции.

Приведем пример определения метода на основе функции CryptEncrypt (шифрование данных), экспортируемой из DLL криптопровайдера:

[DllImport ("advapi32.dll", CharSet =CharSet.Auto, SetLastError = true)]

[return: MarshalAs (UnmanagedType.Bool)]

public static extern bool CryptEncrypt (IntPtr hKey,

IntPtr hHash, int Final, uint dwFlags, byte [] pbData,

ref uint pdwDataLen, uint dwBufLen);

Здесь advapi32.dll – имя системной библиотеки, являющейся посредником между прикладной программой и криптопровайдером.

Класс DllImportAttribute (определение прототипа функции, импортируемой из внешней по отношению к среде Microsoft .Net библиотеки) определен в пространстве имен System. Runtime. InteropServices.

Поясним используемые в этом примере параметры функции CryptEncrypt:

  • hKey – дескриптор (системный указатель) на секретный ключ;

  • hHash – может не использоваться и задаваться нулем;

  • Final – признак последней порции шифруемых данных;

  • dwFlags – флаги (могут не использоваться и задаваться нулем);

  • pbData – буфер с шифруемыми данными;

  • pdwDataLen – длина порции шифруемых данных (после выполнения функции в этот параметр записывается фактическое число зашифрованных байт);

  • dwBufLen – длина буфера с шифруемыми данными.

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

Для расшифрования данных, зашифрованных прямым обращением к криптопровайдеру, предназначена функция CryptoAPI CryptDecrypt.

Другие функции CryptoAPI подробно описаны в [10, 12] и в документации по программированию для Windows.

После установки в операционной системе криптопровайдеров, предоставляющих реализации криптоалгоритмов, соответствующих российским стандартам, разработка новых классов-«оберток» над таким криптопровайдером производится аналогично созданию классов для представления алгоритмов, реализуемых входящими в состав Microsoft Windows криптопровайдерами.

Изменим второй пример из раздела 1.2 и будем шифровать введенный пользователем текст по алгоритму RC4, реализация которого будет импортироваться из криптопровайдера по умолчанию операционной системы Windows.

Главное окно программы приведено на рис. 3. В программе используются следующие константы, значения которых можно узнать из системного заголовочного файла wincrypt.h:

  • 1 (PROV_RSA_FULL, значение провайдера по умолчанию, реализующего криптографические алгоритмы обмена ключами и электронной цифровой подписи на основе асимметричной криптосистемы RSA; используется при вызове функции CryptAcquireContext);

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

  • 0x6801 (CALG_RC4, значение кода алгоритма потокового шифрования RC4; используется при вызове функции CryptGenKey);

  • 22 (PP_ENUMALGS_EX, значение параметра для получения характеристик одного из реализованных криптопровайдером алгоритмов);

  • 1 (CRYPT_FIRST, значение флага для получения характеристик первого из реализованных криптоалгоритмов);

  • 2 (CRYPT_NEXT, значение флага для получения характеристик второго и последующих реализованных криптоалгоритмов);

  • 35 (PP_KEYX_KEYSIZE_INC, значение параметра для получения шага изменения возможной длины ключа).

Длина секретного ключа при вызове функции CryptGenKey задается старшими 16 битами параметра dwFlags [12].

Имена объектов программы, соответствующих элементам управления главного окна, используются в соответствии с табл. 2.

Ниже приведен текст программы.

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

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

using System.Security.Cryptography;

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

using System.Runtime.InteropServices;

Рис. 3. Главное окно программы

namespace WindowsFormsApplication2

{

public partial class Form1 : Form

{

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

IntPtr hProv;

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

IntPtr key;

// длина ключа

int KeyLength;

// буфер для массива байт шифруемой строки

byte[] toEncrypt;

// длина шифрумых и зашифрованных данных

uint PlainLen, CipherLen;

// буфер для массива расшифрованных байт

byte[] encrypted;

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

// инициализация криптопровайдера

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptAcquireContext(ref IntPtr hProv,

string pszContainer, string pszProvaider, uint dwProvType,

uint dwFlags);

/* структура PROV_ENUMALGS_EX для приема параметорв криптопровайдера */

[StructLayout(LayoutKind.Sequential)]

public struct PROV_ENUMALGS_EX

{

public int aiAlgid;

public int dwDefaultLen;

public int dwMinLen;

public int dwMaxLen;

public int dwProtocols;

public int dwNameLen;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]

public string szName;

public int dwLongNameLen;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]

public string szLongName;

}

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

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptGetProvParam(IntPtr hProv, uint dwParam,

ref PROV_ENUMALGS_EX pbData, ref int pdwDataLen,

uint dwFlags);

// генерация случайного секретного ключа

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptGenKey(IntPtr hProv, uint Algid,

uint dwFlags, ref IntPtr hKey);

// шифрование данных

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptEncrypt(IntPtr hKey,

IntPtr hHash, int Final, uint dwFlags, byte[] pbData,

ref uint pdwDataLen, uint dwBufLen);

// расшифрование данных

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptDecrypt(IntPtr hKey,

IntPtr hHash, int Final, uint dwFlags, byte[] pbData,

ref uint pdwDataLen);

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

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]

[return: MarshalAs(UnmanagedType.Bool)]

public static extern bool CryptReleaseContext(IntPtr hProv, uint dwFlags);

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

bool res;

// массив возможных значений длины ключа

int[] ks;

// конструктор класса формы главного окна программы

public Form1()

{

InitializeComponent();

// инициализация криптопровайдера

if (!CryptAcquireContext(ref hProv, null, null, 1, 0))

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

if (!CryptAcquireContext(ref hProv, null, null, 1, 8))

throw new CryptographicException

("Ошибка при инициализации криптопровайдера.");

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

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

PROV_ENUMALGS_EX data = new PROV_ENUMALGS_EX();

// задание длины буфера для приема параметров

int dlen = 100;

/* получение параметров для первого криптоалгоритма, реализованного в криптопровайдере */

if (!CryptGetProvParam(hProv, 22, ref data, ref dlen, 1))

throw new CryptographicException

("Ошибка при получении параметров длины ключа.");

// цикл до получения параметров криптовлгоритма RC4

while (data.szName != "RC4")

CryptGetProvParam(hProv, 22, ref data, ref dlen, 2);

// получение минимально возможной длины ключа

KeyLength = data.dwMinLen;

// создание массива данных для списка

ks = new int[12];

// индексы элемента списка и выделенного элемента

int i=0, isel=0;

// максимальная длина ключа и длина ключа по умолчанию

int ksmax=data.dwMaxLen, ksdef=data.dwDefaultLen;

// получение шага изменения возможной длины ключа

// задание длины буфера дли приема данных

dlen = 4;

/* первое поле объекта data в этом случае содержит шаг изменения длины ключа */

if (!CryptGetProvParam(hProv, 35, ref data, ref dlen, 0))

throw new CryptographicException

("Ошибка при получении шага изменения длины ключа.");

// заполнение списка возможных значений длины ключа

do

{

ks[i] = KeyLength;

// сохранение индекса для длины ключа по умолчанию

if (KeyLength == ksdef)

isel = i;

// увеличение возможной длины ключа

KeyLength += data.aiAlgid;

i++;

}

while (KeyLength <= ksmax);

// связывание списка с массивом данных

KeyLen.DataSource = ks;

// выделение элемента списка

KeyLen.SelectedIndex = isel;

}

// обработка нажатия кнопки "Выход"

private void Exit_Click(object sender, EventArgs e)

{

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

CryptReleaseContext(hProv, 0);

// закрытие главного окна

Close();

}

// обработка изменения открытого текста

private void PlainText_TextChanged(object sender, EventArgs e)

{

/* блокирование кнопки "Зашифровать", если открытый текст не введен */

Encrypt.Enabled = PlainText.Text.Length != 0;

}

// обработка изменения шифротекста

private void CipherText_TextChanged(object sender, EventArgs e)

{

/* блокирование кнопки "Расшифровать", если шифротекст не получен */

Decrypt.Enabled = CipherText.Text.Length != 0;

}

// обработка нажатия кнопки "Зашифровать"

private void Encrypt_Click(object sender, EventArgs e)

{

// очистка редактора с шифротекстом

CipherText.Clear();

// декодирование введенной строки открытого текста

toEncrypt = Encoding.Unicode.GetBytes(PlainText.Text);

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

KeyLength = ks[KeyLen.SelectedIndex];

/* задание длины секретного ключа для его генерации функцией CryptGenKey */

KeyLength = KeyLength << 16;

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

res=CryptGenKey(hProv, 0x6801, (uint)KeyLength, ref key);

// если функция возвращает FALSE, то генерация исключения

if (!res)

throw new CryptographicException("Ошибка генерации ключа.");

// задание длины открытого текста

PlainLen = (uint)toEncrypt.Length;

/* шифрование открытого текста (зашифрованные данные помещаются в буфер с открытым текстом) */

res=CryptEncrypt(key, (IntPtr)null, 1, 0, toEncrypt, ref PlainLen,

(uint)toEncrypt.Length);

// если функция возвращает FALSE, то генерация исключения

if(!res)

throw new CryptographicException

("Ошибка шифрования данных.");

// получение шифротекста

encrypted = toEncrypt;

// отображение шифротекста

CipherText.Text = Encoding.Unicode.GetString(encrypted, 0,

(int)PlainLen);

// блокирование кнопки "Зашифровать"

Encrypt.Enabled = false;

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

KeyLen.Enabled = false;

}

// обработка нажатия кнопки "Расшифровать"

private void Decrypt_Click(object sender, EventArgs e)

{

// очистка строки с открытым текстом

PlainText.Clear();

// получение шифротекста

encrypted = Encoding.Unicode.GetBytes(CipherText.Text);

// создание буфера для расшифрованных данных

toEncrypt = new byte[encrypted.Length];

// задание длины зашифрованных данных

CipherLen = (uint)encrypted.Length;

/* расшифрование данных (расшифрованные данные помещаются в буфер с шифротекстом) */

res = CryptDecrypt(key, (IntPtr)null, 1, 0, encrypted, ref CipherLen);

// если функция возвращает FALSE, то генерация исключения

if(!res)

throw new CryptographicException

("Ошибка расшифрования данных.");

// отображение расшифрованного открытого текста

toEncrypt = encrypted;

PlainText.Text = Encoding.Unicode.GetString(toEncrypt, 0,

toEncrypt.Length);

// блокирование кнопки "Расшифровать"

Decrypt.Enabled = false;

}

}

}

Соседние файлы в папке защита данных