PasswordDeriveBytes(System.Security.Cryptographic)如果已释放,则会失败

本文关键字:释放 失败 如果 System Security Cryptographic PasswordDeriveBytes | 更新日期: 2023-09-27 18:27:03

如果第二次使用PasswordDeriveBytes类,则在using块中使用该类(因为它实现了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加密(cryptoData);AES.Decrypt(cryptoData);

第一次使用时会给您一个正确的AES加密字符串,但如果失败,则在尝试解密同一字符串时出现异常。问题在于通过Byte数组给定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的实例在第二次使用时没有获得密码的字节数组。如果传递了一个字符串,而不是字节数组,那么它就可以工作了。

编辑:仔细查看后,密码参数的默认属性setter似乎有问题。它获取数组的指针,这就是它处理它的原因。它应该为数组生成一个值.clone(),就像salt数组一样。这确实是个错误。

我是对的,还是做错了什么?

编辑:

*更改AES.Encrypt()和AES.Decrypt方法中的第一行,这样就可以了:*

 using (PasswordDeriveBytes pass = new PasswordDeriveBytes(
        (byte[])cryptoData.Password.Clone(), 
        cryptoData.Salt, "SHA1", 2))

PasswordDeriveBytes(System.Security.Cryptographic)如果已释放,则会失败

这当然是违反直觉和未记录的行为,尽管它是否是一个bug可能会引起争论。基本上,当您将密码字节数组传递给构造函数时,PasswordDeriveBytes实例将获得该数组的所有权。这类似于StreamReader获取传递给它的流的所有权,并在对其进行处置时对其进行处理的方式(这种行为也因类似的原因而受到批评,这导致在.NET 4.0中向StreamReader构造函数添加了一个布尔参数,该参数可以防止底层流被处置)。

在传入字节数组之前克隆它可能是您的最佳选择。