【问题标题】:AES-256-CBC in .NET Core (C#).NET Core (C#) 中的 AES-256-CBC
【发布时间】:2020-08-24 05:21:53
【问题描述】:

我正在搜索 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(初始化向量)。不幸的是,我没有 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] 密钥对于这种算法的长度错误。

感谢您对此的任何帮助!

干杯,迈克

【问题讨论】:

  • 你有一个 256 byte 的密钥?那么你肯定应该将 2048 传递给KeySize 吗?您必须使用 LegalKeySizes 属性检查这是否是合法的密钥大小。我不确定是不是。
  • 谢谢你的提示,约翰。它几乎可以肯定是一个 256 位(而不是字节)的密钥。我将不得不再次检查我的算法的第一步(解密密钥)。但是,如果你能给我一些关于 IV 问题的背景知识,那也会很有帮助。
  • 我不是 100% 确定真的。我认为,如果您不将其设置为 0 的任何值。 IV 基本上就像带有密码散列的盐。它确保对同一事物进行两次加密不会产生完全相同的结果。如果您使用它,则需要将其与加密数据一起包含在内。如果你不这样做,那么我认为 0 应该可以工作。我从来没有在没有静脉注射的情况下使用过它,所以我可能错了。
  • 如果 256 字节的密钥长度是正确的,我将如何解决这个问题?
  • AES 支持 128、192 和 256 的密钥大小。

标签: c# .net .net-core aes cbc-mode


【解决方案1】:

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

因此,解密首先要确定salt。基于盐,必须与密码短语、密钥和 IV 一起导出,然后才能解密其余的加密数据。因此,首先需要在 C# 中实现 EVP_BytesToKey,例如here。那么一个可能的实现可能是(使用 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 不得使用。

【讨论】:

  • 您好 Topaco,非常感谢您的深入回答!了解这里的幕后情况很有帮助。干杯,迈克
猜你喜欢
  • 2022-08-17
  • 2013-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-05
相关资源
最近更新 更多