使用RijndaelManaged在C#中加密/解密流



我正在尝试用C#加密泛型流。虽然程序没有问题,但当转换为字符串时,加密和解密返回空白。感谢您的帮助。

public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 10000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
private void Encrypt(Stream input, Stream output, String password)
{
input.Position = 0;
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
using (var stream = new MemoryStream())
{
byte[] buffer = new byte[2048]; // read in chunks of 2KB
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, bytesRead);
var tmp = AES_Encrypt(buffer, passwordBytes);
output.Write(tmp, 0, tmp.Length);
}
}
}
private void Decrypt(Stream input, Stream output, String password)
{
input.Position = 0;
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
using (var stream = new MemoryStream())
{
byte[] buffer = new byte[2048]; // read in chunks of 2KB
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, bytesRead);
var tmp = AES_Decrypt(buffer, passwordBytes);
output.Write(tmp, 0, tmp.Length);
}
}
}
static void Main(string[] args)
{
Program obj = new Program();
var message = new MemoryStream();
var cipher = new MemoryStream();
string tmp = "This is a test if the encryption is working!";
StreamWriter sw = new StreamWriter(message);
sw.Write(tmp);
obj.Encrypt(message, cipher, "password");
cipher.Position = 0;
message = new MemoryStream();
obj.Decrypt(cipher, message, "password");
using (var memoryStream = new MemoryStream())
{
message.CopyTo(memoryStream);
var bytesdecrypt = memoryStream.ToArray();
string result = Encoding.UTF8.GetString(bytesdecrypt);
Console.WriteLine(result);
Console.ReadLine();
}

}
}
}

问题可能是当我在溪流中阅读和写作时。

这段代码有很多问题。

  1. 什么都不解密的原因是在执行message.CopyTo(memoryStream)之前忘记重置message流,因为CopyTo从当前位置开始工作,并且解密后您没有更改位置。

    你可以用重置它

    message.Position = 0;
    
  2. 如果对任意数据进行加密,那么仅仅使用某种操作模式(如CBC)的AES是不够的。我们通常需要某种填充方案。在C#中,默认的方案是PKCS#7填充。即使明文已经是块大小的倍数,也总是添加不明确的填充。在这些情况下,会添加一个完整的填充块。

    现在的问题是,你在加密和解密过程中读取2048字节的块,但加密产生2064字节的密文块,在解密过程中必须这样读取。这是一个简单的修复方法,但最好始终使用流,而不是加密这些单独的块。

  3. 您正在为每个2048字节的块调用Rfc2898DeriveBytes,但它永远不会改变。要么引入随机性,但实际上使用随机salt和随机IV,要么缓存密钥。(随机salt和随机IV仍然是达到语义安全所必需的)