【问题标题】:Is this wrapper around AesManaged ok?这个围绕 AesManaged 的​​包装器可以吗?
【发布时间】:2015-03-25 09:22:26
【问题描述】:

我需要加密/解密一些字符串。我已经根据msdn documentation 构建了我的包装类,但做了一些更改。

由于我想使用给定的字符串/密码来加密/解密数据,因此我不使用 AesManaged 来创建密钥。 (用户应该能够使用他输入的密钥进行加密/解密,因此我无法使用来自AesManaged 的密钥,也无法保存密钥)。

我改为使用Rfc2898DeriveBytes (PBKDF2) 和给定的盐来创建密钥。使用给定的盐是因为我不存储密钥,因此我认为盐必须始终相同。

然后我创建一个 IV,加密给定的字符串并将 IV 和加密的字符串连接起来。然后,这最终将保存在一个文件中。这意味着 IV 与加密数据一起保存。

问题:

  1. 可以将 IV 与加密数据一起存储吗?
  2. 是否有另一种方法来创建密钥,而无需每次都使用相同的盐(基于给定的密码)?
  3. 此加密是使用 AES128 还是 AES256 完成的?
  4. IV 将始终为 16 字节,还是可以更改?

    static void Main(string[] args)
    {
        const string stringToEncrypt = "String to be encrypted/decrypted. Encryption is done via AesManaged";
        const string password = "m1Sup3rS3cre!Password";

        string encrypted = EncryptString(stringToEncrypt, password);
        string roundtrip = DecryptStringFromBytes_Aes(encrypted, password);

        Console.WriteLine("Original:   {0}", stringToEncrypt);
        Console.WriteLine("Round Trip: {0}", roundtrip);

        Console.ReadLine();
    }

    static string EncryptString(string plainText, string password)
    {
        string encryptedString;

        using (AesManaged aesAlg = new AesManaged())
        {
            aesAlg.Key = PasswordAsByte(password);
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write(plainText);
                    }
                    var encrypted = msEncrypt.ToArray();

                    encryptedString = Encoding.Default.GetString(aesAlg.IV);
                    encryptedString += Encoding.Default.GetString(encrypted);
                }
            }
        }
        return encryptedString;
    }

    static string DecryptStringFromBytes_Aes(string cipherText, string password)
    {
        using (AesManaged aesAlg = new AesManaged())
        {
            aesAlg.Key = PasswordAsByte(password);

            aesAlg.IV = Encoding.Default.GetBytes(cipherText).Take(16).ToArray();

            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

            var encryptedByteArray = Encoding.Default.GetBytes(cipherText).Skip(16).ToArray();

            using (MemoryStream msDecrypt = new MemoryStream(encryptedByteArray))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        return srDecrypt.ReadToEnd();
                    }
                }
            }
        }
    }

    private static byte[] PasswordAsByte(string password)
    {
        byte[] salt = Encoding.Default.GetBytes("foobar42");
        Rfc2898DeriveBytes passwordBytes = new Rfc2898DeriveBytes(password, salt);

        return passwordBytes.GetBytes(32);
    }

【问题讨论】:

  • 很高兴您提供了大部分代码 - 但如果您包含 using 指令和类声明,我们就可以复制、粘贴和编译了。 .
  • 这不是 AES 的“实现”,只是您使用 API。
  • 我投票结束这个问题,因为这是一个代码审查问题。
  • 这是未经身份验证的加密。攻击者可以修改加密数据,而您在解密时无法分辨。你同意吗?
  • 关于“AES 的实现”:我刚才说我用 AesManaged 实现了一些东西。也许我的英语不够好。

标签: c# .net encryption aes encryption-symmetric


【解决方案1】:

不,这样不行。

1) 您在不同的地方使用Encoding.Default。不要那样做——这意味着你随心所欲地在你所在的平台上。始终使用显式编码,在大多数情况下最好使用 UTF-8。

2) 您正在使用 Encoding.GetString / Encoding.GetBytes任意二进制 数据转换为字符串并返回。这几乎必然会丢失数据。 (它碰巧在我的机器上成功了,但它确实取决于编码 - 这从根本上来说是个坏主意。)Encoding 专为 固有 文本数据的数据而设计,而你只是以一种或另一种方式应用编码。您的加密数据本质上是二进制数据。请改用Convert.ToBase64StringConvert.FromBase64String

关于您的其他问题:

  • 是的,据我所知,可以将 IV 与加密数据一起存储。
  • 您可以对密码使用相同的方法:每次生成不同的 salt,然后将 that 与加密文本一起存储。恐怕不确定是否普遍推荐。
  • 我相信您通过调用 passwordBytes.GetBytes(32) 来控制密钥大小是 128 位还是 256 位 - 这是一个 256 位密钥,所以它是 AES256。
  • 我相信 AES 的 IV 大小始终为 16 字节(128 位)

