Click here to monitor SSC

Oh no! My padding’s invalid!

Published 28 February 2012 12:33 pm

Recently, I’ve been doing some work involving cryptography, and encountered the standard .NET CryptographicException: ‘Padding is invalid and cannot be removed.’ Searching on StackOverflow produces 57 questions concerning this exception; it’s a very common problem encountered. So I decided to have a closer look.

To test this, I created a simple project that decrypts and encrypts a byte array:

// create some random data
byte[] data = new byte[100];
new Random().NextBytes(data);

// use the Rijndael symmetric algorithm
RijndaelManaged rij = new RijndaelManaged();
byte[] encrypted;

// encrypt the data using a CryptoStream
using (var encryptor = rij.CreateEncryptor())
using (MemoryStream encryptedStream = new MemoryStream())
using (CryptoStream crypto = new CryptoStream(
    encryptedStream, encryptor, CryptoStreamMode.Write))
{
    crypto.Write(data, 0, data.Length);
    encrypted = encryptedStream.ToArray();
}

byte[] decrypted;

// and decrypt it again
using (var decryptor = rij.CreateDecryptor())
using (CryptoStream crypto = new CryptoStream(
    new MemoryStream(encrypted), decryptor, CryptoStreamMode.Read))
{
    byte[] decrypted = new byte[data.Length];
    crypto.Read(decrypted, 0, decrypted.Length);
}

Sure enough, I got exactly the same CryptographicException when trying to decrypt the data even in this simple example. Well, I’m obviously missing something, if I can’t even get this single method right! What does the exception message actually mean? What am I missing?

Well, after playing around a bit, I discovered the problem was fixed by changing the encryption step to this:

// encrypt the data using a CryptoStream
using (var encryptor = rij.CreateEncryptor())
using (MemoryStream encryptedStream = new MemoryStream())
{
    using (CryptoStream crypto = new CryptoStream(
        encryptedStream, encryptor, CryptoStreamMode.Write))
    {
        crypto.Write(data, 0, data.Length);
    }
    encrypted = encryptedStream.ToArray();
}

Aaaah, so that’s what the problem was. The CryptoStream wasn’t flushing all it’s data to the MemoryStream before it was being read, and closing the stream causes it to flush everything to the backing stream. But why does this cause an error in padding?

Cryptographic padding

All symmetric encryption algorithms (of which Rijndael is one) operates on fixed block sizes. For Rijndael, the default block size is 16 bytes. This means the input needs to be a multiple of 16 bytes long. If it isn’t, then the input is padded to 16 bytes using one of the padding modes. This is only done to the final block of data to be encrypted.

CryptoStream has a special method to flush this final block of data – FlushFinalBlock. Calling Stream.Flush() does not flush the final block, as you might expect. Only by closing the stream or explicitly calling FlushFinalBlock is the final block, with any padding, encrypted and written to the backing stream. Without this call, the encrypted data is 16 bytes shorter than it should be.

If this final block wasn’t written, then the decryption gets to the final 16 bytes of the encrypted data and tries to decrypt it as the final block with padding. The end bytes don’t match the padding scheme it’s been told to use, therefore it throws an exception stating what is wrong – what the decryptor expects to be padding actually isn’t, and so can’t be removed from the stream.

So, as well as closing the stream before reading the result, an alternative fix to my encryption code is the following:

// encrypt the data using a CryptoStream
using (var encryptor = rij.CreateEncryptor())
using (MemoryStream encryptedStream = new MemoryStream())
using (CryptoStream crypto = new CryptoStream(
    encryptedStream, encryptor, CryptoStreamMode.Write))
{
    crypto.Write(data, 0, data.Length);

    // explicitly flush the final block of data
    crypto.FlushFinalBlock();

    encrypted = encryptedStream.ToArray();
}

Conclusion

So, if your padding is invalid, make sure that you close or call FlushFinalBlock on any CryptoStream performing encryption before you access the encrypted data. Flush isn’t enough. Only then will the final block be present in the encrypted data, allowing it to be decrypted successfully.

5 Responses to “Oh no! My padding’s invalid!”

  1. hitesh_hpk says:

    Hey Cooper

    i appreciate your code is working well but i have got same error when change my code to this

    UTF8Encoding utf8 = new UTF8Encoding(false);

    string sResult = null;
    byte[] byPassword = new byte[sPASSWORD.Length];
    byPassword = Convert.FromBase64String(sPASSWORD);

    using (MemoryStream stm = new MemoryStream())
    {
    Rijndael rij = Rijndael.Create();
    rij.Key = gKEY.ToByteArray();
    rij.IV = gIV.ToByteArray();
    using (var decryptor = rij.CreateDecryptor())
    using (CryptoStream cs = new CryptoStream(stm, decryptor, CryptoStreamMode.Write))
    {
    cs.Write(byPassword, 0, byPassword.Length);
    //cs.Flush();
    cs.FlushFinalBlock();
    cs.Close();
    }
    byte[] byResult = stm.ToArray();
    sResult = utf8.GetString(byResult, 0, byResult.Length);
    }

    can you give me any suggestion?

    Thanks

  2. Swevo says:

    When you encrypt the data are you using:

    Convert.ToBase64String

    to convert the byte array to sPASSWORD?

  3. Swevo says:

    Thanks for the article Simon.

  4. stalks says:

    Hi,
    Your explain is very good.But I can’t find my error in following code.Can you show me where it?

    public RijndaelManaged GetRijndaelManaged(String secretKey)
    {
    var keyBytes = new byte[16];
    var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
    Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length));
    return new RijndaelManaged
    {
    Mode = CipherMode.CBC,
    Padding = PaddingMode.PKCS7,
    KeySize = 128,
    BlockSize = 128,
    Key = keyBytes,
    IV = keyBytes
    };
    }
    public byte[] Encrypt(byte[] plainBytes, RijndaelManaged rijndaelManaged)
    {
    return rijndaelManaged.CreateEncryptor()
    .TransformFinalBlock(plainBytes, 0, plainBytes.Length);
    }

    public byte[] Decrypt(byte[] encryptedData, RijndaelManaged rijndaelManaged)
    {
    return rijndaelManaged.CreateDecryptor()
    .TransformFinalBlock(encryptedData, 0, encryptedData.Length);
    }
    public String Encrypt(String plainText, String key)
    {
    var plainBytes = Encoding.UTF8.GetBytes(plainText);
    return Convert.ToBase64String(Encrypt(plainBytes, GetRijndaelManaged(key)));
    }
    public String Decrypt(String encryptedText, String key)
    {
    var encryptedBytes = Convert.FromBase64String(encryptedText);
    return Encoding.UTF8.GetString(Decrypt(encryptedBytes, GetRijndaelManaged(key)));
    }
    Thanks

  5. smalls says:

    This was extremely helpful. Thanks

Leave a Reply