简体   繁体   中英

AES Padding is Invalid And Cannot Be Removed

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.

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; .

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.

My Constructor:

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);
}

My encrption code:

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;
    }
}

My decryption code:

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;
    }
}

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.

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

Example

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;
    }
}

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


Alternative

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();
}

Usage:

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM