【问题标题】:Why is this AES encryption code always returning a different cipher text?为什么这个 AES 加密代码总是返回不同的密文?
【发布时间】:2023-04-01 16:54:01
【问题描述】:

注意:

以下代码示例仅用于演示目的,并实现了不安全的方案。如果您正在寻找安全方案,请查看https://stackoverflow.com/a/10177020/40347

我正在使用 AESCryptoServiceProvider 类来测试一些加密概念。到目前为止,在所有示例和文章中,它们都会生成一个随机密钥用于加密,然后立即用于解密。当然,它工作正常,因为您正在使用那里的密钥,但是如果您加密,保存文本,稍后您想要解密它,您将需要相同的密钥。并且为此目的也是相同的 IV。

现在,在这段代码中,我在多次传递中使用相同的密钥和 IV,每次运行批处理时,该批处理都会给出相同的结果(如预期的那样)。但随后我关闭测试应用程序并重新运行相同的代码而不做任何更改,结果(Base64 编码的)密码文本对于相同的输入参数是不同的,为什么?

我从上一次运行中“保存”了一个 B64 编码的密码,并将其提供给 TestDecrypt 方法,正如预期的那样,它抛出了一个加密异常,提到了一些关于填充的内容,尽管我确信这与以下事实有关不知何故,对于相同的 Key、IV、纯文本和参数,它在应用程序的每次单独运行时都会给出不同的结果。

为了加密,我有这个:

    public string Test(string password, Guid guid, string text)
    {
        const int SaltSize = 16;

        string b64Cryptogram;
        MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
        Rfc2898DeriveBytes pwbytes = new Rfc2898DeriveBytes(password, SaltSize);

        // Block 128-bits Key 128/192/256 bits (16/24/32 bytes)
        using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
        {
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.CBC;
            //aes.IV = pwbytes.GetBytes(aes.BlockSize / 8);
            aes.IV = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
            aes.Key = guid.ToByteArray();

            ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {

                        //Write all data to the stream.
                        swEncrypt.Write(text);
                    }
                    b64Cryptogram = Convert.ToBase64String(msEncrypt.ToArray());
                }
            }
            Console.WriteLine("E: {0}", b64Cryptogram);
            aes.Clear();
        }
        return b64Cryptogram;
    }

请注意,我没有使用 RFC2898DeriveBytes,因为它会随机派生一些我将不再记得的东西 :) 加密它的想法正是我知道我用来加密它的东西。

解密方法如下:

    public void TestDecrypt(string password, Guid guid, string ciphertextB64)
    {
        const int SaltSize = 16;

        byte[] cipher = Convert.FromBase64String(ciphertextB64);
        string plaintext;

        MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
        Rfc2898DeriveBytes pwbytes = new Rfc2898DeriveBytes(password, SaltSize);

        // Block 128-bits Key 128/192/256 bits (16/24/32 bytes)
        using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
        {
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.CBC;
            //aes.IV = pwbytes.GetBytes(aes.BlockSize / 8);
            aes.IV = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
            aes.Key = guid.ToByteArray();

            ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
            using (MemoryStream msEncrypt = new MemoryStream(cipher))
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader swEncrypt = new StreamReader(csEncrypt))
                    {
                        plaintext = swEncrypt.ReadToEnd();
                    }
                }
            }
            Console.WriteLine("D: {0}", plaintext);
            aes.Clear();
        }
    }

现在,只需将其放入控制台应用程序并运行即可。然后退出并再次运行,您会看到对于相同的 Mode、Padding、IV、Key 和纯文本数据,每次应用程序运行时输出的密码都不相同。如果您在应用程序的同一运行中重复运行该方法,它们将是相同的。

如果不是很明显,这里是我用来测试的控制台代码:

        Guid guid = Guid.NewGuid();
        string plain = "Text to be encrypted 123458970";
        string password = "This is a test of the emergency broadcast system";

        TestDecrypt(password, guid, Test(password, guid, plain));
        TestDecrypt(password, guid, Test(password, guid, plain));
        Test(password, guid, plain);
        Test(password, guid, plain);
        Test(plain, guid, password);
        TestDecrypt(password, guid, "W4Oi0DrKnRpxFwtE0xVbYJwWgcA05/Alk6LrJ5XIPl8=");
    }    

【问题讨论】:

  • 传入的guid总是一样的吗?
  • 是的,正如我所说,参数是恒定的。
  • 你是如何生成你的 guid 的? var guid = Guid.NewGuid();这将在每次运行时生成一个新的 guid,并在每次运行时生成不同的文本。如果您正在硬编码一个 guid 值,那么我不知道,因为每次运行都会给我相同的文本。
  • 你用这个代码做什么?为什么不使用随机 IV?这是一个严重的安全漏洞,因为它使您的方案容易受到字典攻击(请参阅cwe.mitre.org/data/definitions/329.html)。
  • 查看这里以获得更好的方法:stackoverflow.com/a/10177020/40347

标签: c# encryption aes encryption-symmetric rijndael


【解决方案1】:

这里的解决方案是从存储的或常量Guid 中提取。调用

Guid.NewGuid();

每次都会返回不同的结果。来自文档:

这是一个方便的静态方法,您可以调用它来获取新的 Guid。该方法封装了对 Windows CoCreateGuid 函数的调用。返回的 Guid 保证不等于 Guid.Empty。

或者,在测试时,您可以使用 Guid.Empty,它将返回全零。

或者,您可以使用其字符串构造函数重载来存储它:

var guid = new Guid("0f8fad5b-d9cb-469f-a165-70867728950e");

【讨论】:

    猜你喜欢
    • 2012-08-02
    • 1970-01-01
    • 1970-01-01
    • 2021-11-09
    • 2021-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-12
    相关资源
    最近更新 更多