为什么此AES加密代码始终返回不同的密文?

时间:2016-03-10 20:26:00

标签: c# encryption aes encryption-symmetric rijndael

  

注意:

     

以下代码示例仅用于演示目的,并实现了不安全的方案。如果您正在寻找安全方案,请查看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=");
    }    

1 个答案:

答案 0 :(得分:2)

这里的解决方案是从存储的或常量Guid引入。致电

Guid.NewGuid();

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

  
    

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

  

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

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

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