我有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;
}
}
答案 0 :(得分:1)
我做了一些更改,并将在下面发布一些源代码。要使其工作,只需在Form1上放置3个textBox并将此代码用于Form1.cs。我在上面提到过,我认为一个主要问题是如何在不指定编码的情况下将StreamReader作为字符串读取。我的更改使用BinaryReader作为字节数组读取,然后在读取后转换为UTF-8字符串。我也删除了那个XOR循环。我认为你试图自己实现CBC(CBC是一种XOR的反馈循环)但不需要 - 当你指定CBC模式时,.NET会为你做CBC模式。为了让我的版本尽可能接近您的版本,我没有做出更多更改。但请记住以上一些评论。例如,当.NET提供DeriveBytes函数时,不要使用简单的哈希。像纸巾一样处理静脉注射 - 只使用一次,使用一次。等等。无论如何,如果没有&#34;最佳实践&#34;代码如下:
编辑:抱歉,我忘了提及我还将纯文本类型更改为常规字符串,删除了与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;
}
}
}
答案 1 :(得分:1)
上面的评论(关于这个问题)最重要的一点是:你需要在加密上使用解密时使用相同的IV,否则你的前16个字节的恢复明文将被删除。 (CBC模式将在第一个块后自动更正。)
您可以在发送之前简单地将IV与密文结合起来,然后在解密之前将它们分开在另一端。
如果IV是公共知识,只要它是加密强,随机数据,每个加密都不同,这没关系。但是你需要相同的相应的,另一端的个人IV才能解密。
此外,正如WDS指出的那样,您不应该执行该XOR操作。 (但我无法看到WDS的代码如何在不保留IV的情况下恢复前16个字节。)
此外,恕我直言,使用SHA1而不是密码来生成密钥是一种安全风险。您应该使用PBKDF2或类似的方法来获取密码。
编辑:正如Artjom B.指出的那样,由于您的原始代码XOR是针对第一个明文块的IV,您可以通过在解密时使用所有0x00字节的IV来恢复前16个字节。但这只有在密文至少是两个块(32个字节)时才有效,否则,你会得到填充错误,因为这会破坏填充。尝试在解密时使用所有零字节的IV,并查明输入是否始终至少为16个字节 - 如果它不是&#39,那么无论如何你在加密中都有一个非现成的数组错误。 ;吨。 (你还会有一个不安全的IV,但至少你会解密工作。)