AES 填充无效且无法删除 [英] AES Padding is Invalid And Cannot Be Removed

查看:40
本文介绍了AES 填充无效且无法删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 AES 加密算法来加密和解密我的项目中的值.我的代码几乎每次都有效,但有时我会收到 Padding is invalid and cannot be removed 错误.我的项目是 ASP .NET Core 3.1 项目,它发布在 IIS Server 8.5 上.

I am using AES criptography algorithms to encrypt and decrypt my values in my project. My code works almost everytime but sometimes I get Padding is invalid and cannot be removed error. My project is ASP .NET Core 3.1 project and it's published on IIS Server 8.5.

填充无效且无法删除?9 年前提出的问题,我的密钥和盐始终设置为 128 位,填充模式始终设置为 PKCS#7,如下代码:aes.Padding = PaddingMode.PKCS7;.

As said at Padding is invalid and cannot be removed? question asked 9 years ago, my keys and salts are always set 128 bits and padding mode is always set to PKCS#7 like this code: aes.Padding = PaddingMode.PKCS7;.

但有时,我会收到此错误.在使用 相同 密钥、盐和解密值调试我的代码后,我没有收到任何错误,我的代码又可以正常工作 10 个小时左右.我不知道为什么我的代码会这样,但我找不到任何解决方案.

But sometimes, I got this error. After debugging my code with the same key, salt and decrypted value I didn't get any error and my code works fine for another 10 hours or so. I have no idea why my code behaves like this but I couldn't find any solution.

我的构造函数:

public void KriptoAlgoritmasiniAyarla(string password, string salt, SymmetricAlgorithm algorithm)
{
    if (password == null) throw new ArgumentNullException(nameof(password));
    if (salt == null) throw new ArgumentNullException(nameof(salt));

    DeriveBytes rgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));

    var rgbKey = rgb.GetBytes(algorithm.KeySize >> 3);
    var rgbIv = rgb.GetBytes(algorithm.BlockSize >> 3);

    _sifreleyici = algorithm.CreateEncryptor(rgbKey, rgbIv);
    _desifreleyici = algorithm.CreateDecryptor(rgbKey, rgbIv);
}

我的加密代码:

public byte[] ByteDizisineSifrele(string plainText)
{
    try
    {

        byte[] encrypted;
        // Create a new AesManaged.    
        using (AesManaged aes = new AesManaged())
        {
            aes.Padding = PaddingMode.PKCS7;
            // Create MemoryStream    
            using (MemoryStream ms = new MemoryStream())
            {
                // Create crypto stream using the CryptoStream class. This class is the key to encryption    
                // and encrypts and decrypts data from any given stream. In this case, we will pass a memory stream    
                // to encrypt    
                using (CryptoStream cs = new CryptoStream(ms, _sifreleyici, CryptoStreamMode.Write))
                {
                    // Create StreamWriter and write data to a stream    
                    using (StreamWriter sw = new StreamWriter(cs))
                        sw.Write(plainText);
                    encrypted = ms.ToArray();
                }
            }
        }
        // Return encrypted data    
        return encrypted;
    }
    catch (Exception exp)
    {
        throw exp;
    }
}

我的解密代码:

public string ByteDizisiDesifreEt(byte[] cipherText)
{
    try
    {
        string plaintext = null;
        // Create AesManaged    
        using (AesManaged aes = new AesManaged())
        {
            aes.Padding = PaddingMode.PKCS7;
            // Create the streams used for decryption.    
            using (MemoryStream ms = new MemoryStream(cipherText))
            {
                // Create crypto stream    
                using (CryptoStream cs = new CryptoStream(ms, _desifreleyici, CryptoStreamMode.Read))
                {
                    // Read crypto stream    
                    using (StreamReader reader = new StreamReader(cs))
                        plaintext = reader.ReadToEnd();
                }
            }
        }
        return plaintext;
    }
    catch (Exception exp)
    {
        throw exp;
    }
}

推荐答案

