Нашли ошибку или опечатку? Выделите текст и нажмите

Поменять цветовую

гамму сайта?

Поменять
Обновления сайта
и новые разделы

Рекомендовать в Google +1

Классы криптографии .NET

138

Прежде чем приступать к работе с криптографией в .NET, следует узнать немного больше об основах. Классы криптографии в .NET делятся на три уровня. На первом уровне находится набор абстрактных классов; эти классы определяют задачу шифрования. К ним относятся следующие классы:

AsymmetricAlgorithm

Этот класс представляет асимметричное шифрование, использующее пару ключей "открытый-секретный". Данные, зашифрованные одним ключом, могут быть расшифрованы только другим ключом.

SymmetricAlgorithm

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

HashAlgorithm

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

Второй уровень содержит классы, представляющие специфические алгоритмы шифрования. Они унаследованы от базовых абстрактных классов и также являются абстрактными. Например, класс DES, реализующий алгоритм DES (Data Encrypting Standard - стандарт шифрования данных), унаследован от SymmetricAlgorithm.

Классы третьего уровня представляют набор реализаций шифрования. Каждый из этих классов унаследован от класса алгоритма второго уровня. Это значит, что такой алгоритм шифрования, как DES, может иметь множество классов реализации. Хотя некоторые классы шифрования в .NET Framework реализованы целиком в управляемом коде, большинство все же являются тонкими оболочками для библиотеки CryptoAPI. Классы, которые обертывают функции CryptoAPI, содержат в своем имени CryptoServiceProvider (например, DESCryptoServiceProvider), в то время как имена управляемых классов обычно включают фрагмент Managed (например, RijndaelManaged).

По сути, управляемые классы выполняют всю свою работу в мире .NET под надзором CLR, а неуправляемые классы используют вызовы из неуправляемой библиотеки CryptoAPI. Это может показаться ограничением, но на самом деле является эффективным повторным использованием существующей технологии.

Несмотря на неуклюжий программный интерфейс, CryptoAPI невозможно упрекнуть в несовершенстве технологии. На рисунке ниже показаны классы из пространства имен System.Security.Cryptography. Эта трехуровневая организация обеспечивает практически неограниченную расширяемость. Создавать новые реализации для существующих криптографических классов можно за счет наследования от существующих классов алгоритмов:

Иерархия классов криптографии

Например, можно создать класс, реализующий алгоритм DES, полностью в управляемом коде, создав новый класс DESManaged и унаследовав его от DESCryptoServiceProvider. Аналогично можно добавить поддержку нового алгоритма шифрования, создав для этого новый абстрактный класс алгоритма (например, алгоритм CAST128, который похож на DES, но платформой .NET не предоставляется), а также конкретный класс реализации (такой как, например, CAST128Managed для алгоритма CAST128).

Классы шифрования - один из немногих случаев в библиотеке классов .NET, когда стандартная система именования и правила регистров букв не применяются. Например, есть классы с именами TripleDES и RSA, а не TripleDes и Rsa.

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

Как уже упоминалось, .NET Framework поддерживает три типа шифрования: симметричное, асимметричное и однонаправленное (хеширование). Симметричные алгоритмы всегда используют один и тот же ключ для шифрования и расшифровки. Симметричные алгоритмы работают быстро в обоих направлениях. В таблице ниже перечислены наиболее важные симметричные алгоритмы, поддерживаемые .NET Framework:

Симметричные алгоритмы, поддерживаемые .NET
Абстрактный алгоритм Реализация по умолчанию Допустимая длина ключа Максимальная длина ключа
DES DESCryptoServiceProvider 64 64
TripleDES TripleDESCryptoServiceProvider 128, 192 192
RC2 RC2CryptServiceProvider 40 - 128 128
Rijndael RijndaelManaged 128, 192, 256 256

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

Алгоритмы DES, TripleDES и RC2 реализованы в CryptoAPI, и потому нуждаются в пакете High Encryption Pack. Обратите внимание, что длина ключа для алгоритмов DES и TripleDES включает биты контроля четности, которые не влияют на надежность шифрования. TripleDES с 192-битным ключом использует только 168 бит, в то время как 128-битный ключ - только 112 бит.

В DES 64-битный ключ использует только 56 бит. По этой причине он считается относительно ненадежным, и лучше вместо него применять другие алгоритмы. Дополнительную информацию об относительной надежности этих алгоритмов можно найти в специальных книгах или Интернет-ресурсах, посвященных теории криптографии.

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

  • Передача ключа. При использовании симметричных алгоритмов для обмена данными между двумя приложениями, расположенными на разных машинах, необходим безопасный способ передачи ключа.

  • Атака "грубой силой". При использовании симметричных ключей в течение длительного времени у злоумышленников может быть достаточно времени для расшифровки трафика простым подбором значений битов ключа. Таким образом, с ростом длины ключа в битах надежность ключа возрастает, как объяснялось ранее. Однако обычно это означает, что все равно периодически нужно менять ключи.

  • Долговременное управление ключами. Если приходится обновлять ключи через регулярные интервалы времени, их также требуется передавать, что привносит дополнительный риск. Кроме того, ключ должен храниться в безопасном месте.

Системы на основе симметричных алгоритмов шифрования недостаточно безопасны, поэтому и возникает необходимость в асимметричных алгоритмах.

Асимметричное шифрование

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

Обычно ключ, используемый для шифрования, называется открытым ключом (public key). Его можно выдать любому, кто желает отправлять вам зашифрованную информацию. С другой стороны, секретный ключ (private key) - это единственный ключ, который можно использовать для расшифровки. Таким образом, если вы - единственный владелец секретного ключа, то только вы сможете расшифровать информацию. Этот факт облегчает обмен ключами между различными участниками процесса, поскольку нет необходимости в передаче ключа, с помощью которого можно расшифровать важные данные. В таблице ниже перечислены асимметричные алгоритмы, поддерживаемые .NET Framework:

Асимметричные алгоритмы, поддерживаемые .NET
Абстрактный алгоритм Реализация по умолчанию Допустимая длина ключа Максимальная длина ключа
RSA RSACryptoServiceProvider 384-16384 (с увеличением на 8 бит) 1024
DSA DSACryptoServiceProvider 512-1024 (с увеличением на 64 бита) 1024

Из двух алгоритмов - RSA (его название происходит от фамилий изобретателей алгоритма - Rivest, Shamir, Adleman (Рон Райвест, Ада Шамир, Леонард Адлеман)) и DSA (Digital Signature Algorithm - алгоритм цифровой подписи) - только RSA поддерживает прямое шифрование и расшифровку значений. Алгоритм DSA, как следует из его названия, может использоваться только для подписания информации и верификации подписей.

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

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

Абстрактные криптографические классы

Абстрактные криптографические классы в действительности служат двум целям. Во-первых, они определяют базовые члены, которые должны поддерживать конкретные реализации криптографии. Во-вторых, они предлагают некоторую функциональность через статический метод Create(), который можно использовать непосредственно, без создания экземпляров класса. Этот метод позволяет создавать один из классов конкретной реализации, без необходимости знать то, как именно он реализован. Например, взгляните на следующую строку кода:

DES crypt = DES.Create();

Статический метод Create() возвращает экземпляр класса реализации DES по умолчанию. В данном случае - это DESCryptoServiceProvider. Преимущество этой техники состоит в возможности кодирования в обобщенном виде, избегая зависимости от специфической реализации. Лучше всего то, что если Microsoft обновит платформу, в результате чего реализация DES по умолчанию изменится, код подхватит эту замену автоматически. В частности, это удобно, если используется класс CryptoAPI, который может быть заменен в будущем эквивалентным классом в управляемом коде.

Фактически, при желании можно работать даже на еще более высоком уровне, если используется статический метод Create() одного из классов криптографических задач. Например, рассмотрим такой код:

SymmetricAlgorithm crypt = SymmetricAlgorithm.Create();

Здесь создается экземпляр криптографического класса, который определен как симметричный алгоритм по умолчанию. В данном случае будет не DES, a Rijndael. Возвращенный объект будет экземпляром класса реализации RijndaelManaged. Дополнительную информацию о конфигурировании реализации по умолчанию и настройки дружественных имен, используемых с методом Create(), ищите в документе из MSDN под названием "Отображение имен алгоритмов на криптографические классы".

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

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

Интерфейс ICryptoTransform

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

Чтобы понять, как это работает, необходимо рассмотреть основные типы - интерфейс ICryptoTransform и класс CryptoStream. Интерфейс ICryptoTransform представляет блочную криптографическую трансформацию. Это может быть шифрование, расшифровка, хеширование, кодирование/декодирование Base64 или форматирование. Чтобы создать объект ICryptoTransform для данного алгоритма, используются методы CreateEncryptor() и CreateDecryptor() на экземпляре класса криптографического алгоритма (вроде экземпляра DES или любого другого алгоритма, созданного ранее).

Используйте метод CreateEncryptor(), если хотите шифровать данные, и CreateDecryptor() - если данные нужно расшифровать. Ниже показан фрагмент кода, в котором создается ICryptoTransform для шифрования алгоритмом DES:

DES crypt = DES.Create();
ICryptoTransform transform = crypt.CreateEncryptor();

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

Класс CryptoStream

Класс CryptoStream служит оболочкой для обычного потока и использует ICryptoTransform для выполнения своей работы "за кулисами". Ключевое преимущество состоит в том, что CryptoStream применяет буферизованный доступ, при котором можно осуществлять автоматическое шифрование, не заботясь о размере блока, требуемого конкретным алгоритмом. Другое преимущество CryptoStream в том, что поскольку он является оболочкой для обычного класса .NET, унаследованного от потока, он может легко комбинироваться с другой операцией, такой как файловый доступ (через FileStream), доступ к памяти (через MemoryStream), низкоуровневый сетевой вызов (через NetworkStream) и т.д.

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

Например, в следующем фрагменте кода создается ICryptoTransform с использованием класса, реализующего алгоритм DES, и затем он применяется с существующим потоком для создания CryptoStream:

DES crypt = DES.Create();
ICryptoTransform transform = crypt.CreateEncryptor();
CryptoStream cs = new CryptoStream(fileStream, transform, CryptoStreamMode.Write);
// (Теперь cs можно использовать для записи зашифрованной информации в файл.)

Обратите внимание, что CryptoStream может быть в одном из двух режимов - чтения или записи, как определено перечислением CryptoStreamMode. В режиме чтения трансформация выполняется после получения данных от лежащего в основе потока, как показано на рисунке ниже:

Чтение и расшифровка данных

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

Запись и шифрование данных

Комбинировать оба метода для создания читающего и пишущего CryptoStream невозможно (он все равно не имеет смысла). Аналогично метод Seek() и свойство Position, которые используются для перемещения в другую позицию потока, не поддерживаются для CryptoStream(), и в случае вызова генерируют исключение NotSupportedException. Однако эти методы часто используются с лежащим в основе потоком.

Пройди тесты