【发布时间】:2012-03-08 06:54:57
【问题描述】:
如果第二次使用该类,则在 using 块中使用 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 类中分配第一个参数(从中派生密钥的密码),而该密码是通过 Byte 数组给出的。如果它作为一个字符串给出(因为过载)它工作正常。
助手 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))
一切正常。问题是,PasswordDeriveBytes 的实例没有得到第二次使用密码的字节数组,因为 using 语句。如果传递的是字符串,而不是字节数组,则它可以工作。
编辑:仔细查看后,似乎密码参数的默认属性设置器存在问题。它获取数组的指针,这就是它处理它的原因。它应该创建数组的 value.clone() ,就像 salt 数组一样。这是一个明确的错误。
我是对的,还是我做错了什么?
编辑:
*用这个改变 AES.Encrypt() 和 AES.Decrypt 方法的第一行就可以了:*
using (PasswordDeriveBytes pass = new PasswordDeriveBytes(
(byte[])cryptoData.Password.Clone(),
cryptoData.Salt, "SHA1", 2))
【问题讨论】:
-
请阅读并理解代码。没有人会两次使用同一个对象。有不同的方法。
-
您能发布异常详细信息,包括堆栈跟踪吗?
-
你是对的,我可以验证这一点。很奇怪。
-
@Ramhou - 你完全偏离了轨道。当数组包含值类型(字节)时,没有浅/深复制。解决方法只是使用要使用和归零的密码数组的副本。
-
请注意,
PasswordDeriveBytes有很多很多问题。您应该改用Rfc2898DeriveBytes。如果你使用它,最多使用 20 个字节。您似乎还隐式生成了一个随机 IV,然后就忘了它。
标签: c# .net security cryptography idisposable