可能是因为您正在重复使用相同的 ICryptoTransform 对象(_sifreleyici_desifreleyici>).在某些时候,转换对象不能再被重用,因此接口有一个属性来确定.ICryptoTransform.CanReuseTransform 属性.

Probably because you are reusing the same ICryptoTransform objects (_sifreleyici and _desifreleyici). At some point, the transform object can't be reused anymore and therefore the interface has a property to determine that. The ICryptoTransform.CanReuseTransform property.

因此,您需要检查此属性并在获得 false 时重新创建对象.

Consequently, you need to check this property and recreate the objects when you get false.

示例

private readonly byte[] Key, IV;

public void KriptoAlgoritmasiniAyarla(
    string password, 
    string salt,
    SymmetricAlgorithm algorithm)
{
    // ...

    Key = // Get the key..
    IV =  // Get the IV..
}

private ICryptoTransform encryptor;
private ICryptoTransform Encryptor
{
    get
    {
        if (encryptor == null || !encryptor.CanReuseTransform)
        {
            encryptor?.Dispose();
            encryptor = Algorithm.CreateEncryptor(Key, IV);
        }
        return encryptor;
    }
}

private ICryptoTransform decryptor;
private ICryptoTransform Decryptor
{
    get
    {
        if (decryptor == null || !decryptor.CanReuseTransform)
        {
            decryptor?.Dispose();
            decryptor = Algorithm.CreateDecryptor(Key, IV);
        }
        return decryptor;
    }
}

然后在相关方法中使用这两个属性来创建CryptoStream.

Then use these two properties in the related methods to create the CryptoStream.

替代方案

我想提出以下代码作为替代方案,可用于从 SymmetricAlgorithm 抽象类.

I'd like to propose the code below as an alternative that can be used with the classes that derive from the SymmetricAlgorithm abstract class.

public class SymmetricCrypto<T> : IDisposable where T : SymmetricAlgorithm, new()
{
    private readonly T Algorithm = new T();

    public SymmetricCrypto()
    {
        Algorithm.GenerateKey();
        Algorithm.GenerateIV();
    }

    public SymmetricCrypto(byte[] key, byte[] iv)
    {
        Algorithm.Key = key;
        Algorithm.IV = iv;
    }

    public SymmetricCrypto(string pass)
    {
        var bytes = Encoding.UTF8.GetBytes(pass);
        var rfc = new Rfc2898DeriveBytes(pass, 
            new SHA256Managed().ComputeHash(bytes), 1000);

        Algorithm.Key = rfc.GetBytes(Algorithm.LegalKeySizes[0].MaxSize / 8);
        Algorithm.IV = rfc.GetBytes(Algorithm.LegalBlockSizes[0].MinSize / 8);
    }

    public SymmetricCrypto(byte[] pass)
    {
        var rfc = new Rfc2898DeriveBytes(pass, 
            new SHA256Managed().ComputeHash(pass), 1000);

        Algorithm.Key = rfc.GetBytes(Algorithm.LegalKeySizes[0].MaxSize / 8);
        Algorithm.IV = rfc.GetBytes(Algorithm.LegalBlockSizes[0].MinSize / 8);
    }

    public byte[] Encrypt(string input) =>
        Transform(Encoding.UTF8.GetBytes(input), Algorithm.CreateEncryptor());

    public string Decrypt(byte[] input) =>
        Encoding.UTF8.GetString(Transform(input, Algorithm.CreateDecryptor()));

    private byte[] Transform(byte[] input, ICryptoTransform cryptoTrans)
    {
        using (var ms = new MemoryStream())
        using (var cs = new CryptoStream(ms, cryptoTrans, CryptoStreamMode.Write))
        {
            cs.Write(input, 0, input.Length);
            cs.FlushFinalBlock();

            return ms.ToArray();
        }
    }

    public void Dispose() => Algorithm.Dispose();
}

用法:

void SomeCaller()
{
    using (var crypt = new SymmetricCrypto<AesManaged>("password"))
    {
        var bytes = crypt.Encrypt("Plain Text....");
        // ...

        var plainText = crypt.Decrypt(bytes);
        // ...
    }
}

这篇关于AES 填充无效且无法删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