1

I get a run-time error Padding is invalid and cannot be removed while trying to decrypt some data. I have read all the similar threads I found on stackoverflow and considered the following issues:

  1. Given a different key for decryption than the one used for encryption might bring the above issue. However, as you can see in the source for testing purposes I hard-coded the keys and I still get a runtime error.

  2. The application may be trying to use a different padding mode for decryption than the one used for encryption. Setting the mode explicitly during the encryption or decryption process didn't solve the problem.

  3. Some say that you would need to use the FlushFinalBlock method so you can tell the application to put the final bits into the blocksize. Tried putting it below the Write method, didn't work either.

using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
    //Write all data to the stream.
    swEncrypt.Write(plainText);
    csEncrypt.FlushFinalBlock();
}

Below are the snippets of my code:

// Encrypt 
private byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
    Key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };
    IV = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };

    // Check arguments. 
    if (plainText == null || plainText.Length <= 0)
        throw new ArgumentNullException("plainText");
    if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
    if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");

    byte[] encrypted;

    // Create an RijndaelManaged object 
    // with the specified key and IV. 
    using (RijndaelManaged rijAlg = new RijndaelManaged())
    {
        rijAlg.Key = Key;
        rijAlg.IV = IV;
        rijAlg.Padding = PaddingMode.PKCS7;

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

        // Create the streams used for encryption. 
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                {
                    // Write all data to the stream.
                    swEncrypt.Write(plainText);
                    csEncrypt.FlushFinalBlock();
                }

                encrypted = msEncrypt.ToArray();
                MessageBox.Show("Encrypted str: " + Encoding.UTF8.GetString(encrypted).ToString());
            }
        }
    }

    // Update buffer length
    this.Buflen = encrypted.Length;
    MessageBox.Show("Byte count: " + (Encoding.UTF8.GetByteCount(encrypted.ToString())).ToString());
    MessageBox.Show("Decrypted: " + DecryptStringFromBytes(encrypted, this.Encryption_key, this.Encryption_iv));

    // Return the encrypted bytes from the memory stream. 
    return encrypted;
}

// Decrypt
private string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
{
    Key = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };
    IV = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };

    UTF8Encoding e = new UTF8Encoding();
    MessageBox.Show("ciperText " + cipherText.Length);
    //cipherText = e.GetBytes(cipherText);
    //MessageBox.Show("ciperText2 Length " + cipherText.Length);

    // Check arguments. 
    if (cipherText == null || cipherText.Length <= 0)
        throw new ArgumentNullException("cipherText");
    if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
    if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");

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

    // Create an RijndaelManaged object 
    // with the specified key and IV. 
    using (RijndaelManaged rijAlg = new RijndaelManaged())
    {
        rijAlg.Key = Key;
        rijAlg.IV = IV;
        rijAlg.Padding = PaddingMode.PKCS7;

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

        // Create the streams used for decryption. 
        using (MemoryStream msDecrypt = new MemoryStream(cipherText))
        {
            using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                {
                    // Read the decrypted bytes from the decrypting stream 
                    // and place them in a string.
                    if (cipherText.Length != rijAlg.BlockSize)
                    {
                        throw new InvalidOperationException("Invalid cipherText length");
                    }

                    plaintext = srDecrypt.ReadToEnd();

                    csDecrypt.FlushFinalBlock();
                }
            }
        }
    }

    return plaintext;
}

byte[] encoded_message = new byte[16];
encoded_message = EncryptStringToBytes("test", this.Encryption_key,this.Encryption_iv);

I'm trying to get this working since yesterday morning. None of the solutions I found so far worked in my application. I know the code may look a bit of a mess, but please never mind the parts which are not being used - once I get this working it will surely need a bit of refactoring.

As you can see, while using a hard-coded key and IV for testing purposes the run-time error still occurs so passing different credentials for decryption than the ones used for encryption is not the cause.

Can anyone come up with an idea on how to resolve this? Thanks.

Sam
  • 7,252
  • 16
  • 46
  • 65
Dex
  • 33
  • 9
  • 1
    Doesn't compile. You are abusing the stream layering by flushing the middle stream before flushing the outer stream. This looks like random code snippets glued together. Please take a little more time to post a coherent [SSCCE](http://sscce.org). – President James K. Polk Oct 06 '13 at 14:26
  • Hi, these are the examples from MSDN. Unfortunately the code's too long to post here. I already pasted the parts which contain the problem, so I do not think someone familiar with the issue would need anything else to look at. In case you need to compile you'll need to add the system libraries and call EncryptStringFromBytes from within the main function, by definning a new byte[] array and setting its buffer length to the relevant algorithm's rijndael blocksize, which is usually 16 – Dex Oct 06 '13 at 14:37
  • So you are too busy to post an SSCCE and too busy too read my comment? Well, then I'm too busy to look at your code any further. – President James K. Polk Oct 06 '13 at 14:41
  • Hi, I read your comment. Sorry If I sounded abrupt, this was not what I intended. I already trying moving it to the outer stream, still doesn't help. – Dex Oct 06 '13 at 14:45
  • http://stackoverflow.com/questions/19325743/c-sharp-sockets-and-sending-encrypted-data-the-proper-way-or-at-least-the-way – Dex Oct 11 '13 at 19:25

1 Answers1

0

