Средства безопасности ASP.NET
Часть 3 – Криптография
В предыдущих двух частях статьи были рассмотрены вопросы
аутентификации и авторизации пользователей. Напомним, что аутентификация
подразумевает под собой ввод имени логина и его пароля, т. е. регистрацию; а
авторизация – это проверка прав доступа к определённому ресурсу или на
выполнение каких-либо действий. Нераскрытым остался последний вопрос –
криптография.
Основная задача криптографии – сделать конечные данные
непонятными для третьих лиц. Первые и простейшие методы шифрования начали
встречаться ещё в эпоху античности: существовал так называемый метод Цезаря,
который заключался в смещении каждого символа текста на три позиции вперёд по
алфавиту. Но если две с лишним тысячи лет назад криптография имела лишь
практическое представление, то сегодня существует целая наука криптология,
занимающаяся вопросами шифрования и дешифрования и имеющая прочную
теоретическую базу.
Классификация
В связи с изобилием криптометодов группировать их можно по
различным признакам, поэтому существует несколько видов классификации. Мы
остановимся только на двух из них: первый основан на области применения, а
второй – на принципе действия.
Классификация на основе области применения разделяет все
криптосистемы на две группы: на системы ограниченного использования и на
системы общего использования. К первой группе относятся криптометоды,
стойкость которых определяется тем, насколько долго алгоритм
шифрования/дешифрования будет находиться в секрете. Зачастую примерами таких
криптосистем являются системы, не использующие пароли. Ко второй группе, соответственно,
принадлежат криптометоды, стойкость которых не зависит от алгоритма, т. е.,
чаще всего, их надёжность упирается в ключ.
Криптография, кроме сокрытия информации от “чужих глаз и ушей”,
также позволяет убедиться в достоверности полученных данных, т. е. определить,
были ли они искажены при передаче; а также позволяет определить отправителя,
иными словами, даёт некоторую гарантию, что данные получены от легального
отправителя. На основе такой широкой области применения криптометодов появился
ещё один тип классификации – классификация по принципу действия.
Под термином “Искажение данных при передаче” понимается
умышленный перехват и изменение информации при её трансляции по сетям.
Официально члены, классифицированные по принципу действия,
принято назвать криптографическими примитивами (Cryptographic Primitives). Существуют следующие
примитивы:
Криптографический примитив
|
Описание
|
Шифрование с секретным
ключом (симметричная система)
|
Представляет данные в недоступном для третьих лиц виде,
используя единый секретный ключ для шифрования и дешифрования.
|
Шифрование с открытым ключом (ассиметричная система)
|
Представляет данные в недоступном для третьих лиц виде,
используя пару из открытого и секретного ключей для шифрования и
дешифрования.
|
Криптографическая подпись
|
Позволяет удостовериться, что данные получены от
соответствующего отправителя путём создания цифровой подписи, свойственной
лишь данному отправителю. Использует хеш-функции
|
Криптографическое хеширование
|
Преобразует данные любой длины в сочетание байтов
фиксированной длины. Хеши чаще всего уникальны, потому сочетание из двух
различных байтов не хешируются в один и тот же результат. Применяется при
верификации данных.
|
Рассмотрим эти примитивы поподробнее.
Шифрование с секретным ключом
Криптометоды с секретным ключом, или как их ещё называют симметричные
методы, используют один и тот же ключ для шифрования и дешифрования,
поэтому его нужно хранить в секрете от третьих лиц, поскольку, зная ключ и имея
соответствующий криптоалгоритм, злоумышленник может дешифровать данные. Симметричные
системы отличаются высокой скоростью работы (в сравнении с ассиметричными), что
позволяет применять их для шифрования крупного объёма данных.
Симметричные криптосистемы делятся в свою очередь ещё на две
группы: на блочные и поточные методы.
Блочные шифры применяют одно и то же преобразование к
тексту, разбитому на блоки, длина которых может быть равна 8, 16, 24, 32 байтам
в зависимости от криптометода. Криптосистема, предоставляемая .NET работает по принципу построения
цепочки блочных шифров (Cipher Block Chaining
– CBC), которая использует ключ и вектор инициализации
(Initialization Vector – IV). Простая блочная система
шифрования, неиспользующая вектора инициализации, преобразует один и тот же
блок исходного текста в тот же самый блок зашифрованного текста, т. е. безо
всяких перемещений и рекомбинаций. Если у вас был дублированный блок в исходном
тексте, то он появится и зашифрованном. В результате, зная структуру исходного
текста, злоумышленник может дешифровать соответствующую часть криптограммы и
определить ключ шифрования. Чтобы этого избежать, в технологии CBC информация из предыдущего блока внедряется в шифруемый
следующий блок. Поскольку такой подход использует предыдущий блок для
шифрования следующего, то IV шифрует
самый первый блок. Это позволяет защитить заголовок (первый блок), чтобы взломщик
не мог использовать его для получения ключа.
Необходимость использования IV для шифрования 1-го блока возникает из-за того, что
применение предыдущего блока для шифрования следующего начинается лишь со 2-го
блока, поскольку у 1-го нет предыдущего блока – он и так стоит в самом начале.
Другая группа симметричных криптосистем – поточные методы. Эти
методы применяют изменяющиеся во время шифрования преобразования к наборам
символов, которые образуют единый исходный текст.
Среди симметричных алгоритмов можно выделить следующие: DES (Data Encryption Standard),
DES 2, различные вариации TripleDES,
Rijndael/AES (Advanced Encryption Standard), RC2, RC4,
ГОСТ 28147-89… Если взглянуть на их алгоритмы, то очевидно, что многие из них
основаны на операторе XOr. Вообще, впервые оператор “исключающий
или” был применён в так называемом методе одноразовых блокнотов,
изученный Клодом Шенноном. Действовал этот метод по следующему принципу:
бралась строка исходного текста и строка ключа такой же длины, после чего
каждый символ исходного текста XOr’ился с
соответствующим символом строки ключа. Этот метод работает и как шифровщик, и как
дешифровщик, поскольку повторный вызов функции XOr возвращает исходное значение.
Алгоритм TripleDES имеет
различные конфигурации, немного отличающиеся по принципу работы:
*** DES-EEE3 – тройное
шифрование с различными ключами
*** DES-EDE3 – шифрует,
дешифрует и ещё раз шифрует с различными ключами
*** DES-EEE2 – тройное
шифрование, но одинаковые ключи только при первой и третьей итерациях
***DES-EDE2 – шифрование,
дешифрование и ещё раз шифрование с одинаковыми ключами при первой и третьей
итерациях
Инфраструктура .NET
Framework содержит
классы для работы со следующими методами: DES, TripleDES, RC2, Rijndael. Но
если воспользоваться услугами CryptoAPI, то можно также
получить возможность работать с поточным симметричным методом шифрования RC4. Позже мы рассмотрим примеры использования всех этих
методов, а сейчас перейдём к следующему криптографическому примитиву – к
шифрованию с открытым ключом (ассиметричная система).
Шифрование с открытым ключом
При использовании асимметричного метода шифрования генерируются
2 ключа: один из них считается секретным, а другой открытым. Оба эти ключа
математически взаимосвязаны, а то, какой из них будет секретным, а какой
открытым определяется пользователем. При этом нет никакой разницы между тем,
кому из них достанется та или иная роль. Отличия появляются на практике, и
выражены они полной противоположностью действий, иными словами, текст,
зашифрованный открытым ключом, может быть расшифрован только секретным, и
наоборот: зашифровав секретным ключом, расшифровать удастся только открытым.
Ассиметричное шифрование используют по следующему алгоритму.
Предположим, у нас есть актор А и актор Б. Актор А хочет
зашифровать данные и отправить их актору Б. Для этого актор Б должен сперва
создать пару ключей, из которой открытый ключ он может свободно переслать
актору А. При этом нет явной угрозы в том, если кто-нибудь перехватит этот
открытый ключ во время его передачи, поскольку сообщение, зашифрованное одним
ключом, может быть дешифровано только другим.
В языке UML под
термином актор понимается некое действующее лицо
Здесь и заключается основная особенность криптометодов с
открытым ключом: если вы собираетесь от кого-то получать данные, шифрованные
ассиметричным методом, или хотите, чтобы кто-либо отправил вам информацию,
зашифрованную подобной системой, то именно вы должны создавать пару ключей, из которой
открытый ключ нужно отправить конечному отправителю. Приходится придерживаться
такой стратегии, поскольку, как уже было ранее сказано, пара ключей связана
между собой математически, и потому должна быть создана на одной машине.
Получив от актора Б открытый ключ, актор А шифрует сообщение
с его помощью, после чего отправляет уже готовую криптограмму актору Б.
Наконец, актор Б успешно получает криптотекст и дешифрует его своим, никому не
известным, секретным ключом.
Рис. 1 – Принцип работы асимметричной системы
Ассиметричные криптометоды очень медленные в сравнении с
симметричными, потому они применимы для небольших объёмов информации. Такое
ограничение также вызвано тем, что ассиметричные алгоритмы имеют буфер
фиксированной длины. Зачастую ассиметричные алгоритмы применяются для
шифрования ключей от симметричных систем, поскольку открытый ключ ассиметричного
криптометода можно спокойно отправлять в плавание по сетям, чего не скажешь о
ключах симметричных методов.
Для сравнения: программа, реализующая шифрование симметричным
криптометодом DES, будет работать примерно в 100 раз
быстрее, чем программа с ассиметричным RSA. А при
аппаратной реализации этих алгоритмов соотношение составит 1000-10000 раз.
Системы с открытым ключом поддерживают гораздо больше
комбинаций ключа, что делает их, с некоторой точки зрения, более безопасными. Кроме
того, ассиметричные алгоритмы используются в цифровых подписях, только с
несколько иным алгоритмом, но об этом позже.
Среди алгоритмов с открытым ключом можно выделить следующие:
RSA (Rivest-Shamir-Adleman), DSA (Digital Signature
Standard), DH (Diffie-Hellman). Из этого небольшого списка .NET поддерживает лишь первые два – RSA и DSA.
Цифровая подпись
Цифровая подпись служит для того, чтобы можно было
удостовериться, что данные получены от соответствующего лица. Для этого
применяются методы хеширования и шифрования с открытым ключом. Чтобы картина
происходящего выглядела яснее, давайте вернёмся к нашим акторам и дадим им
возможность, продемонстрировать весь процесс.
Пусть актор А решил отправить письмо актору Б, и чтобы актор
Б не сомневался в достоверности источника, актор А оставил в письме цифровую
подпись. Для этого актор А сперва хешировал всё сообщение, в результате чего
получилось компактная и уникальная трансформация данных. Далее актор А
использует ассиметричный криптометод для генерации пары ключей и отправляет
открытый ключ актору Б. После этого, хеш шифруется ассиметричным методом, но
порядок шифрования обратный. Данные шифрует не обладатель открытого ключа, т.
е. актор Б, а обладатель секретного ключа – актор А. Как только хеш был
зашифрован, получилась готовая цифровая подпись, которую актор А отправляет
актору Б вместе с оригинальным сообщением. Актор Б получает письмо и выполняет
обратные действия: он дешифрует подпись полученным от актора А открытым ключом,
хеширует данные соответствующим хеш-методом и сверяет результат с оригинальным
сообщением. Если они идентичны, значит можно считать, что сообщение
действительно пришло от актора А.
Помните, цифровая подпись не защищает данные от
несанкционированного доступа – она лишь позволяет лишний раз убедиться или же
разубедиться в достоверности отправителя. Для обеспечения безопасности
транслируемых данных их нужно шифровать.
В платформе .NET Framework для создания и проверки цифровой
подписи существует несколько классов. О них мы поговорим чуть позже.
Хеширование
Хеш-функции служат для преобразования бинарных значений
различной длины к бинарным значениям фиксированной длины. Все хеши уникальны и
очень компактны. Достаточно изменить хотя бы один байт или символ в исходном
тексте, и его хеш примет совсем иной вид.
С помощью хешей можно узнать были ли изменены данные, т. к.
хеши двух абсолютно одинаковых наборов данных также идентичны. Давайте посмотрим
на пример.
Пусть актор А решил отправить актору Б сообщение, перед этим
сделав его хеш и сохранив результат у себя. Актор Б получает сообщение и, чтобы
проверить его достоверность тоже создаёт хеш (тем же самым хеш методом) с
полученного сообщения и отправляет его актору А для сравнения. Если хеши
акторов А и Б будут одинаковыми, значит данные не были модифицированы, в
противном случае, есть все основания полагать, что сообщние было перехвачено и
изменено.
Сегодня существуют различные хеш-методы, и вот список
наиболее известных из них: MD2 (Message Digest), MD4,
MD5, SHA1, SHA256,
SHA384, SHA512, HMAC. Большинство из перечисленных алгоритмов реализовано в
инфраструктуре .NET Framework.
Криптография средствами .NET
В большинстве случаев применение криптографических
инструментов в среде .NET Framework сводится к обращению к
пространству имён System.Security.Cryptography, поскольку именно в нём содержатся все
основные классы и интерфейсы для выполнения криптоопераций.
Большинство поддерживаемых криптометодов используют CryptoAPI, но всё же есть некоторые новинки. Среди них:
Rijndael/AES, SHA256, SHA384, SHA512.
Криптометод AES (Advanced Encryption Standard) – есть не что иное, как расширенный DES, что собственно и следует из его названия. Этот метод был
принят в США взамен DES.
Когда мы хотим что-то зашифровать, то исключительно с
интуитивной точки зрения мы хотим работать со строкой, но большинство
криптометодов не работает со строками – они оперируют массивами байтов и
потоками. Для тех, кто впервые берётся за криптографию, такой подход может
показаться несколько непривычным, но, не смотря на это, у вас в любом случае
останется только 4 выхода:
1.
Смириться и использовать массивы байтов с потоками
2.
Найти удовлетворяющую ваши запросы альтернативу
3.
Придумать свой криптометод
4.
Отказаться от криптографии
Симметричные системы
Простейшее шифрование с помощью метода DES
Чтобы не ходить вокруг да около, давайте создадим Web-приложение, выполняющее шифрование методом DES. Для этого создайте новый проект типа Web Application и
добавьте в него следующий код:
- Листинг 1.1 – CryptoTest/default.aspx
<%@ Page
Language="vb" AutoEventWireup="false"
Codebehind="default.aspx.vb" Inherits="CryptoTest.WebForm1"%>
<html>
<head>
<title>CryptoTesttitle>
head>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<b>Clear Text:b><br>
<textarea id="txt" runat=server>textarea>
<asp:Button ID="btnEncrypt" Text="Encrypt" Runat=server/><br><br>
<b>Encrypted Text:b><br>
<table><tr><td bgcolor=LightGrey>
<asp:Label ID="lblResult" Runat=server/>
td>tr>table>
form>
body>
html>
- Листинг 1.2 – CryptoTest/default.aspx.vb
Imports System.Security.Cryptography
Imports System.Text
Imports System.IO
Public Class WebForm1
Inherits System.Web.UI.Page
Protected WithEvents txt As
System.Web.UI.HtmlControls.HtmlTextArea
Protected WithEvents btnEncrypt As
System.Web.UI.WebControls.Button
Protected WithEvents lblResult As
System.Web.UI.WebControls.Label
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Private Sub btnEncrypt_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnEncrypt.Click
Dim DES As New
DESCryptoServiceProvider
Dim DES_Encryptor As ICryptoTransform = DES.CreateEncryptor
Dim fs As New
FileStream(Server.MapPath("temp.dat"), FileMode.Create)
Dim DESCryptoStream As New
CryptoStream(fs, _
DES_Encryptor, CryptoStreamMode.Write)
Dim enc As New UnicodeEncoding, bytes() As Byte
bytes =
enc.GetBytes(txt.Value)
DESCryptoStream.Write(bytes, 0, bytes.Length)
DESCryptoStream.Close()
fs.Close()
fs = Nothing
fs = New FileStream(Server.MapPath("temp.dat"), FileMode.Open)
Dim sr As New
StreamReader(fs)
lblResult.Text = sr.ReadToEnd
sr.Close()
fs.Close()
File.Delete(Server.MapPath("temp.dat"))
End Sub
End Class
В листинге 1.2 все основные действия происходят в
обработчике события нажатия кнопки btnEncrypt. Давайте
рассмотрим его подробнее. Вначале создаётся экземпляр провайдера шифрования. Для
каждого криптометода существует свой провайдер, потому их имена объявлены
примерно в следующих форматах: <ИмяКриптометода>CryptoServiceProvider или <ИмяКриптометода>Managed. Например, DESCryptoServiceProvider,
RSACryptoServiceProvider, RijndealManaged и др. После инициализации криптопровайдера создаётся
переменная, реализующая интерфейс-шифратор; далее открывается файловый поток
для записи данных в файл. После этого, инициализируется криптопоток, в
аргументах которого задаётся конечный поток данных (в нашем случае – это
файловый поток fs); способ криптографической
трансформации, т. е. интрефейс-шифратор, и действие, которое необходимо
выполнить с данными. Из возможных действий выделяются чтение и запись.
В качестве конечного потока данных (первый атрибут конструктора
класса CryptoStream) чаще всего выступает
файловый поток, но это вовсе не означает, что вы не можете использовать иные
типы потоков. Например, ASP.NET приложения позволяют использовать поток Response.OutputStream для вывода
информации.
На этом возведение подготовительной площадки для дальнейших
криптоопераций заканчивается. В следующих строках кода создаётся массив байтов,
поскольку, как уже было сказано, все криптометоды среды .NET Framework работают
не со строками, а с массивами байтов. Наконец, происходит шифрование в
следующей строке:
DESCryptoStream.Write(bytes, 0, bytes.Length)
В этом отрезке кода проиходят те же действия, что и при
обычных операциях с потоками: методу Write передаётся массив байтов, указывается смещение и длина этого
массива. Таким образом, весь процесс шифрования у нас уместился всего в одну
строчку кода, но перед этим пришлось выстроить массивный подготовительный плацдарм!
В конце все потоки закрываются, потом открываются снова, но
уже для чтения из предварительно созданного файла (temp.dat), далее файловый поток вновь закрывается, а временный
файл удаляется.
Во время выполнения приложения вы можете столкнуться с
фокусом, как на рис. 3. Это сообщение говорит о том, что доступ запрещён для
создания файла temp.dat.
Такое происходит в том случае, если пользователя ASPNET
нет в списке ACL к папке Web-приложения.
По умолчанию процесс ASP.NET-приложений запускается от имени пользователя ASPNET, в чём вы можете убедиться, открыв TaskMan на вкладке Процессы (рис. 4)
Рис. 2 – Система
не позволяет создать файл на сервере: доступ запрещён
Рис. 3 – По умолчанию процесс aspnet_wp.exe
запускается от имени пользователя ASPNET
Чтобы исправить положение, можно:
1.
В explorer.exe открыть свойства папки Web-приложения
(адрес приложения может быть следующим: C:\Inetpub\wwwroot\<ИмяПримложения>),
перейти во вкладку Безопасность и добавить пользователя ASPNET в список допустимых субъектов.
2.
Применить заимствование полномочий для Web-приложения.
В этом случае можно либо строго прописать необходимого пользователя для заимствования
его прав, либо заимствовать права от пользователя, запустившего процесс Web-приложения. Для этого достаточно изменить файл Web.config следующим образом:
xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<authentication mode="Windows" />
<authorization>
<allow users="*" />
authorization>
<identity impersonate="true"/>
system.web>
configuration>
Всё,
что нужно было сделать – это добавить строчку
Информацию о заимствовании полномочий можно найти во 2-й части
этой статьи, которая была посвящена авторизации.
Теперь, если приложение работает без ошибок, можно поработать
с шифрованием. Для этого нужно ввести какой-нибудь текст в поле Clear Text и нажать на кнопку Encrypt.
Результат должен быть подобен рисунку 5.
Заметьте, что повторное шифрование одной и той же строки
возвращает различные данные.
Рис. 4 – Результат успешного выполнения приложения
Добавляем инициализационный вектор (IV) и
ключ
Предыдущий пример был самым простейшим: мы только
зашифровали текст без передачи каких-либо дополнительных параметров. Но
вспомним теорию: в ней было сказано, что для эффективной защиты на симметричные
криптометоды нужно накладывать ключ и инициализационный вектор (далее IV). Этим мы сейчас и займёмся, а также, чтобы не
повторяться, откажемся от файлового потока в пользу потока вывода Response.OutputStream.
Создайте новый проект типа WebApplication и используйте листинги 2.1 и 2.2:
- Листинг 2.1: AdvCrypter/default.aspx
<%@ Page
Language="vb" AutoEventWireup="false"
Codebehind="default.aspx.vb"
Inherits="AdvCrypter.WebForm1"%>
<html>
<head>
<title>AdvCryptertitle>
head>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<b>Clear Text:b><br>
<textarea id="txt" runat=server>textarea>
<asp:Button ID="btnEncrypt" Runat=server Text="Encrypt"/>
form>
body>
html>
Обратите внимание, что количество элементов уменьшилось, т.
к. для отображения результата мы используем поток вывода объекта Response.
- Листинг 2.2: AdvCrypter/default.aspx.vb
Imports System.Security.Cryptography
Imports System.Text
Public Class WebForm1
Inherits System.Web.UI.Page
Protected WithEvents btnEncrypt As
System.Web.UI.WebControls.Button
Protected WithEvents txt As
System.Web.UI.HtmlControls.HtmlTextArea
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Private Sub btnEncrypt_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnEncrypt.Click
Dim DES As New
DESCryptoServiceProvider
DES.GenerateKey()
DES.GenerateIV()
Dim
DES_Encryptor As ICryptoTransform =
DES.CreateEncryptor(DES.Key, DES.IV)
Dim DESCryptoStream As New
CryptoStream(Response.OutputStream, _
DES_Encryptor, CryptoStreamMode.Write)
Dim enc As New UnicodeEncoding, bytes() As Byte
bytes =
enc.GetBytes(txt.Value)
DESCryptoStream.Write(bytes, 0, bytes.Length)
DESCryptoStream.Close()
End Sub
End Class
Очевидно, что, если не использовать файловые потоки, то код
получается гораздо короче, потому что нет необходимости сначала открывать поток
для записи, закрывать его, потом снова открывать для чтения, получать из него
данные и вновь закрывать поток. Одним словом, без файлов жизнь становится
легче!
В коде также появились 2 новые строки, в которых через
провайдер алгоритма DES идёт
генерация ключа и IV. Затем для создания интерфейса
трансформации используется другой прототип перегруженной функции DES.CreateEncryptor, в
котором в качестве аргументов передаются ключ и IV.
GenerateKey и
GenerateIV – это (с точки зрения VB.NET) не функции, а процедуры, т.
е. они не возвращают значения, а задают свойства Key
и IV в объекте SymmetricAlgorithm. Именно поэтому в листинге 2.2 сначала
вызываются функции генерации, а потом значения берутся из свойств Key и IV.
Запустив приложение на выполнение, вы как пользователь
никаких существенных изменений в шифровании не заметите, но как разработчик вы
будете знать, что принцип шифрования коренным образом поменялся.
Если в криптометоде применяются ключ и IV,
то их следует сохранить и при том в секрете от посторонних лиц, поскольку эти
данные будут использованы при дешифровании.
Комбинирование криптометодов
В предыдущих примерах мы работали лишь с методом DES, но кроме него существует также ряд других методов. В
этом разделе будет рассмотрен процесс создания Windows-приложение,
реализующего одновременно все симметричные алгоритмы, которые поддерживаются платформой
.NET. Плюс к этому, мы добавим дешифрование, но взамен
отключим поддержку ключей и IV, поскольку иначе их
придётся запоминать для каждого файла в отдельности, а хранить их в открытом
виде на диске ненадёжно.
Благодаря тому, что все симметричные алгоритмы в среде .NET Framework происходят от класса SymmetricAlgorithm,
их одновременная поддержка значительно упрощается.
Давайте теперь всё рассмотрим на конкретном примере. Для
этого создайте новое приложение Windows и создайте форму на подобие рис. 5 (чтобы не тратить время
на создание формы, можно воспользоваться кодом из конструктора листинга 3.1).
Рис. 5 – Примерный вид формы комбинированного
шифрования/дешифрования
- Листинг 3.1: Пример комбинированного шифрования и дешифрования
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text
Public Class Form1
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
‘ Эта часть кода доступна на CD
#End Region
Dim IV() As Byte = {172, 44,
172, 193, 7, 48, 131, 165}
Dim Key() As Byte = {93, 252,
76, 113, 209, 29, 253, 168}
Dim TripleKey() As Byte = {77, 79,
156, 172, 12, 40, 96, 226, _
93, 78, 90, 103,
186, 78, 117, 0, 85, 127, 114, 91, 148, 210, 242, 255}
Dim TripleIV() As Byte = {19, 127,
43, 85, 21, 117, 80, 151}
Dim aesKey() As Byte = {120, 6, 86, 102, 66, 236, 129, 91, 164, _
164, 192, 68, 70,
34, 40, 254, 107, 174, 201, 46, 168, 19, 125, _
202, 188, 52, 75,
23, 108, 94, 114, 27}
Dim aesIV() As Byte = {196,
161, 224, 67, 23, 143, 39, 43, 188, 91, _
247, 125, 97, 95,
246, 26}
Private Sub tlb_ButtonClick(ByVal sender As System.Object, ByVal e As
System.Windows.Forms.ToolBarButtonClickEventArgs) Handles tlb.ButtonClick
Dim fs As FileStream
Dim sr As StreamReader, sw As StreamWriter
Select Case tlb.Buttons.IndexOf(e.Button)
Case 1
Text = ""
txt1.Text =
""
txt2.Text =
""
Case 2
If dlgO.ShowDialog = DialogResult.OK Then
fs = New FileStream(dlgO.FileName, FileMode.Open)
sr = New StreamReader(fs, Encoding.Default)
txt1.Text = sr.ReadToEnd
txt2.Text = ""
Text =
dlgO.FileName
sr.Close()
fs.Close()
End If
Case 3
If dlgS.ShowDialog = DialogResult.OK Then
fs = New FileStream(dlgS.FileName, FileMode.Create)
sw = New StreamWriter(fs)
sw.Write(txt2.Text)
Text =
dlgS.FileName
sw.Close()
fs.Close()
End If
End Select
End Sub
Private Sub cmdEncrypt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdEncrypt.Click
Dim Alg As SymmetricAlgorithm = DefineAlg()
Dim Cryptor As ICryptoTransform = CreateEnc(Alg)
Dim fs As New
FileStream(GetFileName, FileMode.Create)
Dim CrStream As New
CryptoStream(fs, Cryptor, CryptoStreamMode.Write)
Dim b() As Byte =
ToBytes(txt1.Text)
CrStream.Write(b,
0, b.Length)
CrStream.Close()
fs.Close()
fs = Nothing
fs = New FileStream(GetFileName, FileMode.Open)
Dim sr As New
StreamReader(fs, Encoding.Default)
txt2.Text = sr.ReadToEnd
sr.Close()
fs.Close()
End Sub
Private Sub cmdDecrypt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles
cmdDecrypt.Click
Dim Alg As SymmetricAlgorithm = DefineAlg()
Dim Cryptor As ICryptoTransform = CreateDec(Alg)
Dim fs As New
FileStream(GetFileName, FileMode.Open)
Dim CrStream As New
CryptoStream(fs, Cryptor, CryptoStreamMode.Read)
Dim sr As New
BinaryReader(CrStream)
Dim f As New FileInfo(GetFileName)
Dim enc As New
UnicodeEncoding
txt2.Text =
enc.GetString(sr.ReadBytes(f.Length))
sr.Close()
fs.Close()
End Sub
#Region "Service functions"
Private Function DefineAlg() As SymmetricAlgorithm
If optDES.Checked = True Then
Return New DESCryptoServiceProvider
ElseIf optTripleDES.Checked = True Then
Return New TripleDESCryptoServiceProvider
ElseIf optAES.Checked = True Then
Return New RijndaelManaged
ElseIf optRC2.Checked = True Then
Return New RC2CryptoServiceProvider
End If
End Function
Private Function GetFileName() As String
If Text = "" Then
Repeat:
If dlgS.ShowDialog = DialogResult.OK Then
Text =
dlgS.FileName
Return dlgS.FileName
Else
MsgBox("You must create or select a file!", MsgBoxStyle.Exclamation)
GoTo Repeat
End If
Else
Return Text
End If
End Function
Private Function ToBytes(ByVal s As String) As Byte()
Dim enc As New
UnicodeEncoding
Return enc.GetBytes(s)
End Function
Private Function CreateEnc(ByVal AlgType As SymmetricAlgorithm)
As ICryptoTransform
If (TypeOf AlgType Is
DESCryptoServiceProvider) Or _
(TypeOf AlgType Is RC2CryptoServiceProvider) Then
Return AlgType.CreateEncryptor(Key, IV)
ElseIf TypeOf AlgType Is TripleDESCryptoServiceProvider
Then
Return AlgType.CreateEncryptor(TripleKey, TripleIV)
ElseIf TypeOf AlgType Is
RijndaelManaged Then
Return AlgType.CreateEncryptor(aesKey, aesIV)
End If
End Function
Private Function CreateDec(ByVal AlgType As
SymmetricAlgorithm) As ICryptoTransform
If (TypeOf AlgType Is
DESCryptoServiceProvider) Or _
(TypeOf AlgType Is RC2CryptoServiceProvider) Then
Return AlgType.CreateDecryptor(Key, IV)
ElseIf TypeOf AlgType Is
TripleDESCryptoServiceProvider Then
Return AlgType.CreateDecryptor(TripleKey, TripleIV)
ElseIf TypeOf AlgType Is
RijndaelManaged Then
Return AlgType.CreateDecryptor(aesKey, aesIV)
End If
End Function
#End Region
End Class
Итак, получился довольно громоздкий код, но давайте
попробуем разобраться в нём и уловить наиболее важные моменты. Для этого
просмотрим его сверху вниз. В самом начале объявлены 3 пары из ключей и IV. Раньше не было необходимости это делать, поскольку мы только
шифровали данные, но не дешифровали. Это вызвано тем, что даже, если мы сами не
прописываем провайдеру криптоалгоритма, что нужно использовать такой-то ключ и
такой-то IV, то он генерирует их самостоятельно как при
шифровании, так и при дешифровании. В результате ключи и IV,
сгенерированные для шифрования и дешифрования, не совпадают, т. е. расшифровать
данные провайдер не сможет, и вы увидите окно, подобное рис. 6.
Рис. 6 – Исключение, возникающее при несовпадении ключей
или IV’ов шифрования и дешифрования
Ключей и IV объявлено
три пары. Из комментариев видно, что первая пара объявлена для DES и RC2,
вторая – для TripleDES и третья – для AES/Rijndael. Зачем же это было сделано? Обратите внимание на
длину этих массивов – она везде разная. Дело в том, что DES и RC2 используют 8 байтовые ключи и IV, TripleDES работает
с 24 байтовым ключом и 8 байтовым IV, а Rijndael – с 32 байтовым ключом и 16
байтовым IV. Из этих данных уже можно делать смелый
вывод, что наиболее надёжный метод – AES.
Т. к. для выполнения криптоопераций в этом примере
используются файловые потоки, то для удобства обращения к этим файлам была
добавлена панель инструментов, с помощью которой можно создавать, открывать и
сохранять файлы. За реализацию этих возможностей отвечает следующая процедура:
Private Sub tlb_ButtonClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.ToolBarButtonClickEventArgs) Handles tlb.ButtonClick
Мы не будем останавливаться на этом
месте, поскольку к шифрованию оно никакого отношения не имеет, а “спустимся”
дальше по коду до процедуры шифрования:
Private Sub cmdEncrypt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdEncrypt.Click
Dim Alg As SymmetricAlgorithm = DefineAlg()
Dim Cryptor As ICryptoTransform = CreateEnc(Alg)
Dim fs As New
FileStream(GetFileName, FileMode.Create)
Dim CrStream As New
CryptoStream(fs, Cryptor, CryptoStreamMode.Write)
Dim b() As Byte =
ToBytes(txt1.Text)
CrStream.Write(b,
0, b.Length)
CrStream.Close()
fs.Close()
fs = Nothing
fs = New FileStream(GetFileName, FileMode.Open)
Dim sr As New
StreamReader(fs, Encoding.Default)
txt2.Text = sr.ReadToEnd
sr.Close()
fs.Close()
End Sub
Если обратиться к листингу 2.2, то там мы в первой строке
кода шифрования создавали экземпляр конкретного криптопровайдера, в частности –
DESCryptoServiceProvider. Здесь мы используем общий класс, от которого
наследуются все классы-провайдеры симметричных алгоритмов, -
SymmetricAlgorithm. Был применён именно этот класс, потому что данный пример
демонстрирует комбинированное шифрование/дешифрование, т. е. обеспечивает
поддержку всех доступных в .NET Framework симметричных криптометодов: DES,
TripleDES, AES/Rijndael, RC2.
Чтобы определить, какой именно криптометод используется для
шифрования, вызывается функция DefineAlg. Перейдём к этой функции:
Private Function DefineAlg() As SymmetricAlgorithm
If optDES.Checked
= True Then
Return New DESCryptoServiceProvider
ElseIf optTripleDES.Checked = True Then
Return New TripleDESCryptoServiceProvider
ElseIf optAES.Checked = True Then
Return New RijndaelManaged
ElseIf optRC2.Checked = True Then
Return New RC2CryptoServiceProvider
End If
End Function
Как видно из листинга, функция имеет тип SymmetricAlgorithm,
но в зависимости от выбранного переключателя она возвращает новый экземпляр
соответствующего объекта, т. е. если выбран был переключатель optDes, то будет
создан новый экземпляр класса DESCryptoServiceProvider.
Напомним ещё раз, что классы DESCryptoServiceProvider,
TripleDESCryptoServiceProvider , RijndaelManaged и RC2CryptoServiceProvider наследуются от класса SymmetricAlgorithm
, потому, не смотря на то, что функция имеет тип SymmetricAlgorithm,
она может с тем же успехом возвращать экземпляр любого из наследуемых классов,
что и было испльзовано в функции DefineAlg.
Определив алгоритм шифрования, приложение создаёт интерфейс
трансформации, значение которого задаётся специально созданной для этого
функцией CreateEnc. В качестве аргумента функция
получает переменную Alg, она содержит информацию
о выбранном криптометоде.
В листинге 2.2 для создания интерфейса-шифратора мы применяли
следующую строку:
Dim DES_Encryptor As ICryptoTransform = DES.CreateEncryptor(DES.Key, DES.IV)
В листинге 3.1 функция CreateEnc
делает то же самое, но она была создана лишь из-за того, что в этом примере
используются несколько криптометодов, а длины ключей и IV у них разные, поэтому, чтобы определить, какую пару ключ-IV нужно использовать, и была создана
эта функция. Если обратиться к её коду, то можно увидеть, что она лишь
анализирует тип переданного класса и подставляет соответствующий ключ и IV.
Дальше шифрование происходит по тому же принципу, что и в листинге
2.2 с тем лишь отличием, что процесс получения массива байтов вынесен в
отдельную функцию ToBytes:
Private Function
ToBytes(ByVal s As String) As Byte()
Dim enc As New
UnicodeEncoding
Return enc.GetBytes(s)
End Function
При создании тестового приложения проследите за одной
особенностью: свойство Text формы должно быть равно
пустой строке (“”), т. к. оно используется для хранения имени файла, и его
обработка осуществляется в функции GetFileName.
Двигаемся дальше, в сторону дешифрования:
Private Sub
cmdDecrypt_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles cmdDecrypt.Click
Dim Alg As SymmetricAlgorithm = DefineAlg()
Dim Cryptor As ICryptoTransform = CreateDec(Alg)
Dim fs As New
FileStream(GetFileName, FileMode.Open)
Dim CrStream As New
CryptoStream(fs, Cryptor, CryptoStreamMode.Read)
Dim sr As New
BinaryReader(CrStream)
Dim f As New FileInfo(GetFileName)
Dim enc As New
UnicodeEncoding
txt2.Text =
enc.GetString(sr.ReadBytes(f.Length))
sr.Close()
fs.Close()
End Sub
Как видите, начало листинга (зона объявления переменных)
аналогично началу в процедуре шифрования. Только при инициализации интерфейса
трансформации используется не функция CreateEnc,
а CreateDec, которая работает по тому же
принципу, но только создаёт дешифратор вместо шифратора.
Далее создаётся экземпляр объекта FileInfo для определения длины считываемого файла. Необходимость
введения этого класса возникла из-за того, что для получения данных из файла
используется класс BinaryReader. Его метод ReadBytes требует указания
количество байтов, которые должны быть считаны. Этот метод затем используется
для получения дешифрованных данных из криптопотока.
Теперь самое время испытать созданное приложение (рис. 7).
Чтобы зашифровать данные, введите текст в поле Before
или загрузите их из файла, выберите метод шифрования и нажмите Encrypt. Если не был выбран какой-либо файл, то программа
попросит вас выбрать файл для сохранения результатов, после чего покажет
результат шифрования. Чтобы дешифровать только что зашифрованные данные можно
либо открыть зашифрованный файл, либо скопировать данные из Before в поле After –
одним словом, криптограмма должна оказаться в поле Before.
После этого просто нажмите кнопку Decrypt, и
результат предстанет перед вами.
Если нажать кнопку Encrypt несколько раз
подряд, то результат не изменится. Дело в том, что в этом примере ключ и IV не изменяются, поскольку они
жёстко закодированы. В предыдущих примерах мы либо их вообще не трогали, либо
вызывали методы генерации, поэтому результаты тогда были различными.
Рис. 7 – Приложение комбинированного шифрования в
действии
Асимметричные системы
Среда .NET Framework содержит два класса,
которые реализуют асимметричные алгоритмы: RSACryptoServiceProvider
и DSACryptoServiceProvider. Оба метода пригодны
для подписания и верификации данных, т. е. для создания цифровых подписей. Но
только RSA поддерживает
шифрование/дешифрование, поэтому в данном раздел мы остановимся только на нём,
а DSA будет рассмотрен в
разделе “Цифровые подписи”.
Подобно симметричным системам асимметричные имеют базовый класс,
от которого наследуются объекты реализующие конечные криптометоды – это AsymmetricAlgorithm.
Как уже было ранее сказано, асимметричные алгоритмы очень
медленные, и потому пригодны лишь для небольших объёмов информации. Таким
образом, алгоритм RSA может
обработать 43 байта.
Давайте теперь перейдём к практике и создадим Web-приложение, которое шифрует и дешифрует данные с помощью
криптометода RSA:
- Листинг 4.1: RSA/default.aspx:
<%@ Page
Language="vb" AutoEventWireup="false"
Codebehind="default.aspx.vb" Inherits="RSA.WebForm1"%>
<HTML>
<HEAD>
<title>RSAtitle>
HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<b>Clear text:b><br>
<textarea id="txt" runat="server">textarea><br>
<b>Encrypted text:b><br>
<textarea id="txtEnc" runat="server" rows=4>textarea><br>
<b>Decrypted text:b><br>
<textarea id="txtDec" runat="server">textarea><br>
<hr>
<asp:Button ID="btnEnc" Runat="server" Text="Encrypt and
Decrypt" />
form>
body>
HTML>
- Листинг 4.2: RSA/default.aspx.vb:
Imports System.Security.Cryptography
Imports System.Text
Public Class WebForm1
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Protected WithEvents txt As
System.Web.UI.HtmlControls.HtmlTextArea
Protected WithEvents txtEnc As
System.Web.UI.HtmlControls.HtmlTextArea
Protected WithEvents txtDec As
System.Web.UI.HtmlControls.HtmlTextArea
Protected WithEvents btnEnc As
System.Web.UI.WebControls.Button
Dim enc As New
UnicodeEncoding
Private Sub btnEnc_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnEnc.Click
Dim RSAcsp As New RSACryptoServiceProvider
txtEnc.Value =
RSACrypt(enc.GetBytes(txt.Value), _
RSAcsp.ExportParameters(False), KindOfAction.RSAEncrypt)
txtDec.Value =
RSACrypt(enc.GetBytes(txtEnc.Value), _
RSAcsp.ExportParameters(True), KindOfAction.RSADecrypt)
End Sub
Private Function RSACrypt(ByVal DataToEncrypt() As Byte, ByVal RSAKey As RSAParameters, ByVal Action As KindOfAction)
As String
Dim RSA As New
RSACryptoServiceProvider
RSA.ImportParameters(RSAKey)
If Action = KindOfAction.RSAEncrypt Then
Return enc.GetString(RSA.Encrypt(DataToEncrypt, False))
Else
Return enc.GetString(RSA.Decrypt(DataToEncrypt, False))
End If
End Function
Private Enum KindOfAction As Integer
RSAEncrypt = 0
RSADecrypt = 1
End Enum
End Class
С первого взгляда видно, что код реализации асимметричного
метода намного короче, чем код симметричного. Одной из причин столь малых
габаритов является то, что нет нужды инициализировать множество объектов,
связывая их друг с другом. В этом примере используется один единственный класс
– RSACryptoServiceProvider.
В процедуре обработки события нажатия кнопки btnEnc сначала создаётся
экземпляр класса RSACryptoServiceProvider, при этом
автоматически генерируется пара из открытого и секретного ключей. Далее для
шифрования и дешифрования используется функция RSACrypt.
Сравните вызовы этой функции для обоих операций:
txtEnc.Value = RSACrypt(enc.GetBytes(txt.Value), _
RSAcsp.ExportParameters(False), KindOfAction.RSAEncrypt)
txtDec.Value =
RSACrypt(enc.GetBytes(txtEnc.Value), _
RSAcsp.ExportParameters(True), KindOfAction.RSADecrypt)
При шифровании параметры экспортируются с атрибутом в
значении False, а при дешифровании – в значении True. Этот атрибут отвечает за информацию, которая должна
быть экспортирована. Таким образом, если стоит значение False,
то экспортируются данные только об открытом ключе, а если стоит True, то об открытом и о секретном.
Поскольку для шифрования используется открытый ключ, то и данные
можно экспортировать с атрибутом в значении False, а
для дешифрования соответственно атрибут нужно менять в противоположное
значение, чтобы была доступна информация о секретном ключе.
В функции RSACrypt создаётся
ещё один экземпляр объекта RSACryptoServiceProvider (переменная
RSA), но уже не для генерации ключей, а для
выполнения криптоопераций, используя ключи, переданные от класса-генератора (RSAcsp). Теперь можно сделать вывод, что для выполнения
криптоопераций с помощью объекта RSACryptoServiceProvider
необходимы 2 его экземпляра: один для генерации ключей, а другой для выполнения
криптоопераций.
Для шифрования и дешифрования вызываются методы Encrypt и Decrypt объекта RSACryptoServiceProvider. Синтаксисы этих методов идентичны,
они только выполняют противоположные действия. Остановимся на одном из них:
Public Function
Encrypt(ByVal rgb() As Byte, ByVal fOAEP As Boolean) As Byte()
Первый атрибут этого метода принимает массив байтов для
шифрования (в случае с методом Decrypt это был бы
массив байтов для дешифрования), а второй атрибут активизирует или
деактивизирует Дополнение Оптимального Асимметричного Шифрования (OAEP – Optimal Asymmetric Encryption Padding).
Этот механизм работает только на системах Windows XP и старше,
т. е. XP и более новые операционные системы могут
работать как со значением атрибута False, так и со
значением True, а все остальные поддерживают только False.
Объекты RSA и RSACryptoServiceProvider содержат
методы EncryptValue и DecryptValue,
но они не поддерживаются в .NET Framework 1.1
Теперь запустите созданное приложение и попробуйте
что-нибудь зашифровать. В случае успеха вы увидите результат, подобный рис. 8.
Рис. 8 – Успешное шифрование асимметричным методом RSA
Вспомните примечание в начале этого раздела: “RSA способен обработать до 43 байтов”.
Для того, чтобы это проверить, введите текст длиною более 43 символов и нажмите
Encrypt and Decrypt.
Должно появиться исключение, как на рис. 9:
Рис. 9 – Метод RSA способен обработать до 43 байтов
Хранилище ключей метода RSA
Информация о ключах алгоритма RSA хранится в структуре RSAParameters.
Эта структура содержит в себе несколько параметров, имена которых никоим
образом не связаны с открытым и секретным ключом. Эту структуру можно увидеть
на рис. 10.
Рис. 10 – Структура RSAParameters
Определить, какие переменные к чему относятся довольно
легко. Для этого создайте консольное приложение и вставьте код из листинга 5.1.
- Листинг 5.1: Получение информации о ключах
Imports System.Security.Cryptography
Imports System.Text
Module Module1
Sub Main()
Dim RSA As New RSACryptoServiceProvider
Dim RSAparams As RSAParameters = RSA.ExportParameters(False)
Console.WriteLine("Public key for encryption:")
ShowKeyInfo(RSAparams)
RSAparams =
RSA.ExportParameters(True)
Console.WriteLine(vbCrLf)
Console.WriteLine("Public and Private keys for
decryption:")
ShowKeyInfo(RSAparams)
Console.ReadLine()
End Sub
Sub ShowKeyInfo(ByVal KeyInfo As RSAParameters)
Dim enc As New
UnicodeEncoding
On Error Resume Next
Console.WriteLine("D - {0}", enc.GetString(KeyInfo.D))
Console.WriteLine("DP - {0}", enc.GetString(KeyInfo.DP))
Console.WriteLine("DQ - {0}", enc.GetString(KeyInfo.DQ))
Console.WriteLine("Exponent - {0}", enc.GetString(KeyInfo.Exponent))
Console.WriteLine("InverseQ - {0}", enc.GetString(KeyInfo.InverseQ))
Console.WriteLine("Modulus - {0}", enc.GetString(KeyInfo.Modulus))
Console.WriteLine("P - {0}", enc.GetString(KeyInfo.P))
Console.WriteLine("Q - {0}", enc.GetString(KeyInfo.Q))
End Sub
End Module
В листинге 5.1 сначала генерируются ключи путём создания
нового экземпляра объекта RSACryptoServiceProvider,
затем данные о ключах экспортируются в два подхода: в первый раз отправляется
информация только об открытом ключе, а во второй раз об обоих ключах. Далее
процедура ShowKeyInfo просто выводит всю
доступную информацию.
Один только код пока мало о чём говорит, поэтому запустим приложение
и посмотрим на результат. Из рис. 11 видно, что данные об открытом ключе
содержатся в переменных Exponent и Modulus. Все остальные данные, соответственно,
характеризуют секретный ключ.
Возникает вопрос: “Как можно передать ключи пользователю,
если они записаны в 8 переменных?” Для решения этой задачи в объекте RSA, от которого наследуется RSACryptoServiceProvider,
есть два метода: FromXmlString и
ToXmlString. Их синтаксисы приведены ниже:
Public Overrides Sub FromXmlString(ByVal xmlString As String)
Public Overrides Function ToXmlString(ByVal includePrivateParameters As Boolean) As String
Метод FromXmlString импортирует
данные о ключах в объект RSA из строки, а функция ToXmlString, наоборот, генерирует строку с данными о ключах,
причём можно указать, нужно ли экспортировать информацию о секретном ключе.
Рис. 11 – Информация о ключах
Хеширование
Напомним ещё раз, что хеш – это числовое значение
фиксированной длины, которое является уникальным представлением исходных
данных. Среда .NET Framework поддерживает несколько
методов хеширования: MD5, SHA1,
SHA256, SHA384, SHA512.
Все классы конечных хеш-алгоритмов происходят от объекта HashAlgorithm, потому синтаксис и порядок действий для
выполнения операций хеширования будет одинаков для всех хеш-методов. Все
хеш-методы, реализованные в .NET Framework, работают либо с массивами байтов, либо с потоками.
Поскольку основное назначение хешей – это валидация данных,
то создадим Web-приложение, которое хеширует данные и
сравнивает получившиеся хеши. В качестве алгоритма возьмём SHA384
и воспользуемся кодом из листингов 6.1 и 6.2:
- Листинг 6.1: SHA384/default.aspx
<%@ Page
language="vb" Codebehind="default.aspx.vb"
AutoEventWireup="false" Inherits="SHA384.WebForm1" %>
<html>
<head>
<title>SHA384title>
head>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<table border>
<tr bgcolor=lightGrey>
<th>Original textth>
<th>Hash1 (<i>Hello World!i>)th>
<th>Hash2 (new hash)th>
tr>
<tr>
<td><textarea id="txt" runat=server>Hello World!textarea>td>
<td><asp:Label ID="lblOriginalHash" Runat=server/>td>
<td><asp:Label ID="lblNewHash" Runat=server/>td>
tr>
<tr bgcolor=lightGrey>
<th align=left colspan=3>
Result: <asp:Label ID="lblResult" Runat=server/>
th>
tr>
table>
<asp:Button ID="btn" Runat=server Text="New hash"/>
form>
body>
html>
- Листинг 6.2: SHA384/default.aspx.vb
Imports System.Security.Cryptography
Imports System.Text
Imports System.Drawing
Public Class WebForm1
Inherits System.Web.UI.Page
Protected WithEvents txt As
System.Web.UI.HtmlControls.HtmlTextArea
Protected WithEvents lblOriginalHash As System.Web.UI.WebControls.Label
Protected WithEvents lblNewHash As
System.Web.UI.WebControls.Label
Protected WithEvents lblResult As
System.Web.UI.WebControls.Label
Protected WithEvents btn As
System.Web.UI.WebControls.Button
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Dim SHA384 As New
SHA384Managed
Dim OriginalHash() As Byte
Dim enc As New UnicodeEncoding
Private Sub btn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles
btn.Click
Dim i As Integer
Dim NewHash() As Byte
Dim same As Boolean = True
NewHash =
SHA384.ComputeHash(enc.GetBytes(txt.Value))
lblNewHash.Text =
enc.GetString(NewHash)
For i = 0 To NewHash.Length - 1
If NewHash(i) <> OriginalHash(i) Then
same = False
Exit For
End If
Next
If same = True Then
lblResult.ForeColor
= Color.Green
lblResult.Text
= "Hash1 = Hash2"
Else
lblResult.ForeColor = Color.Red
lblResult.Text
= "Hash1 <>
Hash2"
End If
End Sub
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Const ORIGINAL_STRING As String = "Hello World!"
OriginalHash =
SHA384.ComputeHash(enc.GetBytes(ORIGINAL_STRING))
lblOriginalHash.Text = enc.GetString(OriginalHash)
End Sub
End Class
Код листинга 6.2 особой сложности не представляет. В нём
создаются два хеша: первый по неизменяемой строке “Hello World!”, а второй по
значению в поле txt. После этого хеши сверяются, если в
поле txt была введена строка “Hello World!”, то хеши совпадут
(рис.12), в противном случае – нет (рис. 13).
Рис. 12 – Хеши совпали, значит данные не были изменены
Рис. 13 – Хеши не совпали, значит данные были изменены.
Чтобы воспользоваться другим методом, достаточно поменять
провайдер алгоритма, т. е. вместо строки:
Dim SHA384 As New SHA384Managed
Можно использовать, скажем:
Dim SHA384 As New MD5CryptoServiceProvider
Или любую другую.
Цифровые подписи
Вспомним ещё раз, что цифровые подписи позволяют убедиться в
достоверности отправителя. Для этого применяется совокупность из ассиметричных
криптосистем и хеш-алгоритмов. В среде .NET Framework все
основные действия выполняются по средствам классов ассиметричных алгоритмов.
Таковых в .NET Framework 1.1 два: RSACryptoServiceProvider и DSACryptoServiceProvider. Поскольку мы уже рассматривали
примеры с алгоритмом RSA, то в примерах этого раздела
будет в основном использоваться DSA. Структуры их
классов почти одинаковы, поэтому не составит никакого труда перейти к другому
алгоритму.
В .NET Framework есть несколько способов
создания и проверки цифровых подписей, поэтому мы постараемся рассмотреть
большинство из них.
Так же как и для всех предыдущих криптографических
примитивов в инфраструктуре .NET Framework предусмотрен базовый класс
для цифровых подписей – SignatureDescription. С
помощью методов этого класса можно перейти к объектам, выполняющим
непосредственное создание и проверку цифровых подписей. Мы не станем
останавливаться на этом объекте, а перейдём сразу к реализации алгоритма DSA – к классу DSACryptoServiceProvider.
Объект DSACryptoServiceProvider
содержит 3 пары методов для создания и проверки подписей: SignData
и VerifyData, SignHash
и VerifyHash, CreateSignature и VerifySignature.
SignData и VerifyData
Самый простой и быстрый способ создания и проверки цифровых
подписей – использование методов SignData и VerifyData. Метод SignData перегружен, т. е. имеет несколько прототипов:
Public Function SignData(ByVal buffer()
As Byte) As Byte()
Public Function
SignData(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) As Byte()
Public Function
SignData(ByVal inputStream As
System.IO.Stream) As Byte()
Первый прототип принимает в качестве входных данных массив
байт исходного текста, для которого нужно создать цифровую подпись. Второй тоже
принимает массив байт, но плюс к этому есть возможность указать смещение и
количество байт, которые нужно считать. И, наконец, третий прототип принимает
поток данных.
Мы воспользуемся первым прототипом и создадим новое Web-приложение (листинг 7.1):
- Листинг 7.1: Sign1/default.aspx.vb
Imports System.Security.Cryptography
Imports System.Text
Public Class WebForm1
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Const CLEAR_STRING As String = "Hello, World!"
Dim enc As New
UnicodeEncoding
Dim clearText() As Byte, signText()
As Byte
Dim DSA As New
DSACryptoServiceProvider
clearText =
enc.GetBytes(CLEAR_STRING)
signText =
DSA.SignData(clearText)
Response.Write("Clear text: " & CLEAR_STRING & "
")
Response.Write("Signature: " & enc.GetString(signText) & "
")
If DSA.VerifyData(clearText, signText) Then
Response.Write("Good")
Else
Response.Write("Bad")
End If
End Sub
End Class
Код листинга 7.1 сначала преобразует строку в массив байт с
помощью объекта UnicodeEncoding, после чего
полученный массив используется для подписывания данных. Исходные данные и
подпись выводятся на экран, и после этого происходит проверка сигнатуры путём
вызова метода VerifyData, которому в качестве
параметров передаются массивы байт исходного текста и цифровой подписи.
В данном примере мы осуществляем подписывание и проверку с
помощью одного и того же экземпляра объекта DSACryptoServiceProvider,
т. е. нам нет необходимости заботиться о ключах – они нигде не меняются. Но в
рабочих ситуациях создание сигнатуры и её проверка происходят с применением
разных объектов, разными приложениями и, более того, зачастую на разных машинах.
Речь идёт о том, что в рабочих приложениях необходимо сохранять информацию о
ключах с помощью пары методов ToXmlString и FromXmlString объекта DSACryptoServiceProvider.
Запустите это приложение, и, если всё было выполнено
правильно, то вы увидите окно, подобное рис. 14:
Рис. 14 – Цифровая подпись подтверждена
Как видно из рис. 14, данные пришли от верного отправителя,
т. е. проверка подписи прошла успешно. Чтобы увидеть другую сторону медали,
достаточно добавить одну строчку в код (см. листинг 7.2):
- Листинг 7.2: Sign1/default.aspx.vb
Imports System.Security.Cryptography
Imports System.Text
Public Class WebForm1
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Const CLEAR_STRING As String = "Hello, World!"
Dim enc As New
UnicodeEncoding
Dim clearText() As Byte, signText()
As Byte
Dim DSA As New
DSACryptoServiceProvider
clearText =
enc.GetBytes(CLEAR_STRING)
signText =
DSA.SignData(clearText)
Response.Write("Clear text: " & CLEAR_STRING & "
")
Response.Write("Signature: " & enc.GetString(signText) & "
")
signText(10) =
144
If DSA.VerifyData(clearText, signText) Then
Response.Write("Good")
Else
Response.Write("Bad")
End If
End Sub
End Class
Новая строка выделена жирным шрифтом:
signText(10) = 144
Она лишь меняет один из байтов цифровой подписи. Теперь
запустите приложение, и искажение сигнатуры сразу даст о себе знать (рис. 15):
Рис. 15 – Цифровая подпись была изменена
Применение пары методов SignData и VerifyData удобно тем,
что вам не нужно заботиться о создании хеша – система делает всё за вас. Но при
этом теряется некоторая гибкость, в частности вы не можете поменять метод
хеширования, а по умолчанию выбран SHA1.
Стоит отметить, что потеря гибкости при использовании методов SignData и VerifyData
присуща только объекту DSACryptoServiceProvider
– в классе RSACryptoServiceProvider
те же самые методы имеют два параметр, и второй из них – это
название хеш-метода. Это вызвано тем, что метод RSA поддерживает два хеш-алгоритма: SHA1
и MD5. А криптометод DSA
работает только с SHA1, потому и нет смысла добавлять
второй атрибут, но, не смотря на это у метода SignHash
(он будет рассмотрен далее) даже в объекте DSACryptoServiceProvider
есть атрибут для указания имени хеш-метода.
Из-за того, что метод RSA работает
только с хешами SHA1 и MD5, а DSA - лишь с SHA1, то даже не
пытайтесь применять другие хеш-алгоритмы, например, SHA256
или SHA384, потому что вы, в конечном счёте, получите
ошибку, и ничего работать не будет.
Чтобы узнать, какой именно метод алгоритма цифровой подписи
установлен, нужно просмотреть значение свойства SignatureAlgorithm объекта DSACryptoServiceProvider.
Листинг 8.1 демонстрирует этот процесс:
- Листинг 8.1: Sign1/DefAlg.aspx
<%@ Page
Language="vb" AutoEventWireup="false" Inherits="Sign1.DefAlg"%>
<%@ Import
Namespace="System.Security.Cryptography"%>
<html>
<head>
<title>DefAlgtitle>
head>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<%
Dim DSA As New DSACryptoServiceProvider
REsponse.Write(DSA.SignatureAlgorithm)
%>
form>
body>
html>
Открыв эту страницу, вы увидите окно, подобное рис. 16:
Рис. 16 – Информация об алгоритме цифровой подписи
Из рис. 16 видно, что асимметричным алгоритмом является DSA, а хеш-алгоритмом – SHA1.
SignHash и VerifyHash
Метод SignHash отличается от
метода SignData лишь тем, что он работает не с
чистыми данными, а с их хешами, поэтому в качестве параметров ему задаются
входной хеш и имя хеш-метода:
Public Function
SignHash(ByVal rgbHash() As Byte, ByVal str As String) As Byte()
Этот метод имеет свои преимущества над методом SignData, в частности вы вправе сами выбирать алгоритм
хеширования. Код листингов 9.1 и 9.2 демонстрирует применение метода SignHash для создания цифровых подписей (в качестве хеш-метода
выбран SHA1):
- Листинг 9.1: Sign2/default.aspx
<%@ Page
Language="vb" AutoEventWireup="false"
Codebehind="default.aspx.vb" Inherits="Sign2.WebForm1"%>
<HTML>
<HEAD>
<title>Sign2title>
HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<textarea id="txt" runat=server>textarea><br>
<asp:Button ID="btn" Runat=server Text="Sign"/>
<hr>
<asp:Label ID="lbl" Runat=server/>
form>
body>
HTML>
- Листинг 9.2: Sign2/default.aspx.vb
Imports System.Security.Cryptography
Imports System.Text
Public Class WebForm1
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Protected WithEvents txt As
System.Web.UI.HtmlControls.HtmlTextArea
Protected WithEvents btn As
System.Web.UI.WebControls.Button
Protected WithEvents lbl As
System.Web.UI.WebControls.Label
Private Sub btn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles
btn.Click
Dim SHA1 As New
SHA1CryptoServiceProvider
Dim DSA As New
DSACryptoServiceProvider
Dim hashText() As Byte, signText()
As Byte
Dim enc As New
UnicodeEncoding
hashText =
SHA1.ComputeHash(enc.GetBytes(txt.Value))
signText =
DSA.SignHash(hashText, CryptoConfig.MapNameToOID("SHA1"))
lbl.Text = "Signature: " & enc.GetString(signText) & "
"
If DSA.VerifyHash(hashText, CryptoConfig.MapNameToOID("SHA1"), signText) Then
lbl.Text &=
"Good"
Else
lbl.Text &=
"Bad"
End If
End Sub
End Class
Код листинга 9.2 вначале создаёт экземпляры
объектов MD5CryptoServiceProvider и SHA1CryptoServiceProvider. Далее создаётся хеш с помощью метода ComputeHash.
После этого следует создание цифровой подписи по средствам метода SignHash. Этот метод подписывает не исходный чистый
текст, а его хеш. Обратите внимание, что во втором параметре подставляется не
просто строковое имя хеш-алгоритма, а используется метод MapNameToOID объекта CryptoConfig
(этот объект расположен всё в том же пространстве имён System.Security.Cryptography). Этот
метод возвращает код соответствующего алгоритма. Чтобы иметь представление, на
что это похоже, можно воспользоваться кодом из листинга 10.1:
- Листинг 10.1: Sign2/GetAlgName.aspx
<%@ Page
Language="vb" AutoEventWireup="false"
Inherits="Sign2.GetAlgName"%>
<%@Import
Namespace="System.Security.Cryptography"%>
<html>
<head>
<title>GetAlgNametitle>
head>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<%=
CryptoConfig.MapNameToOID("SHA1")%>
form>
body>
html>
Но вернёмся к нашему основному приложению. Запустите его,
введите в поле какой-нибудь текст, нажмите кнопку Sign,
и вы получите ожидаемый результат.
CreateSignature и VerifySignature
Эти два метода есть только в объекте DSACryptoServiceProvider.
Они, в сущности, действуют так же, как и методы SignHash и VerifyHash: они
принимают хеш в качестве входных параметров, но имя хеш-метода больше указывать
не нужно. То есть у метода CreateSignature всего
один атрибут – входные данные в виде хеша.
Создадим новое Web-приложение,
демонстрирующее работу этих методов:
- Листинг 11.1: Sign3/default.aspx.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Security.Cryptography;
using System.Text;
namespace Sign3
{
public class WebForm1 : System.Web.UI.Page
{
private void Page_Load(object sender,
System.EventArgs e)
{
const string CLEAR_TEXT = "CreateSignature and VerifySignature";
SHA1CryptoServiceProvider
SHA1 = new SHA1CryptoServiceProvider();
DSACryptoServiceProvider
DSA = new DSACryptoServiceProvider();
byte[] hashText, signText;
UnicodeEncoding
enc = new UnicodeEncoding();
hashText =
SHA1.ComputeHash(enc.GetBytes(CLEAR_TEXT));
signText =
DSA.CreateSignature(hashText);
Response.Write("Clear text: " + CLEAR_TEXT + "
");
Response.Write("Signature: " + enc.GetString(signText) + "
");
if(DSA.VerifySignature(hashText, signText) == true)
Response.Write("Good");
else
Response.Write("Bad");
}
#region Web Form Designer generated code
override protected void
OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
}
Думаю, нет смысла разбирать этот код, потому что он очень
похож на код листинга 9.2. Запустите приложение, и вы увидите уже привычное
слово “Good”.
Объекты DSASignatureFormatter и DSASignatureDeformatter
Ещё один способ создать цифровую подпись – это применить
объекты DSASignatureFormatter и
DSASignatureDeformatter.
Для алгоритма RSA аналогами являются RSAPKCS1SignatureFormatter и RSAPKCS1SignatureDeformatter.
В следующем примере, демонстрирующем применение этих
объектов, мы воспользуемся одновременно средствами алгоритмов DSA и RSA. Но хеши распределим таким
образом, что DSA будет работать
с SHA1 (потому что другого он и не поддерживает), а RSA – с MD5. Листинг
12.1 описывает всё происходящее:
- Листинг 12.1: Sign4/default.aspx.cs
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Security.Cryptography;
using System.Text;
namespace Sign4
{
public class WebForm1 : System.Web.UI.Page
{
private void Page_Load(object sender,
System.EventArgs e)
{
const string CLEAR_TEXT = "Объекты DSASignatureFormatter, " +
"DSASignatureDeformatter,
RSAPKCS1SignatureFormatter, " +
"RSAPKCS1SignatureDeformatter";
SHA1CryptoServiceProvider
SHA1 = new SHA1CryptoServiceProvider();
MD5CryptoServiceProvider
MD5 = new MD5CryptoServiceProvider();
DSACryptoServiceProvider
DSA = new DSACryptoServiceProvider();
DSASignatureFormatter
DSAform = new DSASignatureFormatter(DSA);
DSASignatureDeformatter
DSAdeform = new DSASignatureDeformatter(DSA);
RSACryptoServiceProvider
RSA = new RSACryptoServiceProvider();
RSAPKCS1SignatureFormatter
RSAform = new RSAPKCS1SignatureFormatter(RSA);
RSAPKCS1SignatureDeformatter
RSAdeform = new
RSAPKCS1SignatureDeformatter(RSA);
byte[] DSAhashText, DSAsignText, RSAhashText, RSAsignText;
UnicodeEncoding
enc = new UnicodeEncoding();
DSAhashText
= SHA1.ComputeHash(enc.GetBytes(CLEAR_TEXT));
RSAhashText
= MD5.ComputeHash(enc.GetBytes(CLEAR_TEXT));
DSAform.SetHashAlgorithm("SHA1");
DSAdeform.SetHashAlgorithm("SHA1");
RSAform.SetHashAlgorithm("MD5");
RSAdeform.SetHashAlgorithm("MD5");
DSAsignText
= DSAform.CreateSignature(DSAhashText);
RSAsignText
= RSAform.CreateSignature(RSAhashText);
Response.Write("Clear text: " + CLEAR_TEXT + "
");
if(DSAdeform.VerifySignature(DSAhashText, DSAsignText) == true)
Response.Write("DSA signature: " +
enc.GetString(DSAsignText)
+
" " +
"Good
");
else
Response.Write("DSA signature: " +
enc.GetString(DSAsignText)
+
" " +
"Bad
");
if(RSAdeform.VerifySignature(RSAhashText, RSAsignText) == true)
Response.Write("RSA signature: " +
enc.GetString(RSAsignText)
+
" " +
"Good");
else
Response.Write("RSA signature: " +
enc.GetString(RSAsignText)
+
" " +
"Bad");
}
#region Web Form Designer generated code
override protected void
OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
}
На первый взгляд, код получился довольно громоздкий, но это
лишь из-за того, что приходилось выполнять одно и то же для 4-х объектов.
Давайте попробуем определить ключевые моменты, и, чтобы не создавалась
путаница, рассмотрим все ситуации на примерах объектов алгоритма DSA.
Сначала был создан экземпляр объекта DSACryptoServiceProvider, при этом сгенерировалась пара ключей. После
этого были созданы экземпляры объектов DSASignatureFormatter
и DSASignatureDeformatter. В качестве атрибута конструкторов передавался объект DSACryptoServiceProvider, содержащий информацию о ключах:
DSASignatureFormatter
DSAform = new DSASignatureFormatter(DSA);
DSASignatureDeformatter
DSAdeform = new DSASignatureDeformatter(DSA);
После этого все операции осуществлялись с переменными DSAform и DSAdeform. В
частности, вызывался метод SetHashAlgorithm,
указывающий необходимый хеш-алгоритм, путём простой передачи его имени без
применения класса CryptoConfig, как это делалось
в листинге 9.2. Как только все параметры были установлены, события начали
развиваться по уже известному сценарию.
Теперь запустите приложение и сравните, как создаёт цифровую
подпись совокупность алгоритмов DSA-SHA1
и RSA-MD5 (см. рис. 17).
Рис. 17 – Цифровые подписи, созданные различными
алгоритмами
Исполним обещанное…
В первой части статьи было обещано рассмотреть хранение
имени пользователя и пароля в файле Web.config в зашифрованном виде. Напомним,
что в файле Web.config, в
разделе
можно хранить информацию для аутентификации, при этом сам процесс
аутентификации значительно упрощается. Но хранить данные в открытом виде
довольно опасно, поэтому среда .NET Framework позволяет использовать хеш-алгоритмы SHA1 и MD5 для сокрытия информации.
С точки зрения криптографии, необходимо создать приложение,
которое только хеширует данные соответствующим образом. Но сразу отметим, что
применение стандартных классов хеширования пространства имён System.Security.Cryptography не подойдёт. Для этого существует
специальный метод HashPasswordForStoringInConfigFile объекта System.Web.Security.FormsAuthentication.
Необходимо использовать именно этот метод, а не какой-нибудь другой только потому,
что он хеширует и представляет данные в нужном формате. Рассмотрим листинги
13.1 и 13.2, чтобы понять, о чём идёт речь:
- Листинг 13.1: HashPassword/default.aspx
<%@ Page
Language="vb" AutoEventWireup="false"
Codebehind="default.aspx.vb" Inherits="HashPassword.WebForm1"%>
<html>
<head>
<title>HashPasswordtitle>
head>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<table border>
<tr><th bgcolor=lightGrey>Hash algorithmth>tr>
<tr><td>
<asp:RadioButtonList ID="optList" Runat=server>
<asp:ListItem Value="SHA1" Selected=True/>
<asp:ListItem Value="MD5"/>
asp:RadioButtonList>
td>tr>
table>
<b>Clear text: b>
<textarea id="txt" runat=server>textarea><br>
<b>Hash: b>
<asp:Label ID="lbl" Runat=server/>
<hr>
<asp:Button ID="btn" Runat=server Text="Hash"/>
form>
body>
html>
- Листинг 13.2: HashPassword/default.aspx.vb
Imports System.Web.Security
Public Class WebForm1
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Protected WithEvents optList As
System.Web.UI.WebControls.RadioButtonList
Protected WithEvents txt As
System.Web.UI.HtmlControls.HtmlTextArea
Protected WithEvents lbl As
System.Web.UI.WebControls.Label
Protected WithEvents btn As
System.Web.UI.WebControls.Button
Private Sub btn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles
btn.Click
lbl.Text =
FormsAuthentication.HashPasswordForStoringInConfigFile _
(txt.Value,
DefAlg())
End Sub
Private Function DefAlg() As String
If optList.Items(0).Selected = True Then
Return "SHA1"
Else
Return "MD5"
End If
End Function
End Class
Как видите, код листинга 13.2 очень простой. В нём функция DefAlg возвращает имя алгоритма,
в зависимости от положения переключателей, а процедура обработки нажатия кнопки
вызывает заветный метод HashPasswordForStoringInConfigFile,
который выполняет все необходимые без вмешательства разработчика.
Запустите приложение и поэкспериментируйте с ним. Результат
должен быть подобен рис. 18:
Рис. 18 – Хеширование паролей для файла конфигурации
Теперь разработаем приложение, использующее эти
криптограммы. Для этого создайте новый Web-проект и
измените файл Web.config
следующим образом:
xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<authentication mode="Forms">
<forms loginUrl="login.aspx" protection="All">
<credentials passwordFormat="MD5">
--password="one"-->
<user name="John" password="F97C5D29941BFB1B2FDAB0874906AB82"/>
--password="two"-->
<user name="Mike" password="B8A9F715DBB64FD5C56E7783C6820A61"/>
--password="three"-->
<user name="Bill" password="35D6D33467AAE9A2E3DCCB4B6B027878"/>
credentials>
forms>
authentication>
system.web>
configuration>
После этого можно приступить к созданию самих страниц. Для
этого воспользуйтесь кодом из листингов 14.1, 14.2 и 14.3.
- Листинг 14.1: HashedCredentials/login.aspx
<%@ Page
Language="vb" AutoEventWireup="false"
Codebehind="login.aspx.vb"
Inherits="HashedCredentials.login"%>
<html>
<head>
<title>logintitle>
head>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<b>Name: b>
<asp:TextBox ID="txtName" Runat=server/><br>
<b>Password: b>
<asp:TextBox ID="txtPassword" Runat=server TextMode=Password/>
<hr>
<asp:Button ID="btn" Runat=server Text="Login"/>
<asp:Label ID="lbl" Runat=server ForeColor="Maroon"
Font-Bold=True Visible=False>Wrong data!asp:Label>
form>
body>
html>
- Листинг 14.2: HashedCredentials/login.aspx.vb
Imports System.Web.Security
Public Class login
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Protected WithEvents txtName As
System.Web.UI.WebControls.TextBox
Protected WithEvents txtPassword As
System.Web.UI.WebControls.TextBox
Protected WithEvents btn As
System.Web.UI.WebControls.Button
Protected WithEvents lbl As
System.Web.UI.WebControls.Label
Private Sub btn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles
btn.Click
If FormsAuthentication.Authenticate(txtName.Text, txtPassword.Text) Then
FormsAuthentication.RedirectFromLoginPage(txtName.Text, False)
Else
lbl.Visible = True
End If
End Sub
End Class
- Листинг 14.3: HashedCredentials/default.aspx.vb
Imports System.Web.Security
Public Class WebForm1
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If User.Identity.IsAuthenticated Then
Response.Write("Hello, "
& User.Identity.Name & "")
Else
Response.Redirect("login.aspx")
End If
End Sub
End Class
Можете смело запускать приложение и вводить имя пользователя
и пароль. Если была введена верная пара, то вы получите приветствие, в
противном случае – сообщение о неверных данных. При этом все данные хранятся в
файле Web.config, но уже не в
открытом виде, а в хешированном.
Создаём свой криптометод
Прежде чем переходить к написанию кода, вспомните, что
основная цель криптографии – это сделать данные непонятными для посторонних
лиц. Вот значит и нужно разработать такой алгоритм, который бы превращал
понятный текст в какую-нибудь абракадабру! Для этого можно делать что угодно:
менять индексы символов, добавлять новые, делить текст на блоки и перемешивать
их, добавлять внутренние ключи и многое другое. Если хорошенько подумать, то
можно создать довольно надёжный метод ограниченного использования, и он может
стать стойкой защитой, пока его алгоритм не будет раскрыт.
Создание криптосистем общего пользования, т. е. таких, от
доступности алгоритма которых стойкость не зависит, требует, в первую очередь,
хорошего математического расчёта. Иными словами, для создания подобного
алгоритма сначала придётся порешать серьёзные задачки.
В этом разделе будет создана реализация давно известного
алгоритма Mirror. Он очень простой и является методом
ограниченного использования, потому что даже не требует ввода пароля. Если
криптоаналитику известно, что текст был зашифрован через Mirror,
то он моментально получит исходные данные, но, если криптограмма попалась
обычному пользователю, который понятия не имеет о том, что такое криптография,
то он вряд ли сможет что-либо разобрать.
Перейдём к практике. Для этого создайте новое Web-приложение и воспользуйтесь кодом из листингов 15.1 и
15.2:
- Листинг 15.1: Mirror/default.aspx
<%@ Page
Language="vb" AutoEventWireup="false"
Codebehind="default.aspx.vb"
Inherits="Mirror.WebForm1"%>
<HTML>
<HEAD>
<title>Mirrortitle>
HEAD>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<textarea id="txt" runat=server>textarea><br>
<asp:Button ID="btn" Runat=server Text="Mirror"/>
form>
body>
HTML>
- Листинг 15.2: Mirror/default.aspx.vb
Imports System.Text
Public Class WebForm1
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
Private Sub InitializeComponent()
End Sub
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
InitializeComponent()
End Sub
#End Region
Protected WithEvents txt As
System.Web.UI.HtmlControls.HtmlTextArea
Protected WithEvents btn As
System.Web.UI.WebControls.Button
Private Sub btn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles
btn.Click
Dim i As Integer, c As Byte
Dim sb As New
StringBuilder
For i = 1 To Len(txt.Value)
c =
Asc(Mid(txt.Value, i, 1))
sb.Append(Chr(c
Xor 255))
Next
txt.Value =
sb.ToString
End Sub
End Class
Код листинга 15.2 очень короткий, и посути шифрование
осуществляется всего в 1 строчке:
c Xor 255
Данное выражение равносильно следующему:
255 – c
Это означает, что одна функция выполняет как шифрование, так
и дешифрование. Алгоритм потому и называется Mirror,
т. е. зеркало, потому что он симметричен относительно 127-го символа шрифта.
Например, в шрифтах, поддерживающих кириллицу, 255-й символ
– это буква “я”. Теперь выполним операцию Xor
255, и этот символ примет индекс 0 (потому что 255 Xor
255 = 0). Повторим действия ещё раз: 0 Xor
255 = 255 – снова получаем 255-й символ, т. е. букву “я”.
В коде листинга 15.2 был применён объект StringBuider,
а не конкатенация строк, потому что с ростом размера строки растёт и время
выполнения конкатенации строк, а объект StringBuilder при этом продолжает идти в том же темпе. Одним словом, StringBuilder значительно быстрее конкатенации.
Теперь самое время запустить приложение и криптировать
какой-нибудь текст (рис. 19):
Рис. 19 – Строка “Hello, World!” зашифрована методом Mirror
Заключение
Сегодня криптография является очень перспективным и
актуальным направлением. Причин для этого множество. Вот некоторые из них: это
и мало изученная область с большим потенциалом; это и следствие того, что
информация на текущий день выступает в роли одного из важнейших блоков, на которых
строится бизнес. Если эта информация имеет хоть какую-то ценность, то всегда
может найтись кто-то, кто попытается получить к ней несанкционированный доступ.
Именно поэтому клише “лучшая защита – это нападение” очень хорошо подходит к
миру информационной безопасности. А чтобы информация оставалась ценной и секретной,
её нужно уметь защищать, и незаменимым инструментом в этой протекции становится
криптография.
Примеры к статье Вы можете скачать
здесь.