11

I am working in a C# application. We have common methods to store data on a file. These methods encrypt the data and store them on the file system. when we need the data, ReadData method decrypts the data and returns me plain text.

This code works fine in normal cases if size of the text in small. but for a example text given below, the decryption code is throwing exception - length of the data to decrypt is invalid.

The exception occurs at line

        // close the CryptoStream
        x_cryptostream.Close();

I tried different ways but no luck. Can some pls help.

Why am I encrypting already encrypted data - I am just trying to store in a file using common method of the huge application. The common methods storedata(key,data) nad readdata(key) do the encryption/decryption I can't avoid.

   public static byte[] Decrypt(byte[] ciphertext, string Key, string IV)
    {
        byte[] k = Encoding.Default.GetBytes(Key);
        byte[] iv = Encoding.Default.GetBytes(IV);

        // create the encryption algorithm
        SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
        x_alg.Padding = PaddingMode.PKCS7;

        // create an ICryptoTransform that can be used to decrypt data
        ICryptoTransform x_decryptor = x_alg.CreateDecryptor(k, iv);

        // create the memory stream
        MemoryStream x_memory_stream = new MemoryStream();

        // create the CryptoStream that ties together the MemoryStream and the 
        // ICryptostream
        CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
        x_decryptor, CryptoStreamMode.Write);

        // write the ciphertext out to the cryptostream
        x_cryptostream.Write(ciphertext, 0, ciphertext.Length);

        // close the CryptoStream
        x_cryptostream.Close();

        // get the plaintext from the MemoryStream
        byte[] x_plaintext = x_memory_stream.ToArray();

Below is the code of encrypt method.

        public static byte[] Encrypt(string strplain, string Key, string IV)
        {
        byte[] k = Encoding.Default.GetBytes(Key);
        byte[] iv = Encoding.Default.GetBytes(IV);

        byte[] plaintext = Encoding.Default.GetBytes(strplain);

        // create the encryption algorithm
        SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
        x_alg.Padding = PaddingMode.PKCS7;
        // create an ICryptoTransform that can be used to encrypt data
        ICryptoTransform x_encryptor = x_alg.CreateEncryptor(k, iv);

        // create the memory stream
        MemoryStream x_memory_stream = new MemoryStream();

        // create the CryptoStream that ties together the MemoryStream and
        // the ICryptostream
        CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
        x_encryptor, CryptoStreamMode.Write);

        // write the plaintext out to the cryptostream
        x_cryptostream.Write(plaintext, 0, plaintext.Length);

        // close the CryptoStream
        x_cryptostream.Close();

        // get the ciphertext from the MemoryStream
        byte[] x_ciphertext = x_memory_stream.ToArray();

        // close memory stream
        x_memory_stream.Close();

        // convert from array to string
        string cipher_Tx = Encoding.Default.GetString(x_ciphertext, 
            0, x_ciphertext.Length);

        x_encryptor.Dispose();

        x_alg.Clear();
        byte[] cipher = Encoding.Default.GetBytes(cipher_Tx);

        return cipher;
    }  
Kevin Morocco
  • 179
  • 1
  • 2
  • 10

2 Answers2

13

Your problem is string cipher_Tx = Encoding.Default.GetString(x_ciphertext, 0, x_ciphertext.Length);.

x_ciphertext is not a valid byte representation of text, it has many unpresentable characters and when you do your byte[] to string conversion you are losing information. The correct way to do it is use a string format that is designed to represent binary data using something like Convert.ToBase64String(byte[]) and Convert.FromBase64String(string).

string cipher_Tx = Convert.ToBase64String(x_ciphertext)

x_encryptor.Dispose();

x_alg.Clear();
byte[] cipher = Convert.FromBase64String(cipher_Tx)

That being said, there is a lot of other "odd" things about your code, for example you don't use using statements and you really should. Also that whole conversion to string and back is totally unnecessary, just return x_ciphertext. There may be other problems with the code too (like where did the strings for Key and IV come from) and many other best practices (like you should be generating a random IV and writing it out in to the output and the key should be generated using a key derivation function not straight from user text), but I stopped checking after I found the string conversion issue.

Scott Baker
  • 10,013
  • 17
  • 56
  • 102
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Thanks for trying to help. Truly appreciate it I modified my code but this doesn't resolve the exception I mentioned. Key and IV is passed by the ReadData/StoreData methods. This code is written several years ago. I believe this is something to do with blocks of data decryption algorithm uses not matching with the size or something. – Kevin Morocco Mar 17 '14 at 23:17
  • Well you seem to really like using `Encoding.Default.` and I am willing to bet almost every use of it is wrong. I would check the other parts of the program for similar issues. – Scott Chamberlain Mar 17 '14 at 23:18
  • I looked at entire code related to encrypt decrypt and changed all the lines of Encoding.Default to Convert.FromBase64String/Convert.ToBase64String. I still get length of the data to decrypt is invalid exception. – Kevin Morocco Mar 18 '14 at 00:06
  • There is no reason to convert to Base 64 and back. Look at byte[] x_ciphertext and then byte[] cipher -- they are exactly the same value. Just return x_ciphertext -- you already have it. – Jim Flood Mar 18 '14 at 23:31
0

Your code above works as long as the key and iv used to decrypt match the key and iv used to encrypt. Try this:

byte[] test = new byte[1000000];
for (int i = 0; i < 256; i++)
{
    test[i] = (byte)i;
}
var ciphertext = Encrypt(Encoding.Default.GetString(test), "0000000000000000", "0000000000000000");
byte[] check = Decrypt(ciphertext, "0000000000000000", "0000000000000000");
for (int i = 0; i < 256; i++)
{
    Debug.Assert(check[i] == (byte)i, "round trip");
}

As you can see, one million bytes encrypt and decrypt just fine with your code, so I don't think it has anything to do with data size.

However, change the IV like this:

byte[] check = Decrypt(ciphertext, "0000000000000000", "000000000000000X"); // note X

and the Debug.Assert will fire -- the decryption will not match. However, x_cryptostream.Close() succeeds.

Next, try changing the key like this:

byte[] check = Decrypt(ciphertext, "000000000000000X", "0000000000000000"); // note X

Now, x_cryptostream.Close() will fail with a CryptographicException, probably, "Padding is invalid and cannot be removed."

Corrupting the key will cause the decryption to fail, and x_cryptostream.Close() to fail.

I think the problem is in your saving and later restoring the key bytes.

BTW: Hopefully you are using the full binary range of the key, and not basing it only on ASCII characters, otherwise you don't really have a strong key.

Jim Flood
  • 8,144
  • 3
  • 36
  • 48
  • 1
    Thanks. I accidentally deleted padding. I think changing encoding to Convert.FromBase64String(cipher_Tx) and adding x_alg.Padding = PaddingMode.PKCS7 fixed the issue. – Kevin Morocco Mar 19 '14 at 04:41