System.Security.Cryptography.CryptographicException : Bad PKCS7 padding. Invalid length {x}

Hi guys!
I’m playing with encryption/decryption and having the same error while deciphering the message no matter what I’m doing (tried to change some code with what Google gives me for that problem, tried to switch to another ciphering algorithm etc), just the number {x} is different every time. Here’s my code;

public class EncryptionUtil {
    private static int KEY_SIZE = 256;
    private static string KEY = "OklrgxUOBGIILOOzy6NpT2BWjkPSwFDw";
    private static byte[] KEY_BYTES = Encoding.UTF8.GetBytes(KEY);


    public static string Encrypt(string input) {
        using (Rijndael aes = Rijndael.Create()) {
            aes.Key = KEY_BYTES;
            aes.KeySize = KEY_SIZE;
            aes.BlockSize = 128;
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.CBC;
            aes.GenerateIV();
            byte[] initializationVector = aes.IV;

            using (var encrypter = aes.CreateEncryptor(aes.Key, aes.IV))
            using (var cipherStream = new MemoryStream()) {
                using (var tCryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
                using (var tBinaryWriter = new BinaryWriter(tCryptoStream)) {
                    cipherStream.Write(initializationVector, 0, initializationVector.Length);
                    tBinaryWriter.Write(input);
                    tCryptoStream.FlushFinalBlock();
                }
                byte[] result_b = cipherStream.ToArray();
                string result = Convert.ToBase64String(result_b);
                return result;
            }
        }
    }

    public static string Decrypt(string input) {
        using (Rijndael aes = Rijndael.Create()) {
            aes.Key = KEY_BYTES;
            aes.KeySize = KEY_SIZE;
            aes.BlockSize = 128;
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.CBC;

            byte[] inputBytes = Convert.FromBase64String(input);

            using (var decipherStream = new MemoryStream(inputBytes)) {
                byte[] initializationVector = new byte[16];
                decipherStream.Read(initializationVector, 0, 16);
                aes.IV = initializationVector;

                using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV)) {
                    using (var cs = new CryptoStream(decipherStream, decryptor, CryptoStreamMode.Read)) {
                        using (var sr = new StreamReader(cs)) {
                            return sr.ReadToEnd();
                        }
                    }
                }
            }
        }
    }
}

Main thing that I’m writing init vector along with result string when encrypting. Then, when decrypting - I’m reading this IV first (first 16 bytes) then reading the rest of the encrypted message.
Here’s how I’m testing:

class EncryptionUtilTest {

    private const string INPUT = "This is some test JSON {data : \"value\", object: {data : \"value\"}}"; 

    [Test]
    public void EncryptionAndDecryptionMatch() {
        string encryptedInput = EncryptionUtil.Encrypt(INPUT);

        string decryptedInput = EncryptionUtil.Decrypt(encryptedInput);

        Assert.AreEqual(decryptedInput, INPUT, "Original and decrypted inputs do not match");
    }
}

Here’s my stacktrace :

System.Security.Cryptography.CryptographicException : Bad PKCS7 padding. Invalid length 105.
---
at Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException (PaddingMode padding, Int32 length, Int32 position) [0x0005c] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/Mono.Security.Cryptography/SymmetricTransform.cs:363
at Mono.Security.Cryptography.SymmetricTransform.FinalDecrypt (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) [0x001a3] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/Mono.Security.Cryptography/SymmetricTransform.cs:515
at Mono.Security.Cryptography.SymmetricTransform.TransformFinalBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) [0x00034] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/Mono.Security.Cryptography/SymmetricTransform.cs:554
at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) [0x00000] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Security.Cryptography/RijndaelManagedTransform.cs:94
at System.Security.Cryptography.CryptoStream.Read (System.Byte[] buffer, Int32 offset, Int32 count) [0x00240] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Security.Cryptography/CryptoStream.cs:205
at System.IO.StreamReader.ReadBuffer () [0x00012] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.IO/StreamReader.cs:338
at System.IO.StreamReader.Read (System.Char[] buffer, Int32 index, Int32 count) [0x00077] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.IO/StreamReader.cs:415
at System.IO.StreamReader.ReadToEnd () [0x00040] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.IO/StreamReader.cs:519
at EncryptionUtil.Decrypt (System.String input) [0x00085] in D:\My_documents	runk\Assets\Scripts\utils\EncryptionUtil.cs:58
at EncryptionUtilTest.EncryptionAndDecryptionMatch () [0x0000b] in D:\My_documents	runk\Assets\Editor\Tests\EncryptionUtilTest.cs:16