I'm writing this thread for anyone who has tried to send encrypted data through sockets as I have been having trouble doing this over the last 3 days. As you know, encrypted data might contain padding characters and special symbols that, mixed with the way packets are sent through the socket (usually in binary form and in chunks), might not come out on the other side the way you expect. The errors that you will usually get are Padding is invalid and cannot be removed or *Length of data to decrypt is invalid *. I've come across this while writing an application which sends encrypted Rijndael credentials through a UDP socket. So usually what you sent is not what you get, a padding character is being added (or several like it) which results in bugos encrypted data which can not be recognized by the rijndael algorithm (please note that this may also result while trying to decrypt any other type of encryption and I suppose it actually does). So I'm giving you my solution below with the intention to help to anyone who's in the process of sending encrypted data and trying to do this correctly, (I'm not saying that this is the ONLY and this is the most proper way to do this, but this is the way that I found to work and that I personally used):

1) Having in mind that most encryption functions take a byte array as a parameter, and most socket functions for sending data also do so, first we get our encrypted string in bytes array form:

byte[] encrypted_string = new byte[lengthofblocksize]; // where lengthofblocksize would be the length in bytes of the blocksize for the used algorithm, for rijndael that usually is 16
//getting the encrypted byte array
encrypted_string = EncryptStringToBytes(messageInPlainText, key, iv); 

where messageInPlainText is your plain text message, key and iv are your key and iv parameters.

2) Before sending the data When sending the data through the socket, encrypt it into a base64 string. Suppose we store our string in the variable "textdata". The first step would be string encrypted_string_base64 = Convert.ToBase64String(textdata);

3) now you have the base64 string as string. What you need is to get it in bytes array, which can be done using the UTF8Encoding GetBytes method: encrypted_string = enc.GetBytes(encrypted_string_base64.ToString()); 4) and THEN you are ready to send the data through the socket so you can get a proper encoded binary data on the other side. sck.Send(encrypted_string);

5) So on the other side you get the string usually in binary data, let's suppose it's in the variable recvdata. What you need to do is to convert the byte array to a string:

 string recv_textdata = Convert.FromBase64String(recvdata);

However, before this you might need to create a method which clears out all unnecesssary padding and symbols:

 UTF8Encoding enc = new UTF8ENcoding();
 string recv_textdata = FormatStringAsBase64(enc.GetString(recvdata)).Trim();
(please scroll to the bottom in order to see the full definition of the above method)

6) What you have now is the encrypted data as a base64 string, which you are still not ready to pass to your decrypt method, as you need to decode it from base64 and convert it to bytes array, which can be done directly using the Convert.FromBase64String method (at least mine is taking a byte array as a parameter) - it takes a string as a parameter and returns a byte array as a result:

byte[] recv_textdata_encrypted_ready = new byte[lengthofblocksize]; //length of blocksize is 16 for rijndael as explained
recv_textdata_encrypted_ready = Convert.FromBase64String(recv_textdata);

7) NOW you are ready to pass your encrypted byte array for decryption: string decrypted = DecryptStringFromBytes(recv_textdata_encrypted_ready, this.Encryption_key, this.Encryption_iv);

So, back to the definition of FormatAsBase64String and why you need it. As you pass the base64 encoded string through the UDP socket, the datagram is sent in chunks and might add unnecessary padding, which usually can only be seen by comparing the md5 hash of the data you send through the socket and the data you receive through the socket, or by writing it to a binary file (this is the way I used) and opening the result using text editor supporting UTF8 Encoding (or any other encoding you used in point 5) ). So if you do not use the method below, the data you SEND might not be the same as the data you RECEIVE, which will result in a string with unnecessary padding ineligible for decryption through the .NET decryption libraries as they will not recognise it, or what happened in my case, the Convert.FromBase64String method will not recognize it as a valid base64 encoded string and will spit out an error that the string contains an invalid padding and is not in a valid base64 encoded format. So, to validate your string as a valid base64 only so you do not have trouble while convert it to the normally binary encrypted bytes array, you may need something like this:

    private static string FormatStringAsBase64(string message_encrypted_base64)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < message_encrypted_base64.Length; i++)
        {
            //current char
            char currchar = message_encrypted_base64[i];

            //check if we got a valid base64 symbol
            /* From Microsoft MSDN Official Convert.ToBase64String Method (http://msdn.microsoft.com/en-us/library/dhx0d524.aspx):
             * The base-64 digits in ascending order from zero are the uppercase 
             * characters "A" to "Z", the lowercase characters "a" to "z", the numerals "0" 
             * to "9", and the symbols "+" and "/". 
             * The valueless character, "=", 
             * is used for trailing padding.
             * 
             */
            if (char.IsLetterOrDigit(currchar) || currchar == '=' || currchar == '+' || currchar == '/')
            {
                sb.Append(currchar);
            }
        }
        return sb.ToString();
    }

I'm writing this for anyone trying to send an encrypted rijndael data through a UDP socket. I lost 3 days to figure this out myself (well, I may be looking like a moron for taking that long to figure this out but when you have a messed code with all the ways trying to figure out what's the issue that happens) after reading a whole bunch of threads with similar errors on stackoverflow. None of them had the exact same issue nor a solution to it. So if you are trying to send encrypted data through a UDP socket and geting an error like Invalid padding and padding cannot be removed or Length of data to decrypt is invalid this thread might come handy.

I believe this thread might help to many others who have come across the described problem and will save time to many users on this forum. If the mods do not believe or find the thread unnecessary or inappropriate you can get rid of it, I just decided to spare some of my time in order to describe the problem I met and how to resolve it as it may save plenty of time to others, believe me this was really getting me mad until I found a solution to what is obviously quite an easy to fix issue.

Dex
  • 33
  • 9
  • EDIT: In the first step you need a buffer size, not a blocksize. (Sorry I'm a little tired while writing this). You need a buffer size in which your base64 encoded will fit. You can begin with 255 as an array size for testing purposes and then go with what you would need. – Dex Oct 11 '13 at 19:46