AES — симметричное шифрование в .NET C#

AES — это симметричный алгоритм блочного шифрования также известный как алгоритм Rijndael. На данный момент AES принят в качестве стандарта шифрования в США, который пришел на смену, считающемуся уже устаревшим, алгоритму шифрования DES. Краткую информацию о том, как работают алгоритмы симметричного шифрования можно получить здесь. В .NET для использования AES, так же, как и других алгоритмов шифрования или хэширования, необходимо подключить в проект пространство имен System.Security.Cryptography.

Класс Aes в .NET C#

Aes — это абстрактный класс от которого наследуются все реализации алгоритма AES. В свою очередь, класс Aes сам является наследником другого абстрактного класса — SymmetricAlgorithm, который представляет собой, как следует из названия, симметричный алгоритм шифрования. В .NET, все классы, производные от SymmetricAlgorithm класса, используют режим сцепления блоков текста (англ. Cipher Block Chaining, CBC). Этот режим требует использования ключа (Key) и вектора инициализации (IV) для выполнения криптографических преобразований данных. В режиме CBC каждый блок открытого текста (кроме первого) побитово складывается по модулю 2 (операция XOR) с предыдущим результатом шифрования и, поэтому каждый блок зашифрованного текста зависит от всех предыдущих блоков. При этом, для шифрования первого блока используется вектор инициализации (IV). Режим CBC используется по умолчанию, но, при желании его можно изменить.

Соответственно, чтобы расшифровать данные, зашифрованные с помощью одного из наследников SymmetricAlgorithm, необходимо задать свойства Key и IV те же, что использовались для шифрования.

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

Aes aes = new Aes()

В большинстве случаев достаточно вызвать метод Create какого-либо класса (Aes, Des и т.д.), чтобы получить объект для шифрования по заданному алгоритму. Например, воспользуемся этим методом и создадим объект для шифрования данных с использованием AES и посмотрим на его настройки по умолчанию и некоторые другие свойства:

Aes aes = Aes.Create();

Console.WriteLine($"{aes.GetType()}");
Console.WriteLine($"Размер ключа: {aes.KeySize}");
Console.WriteLine($"Размер блока: {aes.BlockSize}");
Console.WriteLine($"Режим работы: {aes.Mode}");
Console.WriteLine("Размеры ключей, поддерживаемые симметричным алгоритмом:");
foreach (var keySize in aes.LegalKeySizes) 
{
    Console.WriteLine($"{keySize.MinSize} - {keySize.MaxSize} бит (с шагом {keySize.SkipSize})");
}
Console.WriteLine("Размеры блоков, поддерживаемые симметричным алгоритмом:");
foreach (var blockSize in aes.LegalBlockSizes) 
{
    Console.WriteLine($"{blockSize.MinSize} - {blockSize.MaxSize} бит");
}

Console.WriteLine($"Вектор инициализации: {Convert.ToHexString(aes.IV)}");
Console.WriteLine($"Ключ: {Convert.ToHexString(aes.Key)}");

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

System.Security.Cryptography.AesImplementation
Размер ключа: 256
Размер блока: 128
Режим работы: CBC
Размеры ключей, поддерживаемые симметричным алгоритмом:
128 — 256 бит (с шагом 64)
Размеры блоков, поддерживаемые симметричным алгоритмом:
128 — 128 бит
Вектор инициализации: 5B19443818A7FFAA9AFA99D73D0EB2EC
Ключ: D420447765A961A0D2257D117A48613DC44B24CE4D6AA3530EE925C6308C320E

Как видно из вывода консоли, для AES мы можем использовать ключи размером 128 бит, 192 бита и 256 бит. По умолчанию, в полученном объекте уже создан ключ размером в 256 бит и вектор инициализации в 128 бит. Но, опять же, при желании, мы можем изменить эти значения.

Класс CryptoStream

Классы симметричного шифрования (Aes, Des и т.д.) используются со специальным классом потока — CryptoStream , который шифрует данные, считанные в поток. Класс CryptoStream инициализируется с помощью класса управляемого потока и класса, реализующего интерфейс ICryptoTransform, а также с указанием значения перечисления CryptoStreamMode, которое указывает тип доступа, разрешенного для CryptoStream.

Класс CryptoStream можно инициализировать с помощью любого класса, который является производным Stream от класса, включая FileStreamMemoryStream и т.д.

Например, чтобы создать объект типа CryptoStream для шифрования данных, можно использовать такой конструктор:

            using Aes aes = Aes.Create();
            
            using FileStream inStream  = new FileStream(sourceFileName, FileMode.Open); //создаем файловый поток на чтение - данные из этого потока будут шифроваться
            using FileStream outStream = new FileStream(outputFileName, FileMode.Create);//создаем файловый поток на запись - в этот поток будут записываться зашифрованные данные

            //поток для шифрования данных
            CryptoStream encStream = new CryptoStream(outStream, //поток в который будут записываться зашифрованные данные
                                                      aes.CreateEncryptor(aes.Key, aes.IV), //класс, реализующий интерфейс ICryptoTransform
                                                      CryptoStreamMode.Write); //поток открывается на запись

