在使用块中使用PasswordDeriveBytes类(由于它实现了IDisposable,因此它处理它)会在第二次使用该类时产生问题。这是代码:
public class AES
{
protected static CryptoData localCryptoData;
static AES()
{
localCryptoData = new CryptoData();
}
public static string Encrypt(CryptoData cryptoData)
{
using (PasswordDeriveBytes pass = new PasswordDeriveBytes(cryptoData.Password, cryptoData.Salt, "SHA1", 2))
using (RijndaelManaged symmetricKey = new RijndaelManaged())
{
byte[] keyBytes = pass.GetBytes(cryptoData.KeySize / 8);
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, cryptoData.InitVector))
using (MemoryStream memoryStream = new MemoryStream())
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(cryptoData.ByteText, 0, cryptoData.ByteText.Length);
cryptoStream.FlushFinalBlock();
return Convert.ToBase64String(memoryStream.ToArray());
}
}
}
public static string Decrypt(CryptoData cryptoData)
{
using (PasswordDeriveBytes pass = new PasswordDeriveBytes(cryptoData.Password, cryptoData.Salt, "SHA1", 2))
using (RijndaelManaged symmetricKey = new RijndaelManaged())
{
byte[] cipherTextBytes = Convert.FromBase64String(cryptoData.Text);
byte[] keyBytes = pass.GetBytes(cryptoData.KeySize / 8);
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, cryptoData.InitVector))
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] textBytes = new byte[cipherTextBytes.Length];
int count = cryptoStream.Read(textBytes, 0, textBytes.Length); //throws CryptographicException - Padding is invalid and cannot be removed.
return Encoding.UTF8.GetString(textBytes, 0, count);
}
}
}
如果以这种方式使用此类:
AES.Encrypt(cryptoData); AES.Decrypt(cryptoData);
首次使用会为您提供正确的AES加密字符串,但如果在尝试解密相同字符串时失败并出现异常。问题在于,当通过字节数组给出此密码时,从PasswordDeriveBytes类中分配第一个参数(用于从中派生密钥的密码)。如果它是一个字符串(由于过载),它可以正常工作。
帮助程序CryptoData类:
public class CryptoData
{
private string text;
public string Text
{
get { return text; }
set
{
text = value;
if (value != null)
{
ByteText = Encoding.ASCII.GetBytes(value);
}
else
{
ByteText = null;
}
}
}
public byte[] ByteText { get; private set; }
public byte[] Password { get; set; }
public int KeySize { get; set; }
public byte[] InitVector { get; set; }
public byte[] Salt { get; set; }
}
如果您只是在方法中更改此行:
using (PasswordDeriveBytes pass = new PasswordDeriveBytes(cryptoData.Password,
cryptoData.Salt, "SHA1", 2))
到
using (PasswordDeriveBytes pass = new PasswordDeriveBytes("somePassword",
cryptoData.Salt, "SHA1", 2))
一切正常。问题是,由于using语句,PasswordDeriveBytes的实例在第二次使用时没有得到密码的字节数组。如果传递了一个字符串,而不是一个字节数组,它就可以工作。
编辑:仔细检查后,似乎密码参数的默认属性设置器中存在问题。它获取数组的指针,这就是它处理它的原因。它应该创建数组的value.clone(),就像salt数组的情况一样。这是一个明确的错误。
我是对的,还是我做错了什么?
修改:
* 使用此方法更改AES.Encrypt()和AES.Decrypt方法中的第一行,它可以正常工作:*
using (PasswordDeriveBytes pass = new PasswordDeriveBytes(
(byte[])cryptoData.Password.Clone(),
cryptoData.Salt, "SHA1", 2))
答案 0 :(得分:1)
这肯定是反直觉和无证件的行为,虽然它是否是一个bug可能会被辩论。基本上,当您将密码字节数组传递给构造函数时,PasswordDeriveBytes实例将获得该数组的所有权。这类似于StreamReader获取传递给它的Stream的所有权的方式,并且当它被Disposed时将它Dispose它(这种行为也是criticized基于相似的理由,这导致添加一个布尔参数.NET 4.0中的StreamReader构造函数,可以防止底层流被处置。
在传递之前克隆字节数组可能是最佳选择。