.NET CoreC#中的AES-256-CBC

问题描述

我正在搜索C#代码以重现以下openssl命令。

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

其他信息:

  • 以字节[]形式存在的加密文件
  • key.bin是一个长度为256的byte [](密钥是通过对另一个文件进行更简单的解密而获得的,我已经在C#中实现了这一点)。

我一直在尝试通过搜索网络找到的各种示例。 问题在于,所有这些示例都需要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]键的长度对于这种算法而言是错误的。

谢谢您的帮助!

干杯,迈克

解决方法

发布的OpenSSL语句使用-pass file:选项,因此使用了口令(从文件中读取),请参见openssl enc。这导致加密过程首先生成随机 8字节盐,然后与密码短语一起使用(不是很安全)专有的OpenSSL函数{{1来导出32字节密钥和16字节IV。 }}。此功能使用多个参数,例如摘要和迭代计数。密钥派生的默认摘要为 MD5 ,迭代次数为1。请注意,OpenSSL版本1.1.0和更高版本将 SHA256 用作默认摘要,即,取决于用于生成密文的OpenSSL版本,必须使用适当的摘要进行解密。在密文之前是一个块,其前8个字节是EVP_BytesToKey的ASCII编码,后跟8个字节的salt。

因此,解密必须首先确定盐。必须根据盐和密码短语一起导出密钥和IV,然后才能解密其余的加密数据。因此,首先需要在C#中实现Salted__,例如here。然后可能的实现方式是(使用 MD5 作为摘要):

EVP_BytesToKey

请注意,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,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)。另请注意,StreamReader使用一种编码(默认为UTF-8),该编码需要兼容的数据(即文本数据,但是csv文件应满足此要求)。否则(例如,对于二进制数据而不是文本数据)不得使用byte[]