Can someone , please, help me to get it work?

After about a week of explorations I’ve found the way to make it work. There were few things I’ve changed:

  • Left all algorithm parameters default, except for the key (not sure if it affects anything, just to keep code smaller).
  • Removed the call to CryptoStream.FlushFinalBlock() in the EncryptionUtil.Encrypt method. As I understand, this was the root cause of all troubles. After that I’ve stopped getting any exceptions and got decrypted text almost like the original one. Except it always had an “A” letter in the beginning of the string :slight_smile: Like if I have an original string “Some string to encrypt” then after 1 pass of encryption\decryption I’m getting “ASome string to encrypt”.
  • This problem was solved by replacing BinaryWriter in Encrypt method with StreamWriter.

So, If someone is interested in ready-to-use solution, the final code is:

public class EncryptionUtil {
    private static string KEY = "kPSwFT2IILOOzUOB6NpGgxwDyrBWjklO"; // pick some other 32 chars
    private static byte[] KEY_BYTES = Encoding.UTF8.GetBytes(KEY);


    public static string Encrypt(string plainText) {
        // Check arguments.
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText");

        byte[] encrypted;
        // Create an AesManaged object
        // with the specified key and IV.
        using (Rijndael algorithm = Rijndael.Create()) {
            algorithm.Key = KEY_BYTES;

            // Create a decrytor to perform the stream transform.
            var encryptor = algorithm.CreateEncryptor(algorithm.Key, algorithm.IV);

            // Create the streams used for encryption.
            using (var msEncrypt = new MemoryStream()) {
                using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
                    using (var swEncrypt = new StreamWriter(csEncrypt)) {
                        // Write IV first
                        msEncrypt.Write(algorithm.IV, 0, algorithm.IV.Length);
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }

        // Return the encrypted bytes from the memory stream.
        return Convert.ToBase64String(encrypted);
    }

    public static string Decrypt(string cipherText) {
        // Check arguments.
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText");

        // Declare the string used to hold
        // the decrypted text.
        string plaintext = null;

        // Create an AesManaged object
        // with the specified key and IV.
        using (Rijndael algorithm = Rijndael.Create()) {
            algorithm.Key = KEY_BYTES;

            // Get bytes from input string
            byte[] cipherBytes = Convert.FromBase64String(cipherText);

            // Create the streams used for decryption.
            using (MemoryStream msDecrypt = new MemoryStream(cipherBytes)) {
                // Read IV first
                byte[] IV = new byte[16];
                msDecrypt.Read(IV, 0, IV.Length);

                // Assign IV to an algorithm
                algorithm.IV = IV;

                // Create a decrytor to perform the stream transform.
                var decryptor = algorithm.CreateDecryptor(algorithm.Key, algorithm.IV);

                using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) {
                    using (var srDecrypt = new StreamReader(csDecrypt)) {
                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }
        }
        return plaintext;
    }
}

aes.KeySize = KEY_SIZE causes a new random key being generated. Furthermore, in the first code aes.KeySize = KEY_SIZE is called after aes.Key = KEY_BYTES. Both result in the key being randomly re-generated, so that KEY_BYTES is never used as a key. This applies to both encryption and decryption, thus both keys are different, which causes the error. If the line aes.KeySize = KEY_SIZE is commented out in Encrypt and Decrypt, the code basically works (the problem with the preceding A is still there and is solved as described by replacing BinaryWriter with StreamWriter; for this purpose FlushFinalBlock must be removed).

Moreover, the unchanged first code doesn’t generate the posted stacktrace, but: System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed (under various Mono implementations from version 4.2.2 to 6.0.0). Since this post was created about 3.5 years ago, a bug can’t be excluded of course, which may have been fixed in the meantime, so that the original behavior can’t be reproduced anymore.