#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, большое вам спасибо за этот подробный ответ! Знание того, что здесь происходит под капотом, очень помогает. Приветствую, Майк