【讨论】:

  • 我确实根据您的评论更改了编码。对此并不完全了解。
  • 乔恩,你的回答有很多不确定性:) 基本上你确实可以用密文存储盐。只要密钥每次都更改(因为盐),静态 IV 就可以了,尽管仍然建议使用随机 IV。我今晚也将尝试回答(CET),但你在这里基本上是正确的(有一点说IV是CBC模式而不是AES所必需的,在这种情况下它等于块密码的块大小,对于 AES 来说确实是 128 位)。
  • @MaartenBodewes:除非我真的有信心,否则我尽量不在安全敏感的答案中表达确定性:) 期待您的回答。
【解决方案2】:

通常盐与密码的加密散列一起使用,以防止字典攻击。要使用 AES 获得相同类型的对称加密保护,您应该使用随机初始化向量。因此,当您加密时创建一个随机 IV 并将其添加到消息中(以明文形式)。当您解密时,从加密的消息中获取 IV 并使用它来解密消息。那么用相同的密钥加密的相同消息的密文就会不同。

  • 所以,是的,可以将 IV 与加密数据一起存储。

  • 您不需要每次都使用不同的盐,因为随机 IV 的目的与盐如何使字典对哈希的攻击更加困难的目的相似。

  • AES 可以使用 128、192 或 256 位的密钥大小,因此要使用 AES 256,您需要一个 256 位密钥(32 字节),这就是您所使用的。

  • AES 使用 128 位块,需要 128 位 IV(或 16 个字节)。

【讨论】:

  • 错了,恐怕静脉注射不能替代盐。
  • @MaartenBodewes:你能澄清一下你的说法吗?如果要添加随机盐,则加密消息将是盐 + IV + 密文。添加盐可以防止什么样的攻击?
  • 它可以防止对键值的字典攻击和彩虹表攻击。如果您刚刚加密了文本,那么验证密钥会更加困难,但它不会添加与 PBKDF2 相同的保护(如 RFC 2898 中所定义)。 PBKDF2 还包含针对用户和可能的攻击者的许多迭代(工作因素)。在这些迭代中使用盐。 IV 仅在之后作为 CBC 模式加密的要求应用。所以在工作因素中不考虑。
  • @MaartenBodewes:我看不出如何使用随机 IV 对 AES 加密进行彩虹表攻击。彩虹表包含预先计算的散列,但 AES 不是散列。另外,我并不是说不应该使用 PBKDF2 来使暴力字典攻击更加困难。我的观点是,使用随机盐不会增加随机 IV 尚未提供的任何安全性。
  • 您可以预先计算密钥并根据使用相同盐创建的任何密文验证它们。 IV 对防止密钥验证没有任何作用。验证将包括一两个块解密而不是完整的工作因子。当然,它没有简单的查找那么快,但也没有应有的那么难。
【解决方案3】:

是否可以将 IV 与加密数据一起存储?

是的,没关系。此外,您使用的是 AesManaged,而没有明确设置 Mode - 这种情况下模式是 CBC,在 CBC 模式下 IV 应该在密文之前。

是否有另一种方法来创建密钥,而无需每次都使用相同的盐(基于给定的密码)?

Rfc2898DeriveBytes 是从文本密码派生密钥的非常标准的方法。无需重新发明从密码派生密钥的方法,只需像现在一样使用 Rfc2898DeriveBytes。

此加密是使用 AES128 还是 AES256 完成的?

它是AES256,因为您使用的是 32 字节密码。

IV 是否总是 16 字节,或者这可以改变吗?

IV property 的大小必须与 BlockSize 属性除以 8 相同。因此对于 128 位块,它是 16。

【讨论】:

  • 关于已存储为前缀的 IV 的一个问题:AesManaged.CreateDecryptor 需要一个 IV,但我没有找到显示使用前缀 IV 的示例。你有这个链接吗?
  • @Manuel Oh.... 这看起来像是我对 .NET AES 实现的误解。因为按照标准在 CBC 模式 IV 应该是密文的前缀 - 我想 CryptoStream 已经做到了。但实际上,在写入流时,它不会在密文前面加上 IV。所以你是对的,你必须手动附加 IV。很抱歉造成混淆,我会更改答案以防止混淆其他人。
  • 这个问题已经使用Rfc2898DeriveBytes而不是重新发明它,所以这句话可能应该被废弃了。
  • @MaartenBodewes 的问题是“除了 Rfc2898DeriveBytes 之外还有其他方法吗...”我的回答应该是“不要重新发明派生密钥的方式,只需按照您的方式使用 Rfc2898DeriveBytes”。这可能从最初的答案表述中并不明显。
  • 请使用复制/粘贴来创建报价。我在问题中的任何地方都没有看到“除了 Rfc2898DeriveBytes 之外还有其他方法吗?
猜你喜欢
  • 1970-01-01
  • 2015-04-13
  • 2010-11-03
  • 2012-05-23
  • 2023-03-17
  • 2021-03-26
  • 1970-01-01
  • 1970-01-01
  • 2013-08-14
相关资源
最近更新 更多