AES-256-CBC в .NET Core (C #)

#c# #.net #.net-core #aes #cbc-mode

#c# #.net #.net-ядро #aes #cbc-режим

Вопрос:

Я ищу код C # для воспроизведения следующей команды openssl.

openssl enc -d -aes-256-cbc -in my_encrypted_file.csv.enc -out my_decrypted_file.csv -pass file:key.bin

Дополнительная информация:

  • Зашифрованный файл присутствует в виде байта[]
  • Ключ.bin — это байт [] длиной 256 (ключ получается более простым расшифрованием еще одного файла, который мне удалось реализовать на C #).

Я пробовал различные примеры, найденные при поиске в Интернете. Проблема в том, что для всех этих примеров требуется IV (вектор инициализации). К сожалению, у меня нет IV, и никто в команде не знает, что это такое или как это можно определить. Команда openssl, похоже, не нуждается в ней, поэтому я немного смущен этим.

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

 public static string DecryptAesCbc(byte[] cipheredData, byte[] key)
{
    string decrypted;

    System.Security.Cryptography.Aes aes = System.Security.Cryptography.Aes.Create();
    aes.KeySize = 256;
    aes.Key = key;
    byte[] iv = new byte[aes.BlockSize / 8];
    aes.IV = iv;
    aes.Mode = CipherMode.CBC;

    ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);

    using (MemoryStream ms = new MemoryStream(cipheredData))
    {
        using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
        {
            using (StreamReader sr = new StreamReader(cs))
            {
                decrypted = sr.ReadToEnd();
            }
        }
        
        return decrypted;
    }
}
  

Ошибка кода говорит о том, что мой ключ byte [256] имеет неправильную длину для такого алгоритма.

Спасибо за любую помощь с этим!

Приветствую, Майк

Комментарии:

1. У вас есть 256 байтовый ключ? Конечно, вы должны передать 2048 на KeySize тогда? Вам нужно было бы проверить, является ли это допустимым размером ключа, используя LegalKeySizes свойство. Я не уверен, так ли это.

2. Спасибо тебе за этот намек, Джон. Это почти наверняка 256-битный (а не байтовый) ключ. Мне придется еще раз изучить первый шаг моего алгоритма (расшифровка ключа). Однако, если бы вы могли рассказать мне немного о проблеме с капельницей, это тоже было бы очень полезно.

3. На самом деле я не уверен на 100%. Я думаю, что если вы ничего не зададите, это будет 0s. IV в основном похож на соль с хэшированием пароля. Это гарантирует, что шифрование одного и того же дважды не приведет к точно такому же результату. Если вы используете его, вам нужно будет включить это вместе с зашифрованными данными. Если вы этого не сделаете, я думаю, что 0s должны работать. Я никогда не использовал его без IV, хотя, так что я могу ошибаться в этом.

4. Если бы длина ключа в 256 байт была правильной, как бы я должен был решить эту проблему?

5. AES поддерживает размеры ключей в 128, 192 и 256 бит .

Ответ №1:

Опубликованный оператор OpenSSL использует -pass file: параметр и, следовательно, кодовую фразу (которая считывается из файла), см. openssl enc. Это приводит к тому, что процесс шифрования сначала генерирует случайную соль в 8 байт, а затем вместе с парольной фразой получает ключ в 32 байта и 16 байт IV, используя (не очень безопасную) проприетарную функцию OpenSSL EVP_BytesToKey . Эта функция использует несколько параметров, например, дайджест и количество итераций. Дайджест по умолчанию для получения ключа — MD5, а количество итераций равно 1. Обратите внимание, что OpenSSL версии 1.1.0 и более поздние версии используют SHA256 в качестве дайджеста по умолчанию, т. Е. в зависимости от версии OpenSSL, используемой для генерации зашифрованного текста, для расшифровки должен использоваться соответствующий дайджест. Перед зашифрованным текстом находится блок, первые 8 байт которого являются кодировкой ASCII Salted__ , за которым следует 8 байт salt.

Следовательно, при расшифровке необходимо сначала определить соль. На основе соли вместе с парольной фразой должны быть получены ключ и IV, а затем остальные зашифрованные данные могут быть расшифрованы. Таким образом, в первую очередь требуется реализация EVP_BytesToKey на C #, например, здесь. Тогда возможной реализацией могло бы быть (использование MD5 в качестве дайджеста):

 public static string DecryptAesCbc(byte[] cipheredData, string passphrase)
{
    string decrypted = null;

    using (MemoryStream ms = new MemoryStream(cipheredData))
    {
        // Get salt
        byte[] salt = new byte[8];
        ms.Seek(8, SeekOrigin.Begin);
        ms.Read(salt, 0, 8);

        // Derive key and IV
        OpenSslCompat.OpenSslCompatDeriveBytes db = new OpenSslCompat.OpenSslCompatDeriveBytes(passphrase, salt, "MD5", 1);
        byte[] key = db.GetBytes(32);
        byte[] iv = db.GetBytes(16);

        using (Aes aes = Aes.Create())
        {
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.CBC;
            aes.Key = key;
            aes.IV = iv;

            // Decrypt
            ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
            using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
            {
                using (StreamReader sr = new StreamReader(cs, Encoding.UTF8))
                {
                    decrypted = sr.ReadToEnd();
                }
            }
        }
    }

    return decrypted;
}
  

Обратите внимание, что вторым параметром DecryptAesCbc является кодовая фраза (как string ), а не ключ (как byte[] ). Также обратите внимание, что StreamReader используется кодировка (UTF-8 по умолчанию), которая требует совместимых данных (т. Е. текстовых данных, но это должно соблюдаться для csv файлов). В противном случае (т. Е. для двоичных данных в отличие от текстовых данных) StreamReader не должны использоваться.

Комментарии:

1. Привет, Topaco, большое вам спасибо за этот подробный ответ! Знание того, что здесь происходит под капотом, очень помогает. Приветствую, Майк