Теперь попробуем воспользоваться полученными знаниями и зашифровать какой-нибудь файл

Шифрование файлов с помощью Aes

В качестве примера, зашифруем текстовый файл со следующим содержимым:

Алгоритм AES C#Вот как может выглядеть процесс шифрования файла с использованием алгоритма AES:

private static byte[] GetIV(string ivSecret)
{
    using MD5 md5 = MD5.Create();
    return md5.ComputeHash(Encoding.UTF8.GetBytes(ivSecret));
}

private static byte[] GetKey(string key)
{
    using SHA256 sha256 = SHA256.Create();
    return sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
}

static void Main(string[] args)
{
    string sourceFileName = "secretfile.txt"; //файл, который будем шифровать
    string outputFileName = "secretfile.enc"; //файл, который будет содержать зашифрованные данные
    string key = "секретный ключ"; //ключ для шифрования
    string ivSecret = "вектор"; //вектор инициализации


    using Aes aes = Aes.Create();
    aes.IV = GetIV(ivSecret);
    aes.Key = GetKey(key);

    using FileStream inStream  = new FileStream(sourceFileName, FileMode.Open); //создаем файловый поток на чтение
    using FileStream outStream = new FileStream(outputFileName, FileMode.Create);//создаем файловый поток на запись

    //поток для шифрования данных
    CryptoStream encStream = new CryptoStream(outStream, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write);

    long readTotal = 0;             
    
    int len;
    int tempSize = 100; //размер временного хранилища
    byte[] bin = new byte[tempSize];    //временное Хранилище для зашифрованной информации

    while (readTotal < inStream.Length)
    {
        len = inStream.Read(bin, 0, tempSize);
        encStream.Write(bin, 0, len);
        readTotal = readTotal + len;
        Console.WriteLine($"{readTotal} байт обработано");
    }

    encStream.Close();
    outStream.Close();
    inStream.Close();
}

Несмотря на то, что при создания экземпляра Aes у нас уже будет записан в свойствах и ключ и вектор инициализации я всё же создал свои значения этих свойств, используя алгоритмы хэширования MD5 (128 бит) и SHA-256 (256 бит).

После выполнения этого кода рядом с незашифрованным файлом появится файл secretfile.enc следующего содержания:

AES в C#

Зашифровать файл — хорошо, но его ещё потребуется и расшифровать. Посмотрим, как можно реализовать обратную операцию — расшифровку файла.

Расшифровка файлов

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

private static void DecryptFile(string sourceFile, string destFile, byte[] key, byte[] iv)
{
    using FileStream fileStream = new(sourceFile, FileMode.Open);
    using Aes aes = Aes.Create();
    
    aes.IV = iv;
    
    using CryptoStream cryptoStream = new(fileStream,
                 			  aes.CreateDecryptor(key, iv),
                                          CryptoStreamMode.Read); //создаем поток для чтения (расшифровки) данных

    using FileStream outStream = new FileStream(destFile, FileMode.Create);//создаем поток для расшифрованных данных
    
    using BinaryReader decryptReader = new(cryptoStream);

    int tempSize = 10;  //размер временного хранилища
    byte[] data;        //временное хранилище для зашифрованной информации

    while (true)
    {
        data = decryptReader.ReadBytes(tempSize);
        if (data.Length == 0)
            break;
        outStream.Write(data, 0, data.Length);
    }
}

Здесь мы уже создаем объект типа CryptoStream в режиме CryptoStreamMode.Read, то есть для расшифровки данных. Так как мы исходим из того, что не знаем, что зашифровано в файле (текст или какие-то двоичные данные), то для чтения данных из CryptoStream мы создали объект типа BinaryReader для чтения двоичных данных и, далее, с помощью этого объекта прочитали всё содержимое из CryptoStream и записали его в выходной поток данных.

Так как я знал, что мой файл маленький — всего 42 байта, то задал размер буфера чтения всего 10 байт, но никто не запрещает его делать больше, например 100 или 1000 байт.

Вызов метода:

string sourceFileName = "secretfile.txt"; //файл, который будем шифровать
string outputFileName = "secretfile.enc"; //файл, который будет содержать зашифрованные данные
string key = "секретный ключ"; //ключ для шифрования
string ivSecret = "вектор"; //вектор инициализации

DecryptFile(outputFileName, "decrypt.txt", GetKey(key), GetIV(ivSecret));

На выходе получим файл decrypt.txt с расшифрованными данными.

Итого

В этой статье мы рассмотрели один из алгоритмов симметричного шифрования — AES. В этом алгоритме могут использоваться ключи размером 128, 192 и 256 байт. По размеру ключа может определяться конкретный алгоритм AES. Например, AES-192 будет означать, что здесь используется ключ размером 192 бита. Для создания ключей и векторов инициализации мы можем воспользоваться алгоритмами хэширования или же использовать предложенные по умолчанию при создании конкретного объекта типа Aes. Главное при этом — запомнить значения, чтобы была возможность расшифровки данных.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии