Search code examples
c#cryptographyaes

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

Solution

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