C#AES解密代码

我有AES算法的字符串加密/解密代码。 加密工作正常,但我解密后无法获得正确的纯文本。 解密代码中肯定有问题。

我复制了下面的代码。
请帮忙。 谢谢。

以下是加密和解密代码。

public static class EncryptionHelper { private static int BlockSize = 16; private static byte[] Key { get { byte[] hash = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes("BlahBlahBlah")); byte[] key = new byte[BlockSize]; Array.Copy(hash, 0, key, 0, BlockSize); return key; } } private static byte[] IV { get { StringBuilder builder = new StringBuilder(); Random random = new Random(); for (int i = 0; i < BlockSize; i++) { char ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))); builder.Append(ch); } return Encoding.UTF8.GetBytes(builder.ToString()); } } public static string DecodeAndDecrypt(string cipherText) { string DecodeAndDecrypt = AesDecrypt(Convert.FromBase64String(HttpUtility.UrlDecode(cipherText))); return (DecodeAndDecrypt); } public static string EncryptAndEncode(string plaintext) { return HttpUtility.UrlEncode(Convert.ToBase64String(AesEncrypt(plaintext))); } public static string AesDecrypt(Byte[] inputBytes) { Byte[] outputBytes = inputBytes; string plaintext = string.Empty; using (MemoryStream memoryStream = new MemoryStream(outputBytes)) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateDecryptor(Key, IV), CryptoStreamMode.Read)) { using (StreamReader srDecrypt = new StreamReader(cryptoStream)) { plaintext = srDecrypt.ReadToEnd(); } } } return plaintext; } public static byte[] AesEncrypt(string inputText) { byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText); for (int i = 0; i < BlockSize; i++) { inputBytes[i] ^= IV[i]; } byte[] result = null; using (MemoryStream memoryStream = new MemoryStream()) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateEncryptor(Key, IV), CryptoStreamMode.Write)) { cryptoStream.Write(inputBytes, 0, inputBytes.Length); cryptoStream.FlushFinalBlock(); result = memoryStream.ToArray(); } } return result; } private static RijndaelManaged GetCryptoAlgorithm() { RijndaelManaged algorithm = new RijndaelManaged(); algorithm.Padding = PaddingMode.PKCS7; algorithm.Mode = CipherMode.CBC; algorithm.KeySize = 128; algorithm.BlockSize = 128; return algorithm; } } 

我做了一些更改,并将在下面发布一些源代码。 为了使它工作,只需在Form1上放置3个textBox并将此代码用于Form1.cs。 我在上面提到过,我认为一个主要问题是如何在不指定编码的情况下将StreamReader作为字符串读取。 我的更改使用BinaryReader作为字节数组读取,然后在读取后转换为UTF-8字符串。 我也删除了那个XOR循环。 我认为你试图自己实现CBC(CBC是一种XOR的反馈循环)但不需要 – 当你指定CBC模式时,.NET会为你做CBC模式。 为了使我的版本尽可能接近你的版本,我没有做更多的改动。 但请记住以上一些评论。 例如,当.NET提供DeriveBytes函数时,不要使用简单的哈希。 像纸巾一样处理静脉注射 – 只使用一次,使用一次。 等等。 无论如何,function如果不是“最佳实践”代码如下:

编辑:对不起我忘了提到我还将纯文本类型更改为常规字符串,删除了与HTTP相关的代码。 这只是为了让我更容易合作。 它应该与您的HTTP方法一样好用,就像使用常规字符串一样。

 using System; using System.IO; using System.Security.Cryptography; using System.Text; using System.Windows.Forms; namespace StackOverflow { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void textBox3_TextChanged(object sender, EventArgs e) { textBox1.Text = EncryptionHelper.EncryptAndEncode(textBox3.Text); textBox2.Text = EncryptionHelper.DecodeAndDecrypt(textBox1.Text); } } public static class EncryptionHelper { private static int BlockSize = 16; private static byte[] Key { get { byte[] hash = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes("BlahBlahBlah")); byte[] key = new byte[BlockSize]; Array.Copy(hash, 0, key, 0, BlockSize); return key; } } private static byte[] IV { get { StringBuilder builder = new StringBuilder(); Random random = new Random(); for (int i = 0; i < BlockSize; i++) { char ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))); builder.Append(ch); } return Encoding.UTF8.GetBytes(builder.ToString()); } } public static string DecodeAndDecrypt(string cipherText) { byte[] cipherBytes = Convert.FromBase64String(cipherText); string decodeAndDecrypt = AesDecrypt(cipherBytes); return decodeAndDecrypt; } public static string EncryptAndEncode(string plaintext) { return Convert.ToBase64String(AesEncrypt(plaintext)); } public static string AesDecrypt(Byte[] inputBytes) { Byte[] outputBytes = inputBytes; string plaintext = string.Empty; using (MemoryStream memoryStream = new MemoryStream(outputBytes)) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateDecryptor(Key, IV), CryptoStreamMode.Read)) { using (BinaryReader srDecrypt = new BinaryReader(cryptoStream)) { outputBytes = srDecrypt.ReadBytes(inputBytes.Length); plaintext = Encoding.UTF8.GetString(outputBytes); } } } return plaintext; } public static byte[] AesEncrypt(string inputText) { byte[] inputBytes = UTF8Encoding.UTF8.GetBytes(inputText); byte[] result = null; using (MemoryStream memoryStream = new MemoryStream()) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, GetCryptoAlgorithm().CreateEncryptor(Key, IV), CryptoStreamMode.Write)) { cryptoStream.Write(inputBytes, 0, inputBytes.Length); cryptoStream.FlushFinalBlock(); result = memoryStream.ToArray(); } } return result; } private static RijndaelManaged GetCryptoAlgorithm() { RijndaelManaged algorithm = new RijndaelManaged(); algorithm.Padding = PaddingMode.PKCS7; algorithm.Mode = CipherMode.CBC; algorithm.KeySize = 128; algorithm.BlockSize = 128; return algorithm; } } } 

上面的注释(在问题上)最重要的一点是:你需要在加密时使用与解密相同的IV ,否则你的前16个字节的恢复明文将被删除。 (CBC模式将在第一个块后自动更正。)

您可以在发送之前简单地将IV与密文组合,然后在解密之前将它们分开在另一端。

IV是公共知识并不重要,只要它是加密强,随机数据,每个加密都不同。 但是你需要在另一端使用相同的相应的个人IV进行解密。

此外,正如WDS指出的那样,您不应该执行该XOR操作。 (但我无法看到WDS的代码如何在不保留IV的情况下恢复前16个字节。)

此外,恕我直言,使用SHA1而不是密码来生成密钥是一种安全风险。 您应该使用PBKDF2或类似的方法来获取密码。

编辑:正如Artjom B.指出的那样,由于您的原始代码XOR是针对第一个明文块的IV,您可以通过在解密时使用所有0x00字节的IV来恢复前16个字节。 但这只有在密文至少是两个块(32个字节)时才有效,否则,你会得到填充错误,因为这会破坏填充。 尝试在解密时使用所有零字节的IV,并查明输入是否始终至少为16个字节 – 如果不是加密,那么无论如何你都有一个非最终的数组错误。 (你仍然会有一个不安全的IV,但至少你会解密工作。)