1

I recently converted a previous encryption program from Java to C#. The decryption function works fine (as tested using the Java encrypted string), but the encryption produces a result that neither the Java program or the C# program can decode. The code and unit test is included below, along with the output. GetKey() function is my own salt generated key that correctly produces the same key for a given input (tested in Java and C#).

The encrypt and decrypt functions are exact duals. I can't understand why one works and the other does not.

I have reduced all texts to base64 to avoid the sign pitfall between java and C#. inserted the Java encrypted string (via debugger) in the decrypt function and it worked fine. The encrypted string fails to decode both in the decrypt and in its Java version with the same error

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Timers;

namespace Krypto
{
    class Program
    {
        static void Main(string[] args)
        {
        string text;
        string key;
        string encrypText;
        string decryptText;

        Console.Write("Plese enter text to encrypt:");
        text = Console.ReadLine();

        Console.WriteLine("");

        Console.Write("Plese enter Key:");
        key = Console.ReadLine();
        Console.WriteLine("");

        encrypText = EncryptString(text, key);
        Console.WriteLine("The encrypted string is: "+ encrypText);

        decryptText = Decrypt(encrypText, key);
        Console.WriteLine("The decrypted string is: " + decryptText);

        if (text.Equals(decryptText)) Console.WriteLine("The test was sucessful");
        else Console.WriteLine("The test failed!");

        Console.ReadKey();

        }


        static string EncryptString(string text, string key)
        {
            byte[] key16 = getKey16(key);
            string text64 = System.Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(text));
            byte[] encrypted;

            byte[] ivArr = { 1, 3, 3, 4, 5, 6, 6, 7, 4, 3, 2, 1, 7, 5, 5, 7 };
            byte[] IVBytes16Value = new byte[16];
            Array.Copy(ivArr, IVBytes16Value, 16);

        // Create an RijndaelManaged object 
        // with the specified key and IV. 
        using (RijndaelManaged aes = new RijndaelManaged())
            {
                aes.Key = key16; 
                aes.IV = IVBytes16Value;
                aes.BlockSize = 128;
                aes.KeySize = 256;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7; 


            // Create a decryptor to perform the stream transform.
            ICryptoTransform encryptor = aes.CreateEncryptor();

            try
            {
                byte[] textBytes = Convert.FromBase64CharArray(text64.ToCharArray(), 0, text64.Length);
                encrypted = encryptor.TransformFinalBlock(textBytes, 0, textBytes.Length);
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: " + e.Message);
                return "";
            };

        }


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

        }

    private static string Decrypt(string CipherText, string key)
    {
        byte[] key16 = getKey16(key);
        RijndaelManaged aes = new RijndaelManaged();
        aes.BlockSize = 128;
        aes.KeySize = 256;

        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;

        byte[] ivArr = { 1, 3, 3, 4, 5, 6, 6, 7, 4, 3, 2, 1, 7, 5, 5, 7 };
        byte[] IVBytes16Value = new byte[16];
        Array.Copy(ivArr, IVBytes16Value, 16);

        aes.Key = key16;
        aes.IV = IVBytes16Value;

        ICryptoTransform decrypto = aes.CreateDecryptor();
        byte[] decryptedData = null;

        try
        {
            byte[] encryptedBytes = Convert.FromBase64CharArray(CipherText.ToCharArray(), 0, CipherText.Length);
            decryptedData = decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: " + e.Message);
            return ""; 
        };
            return System.Text.ASCIIEncoding.UTF8.GetString(decryptedData);
        }

    }

private static byte[] getKey16(string key)
        {
            byte[] key16 = new byte[16];
            //make key a 64bit string
            string base64key = System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(key));
            byte[] keyArr = Convert.FromBase64String(base64key);

            int keyarraysize = keyArr.Count();

            if (keyarraysize < 16)
            {
                int counter = 0;
                while (keyarraysize * (counter + 1) < 16)
                {
                    for (int i = 0; i < keyarraysize; ++i) key16[i + keyarraysize * counter] = keyArr[i];
                    ++counter;
                }
                for (int i = keyarraysize * counter; i < 16; ++i)
                {
                key16[i] = 0; 
                }
            }
            else for (int i = 1; i < 16; ++i) key16[i] = keyArr[i];
            return key16;

        }
}

the encrypt and decrypt in C# are exact duals. It should simply produce a correct test, instead i get the output below

Plese enter text to encrypt:This is a test

Plese enter Key:testKey

The encrypted string is: vLCR4QCJcVHvN4ss7H4Q2g== Error: Padding is invalid and cannot be removed. The decrypted string is: The test failed!

levy
  • 11
  • 3
  • Why are you converting the plaintext into base64 and back again when encrypting? Just use `Encoding.UTF8.GetBytes(text)` to get the UTF-8 representation of the bytes. – Jon Skeet Feb 07 '19 at 16:46
  • The referenced `getKey16` function is not shown. – Clay Feb 07 '19 at 20:17
  • Check this out: https://stackoverflow.com/questions/202011/encrypt-and-decrypt-a-string-in-c/10366194#10366194 – Clay Feb 07 '19 at 20:49
  • @Jon, The use of Base64 is needed to be sure that the Java and C# program work correctly with one another, otherwise you may be trapped unto a signed/unsigned byte situation – levy Feb 07 '19 at 21:06
  • @Clay, the function just generates an array with size multiple of 16 of bytes based on the key parameter. It is not relevant to the debug you can create an array of fixed values byte[16] and the use instead, the error remains – levy Feb 07 '19 at 21:08
  • @Clay, I saw the link. This is essentially the same that i am doing. I don't need to create a stream beause i know that the clear/encrypted text in case is small. Other Stack Overflow responses support that – levy Feb 07 '19 at 21:16
  • @Clay, This question was asked before https://stackoverflow.com/questions/39093489/c-sharp-equivalent-of-the-java-secretkeyspec-for-aes, but the solution was not valid in my case – levy Feb 07 '19 at 21:17
  • > " The use of Base64 is needed to be sure that the Java and C# program work correctly with one another" No, it's really not. Calling `Convert.FromCharArray(Convert.ToBase64String(bytes).ToCharArray())` will always give you back a byte array containing the same bytes as you started with. (You also don't need to create a byte array, then create a second one and copy from the first to the second as you're doing with `ivArr` and `IVBytes16Value`.) – Jon Skeet Feb 08 '19 at 07:21
  • 1
    Note that without `getKey16`, we can't run your code. If you could provide a *complete* example, it would be easier to help you. – Jon Skeet Feb 08 '19 at 07:22
  • @jon, You are right about the array copy...that's a Java leftover thing – levy Feb 08 '19 at 13:42
  • @jon, the simplified getKey() added above will do. The error does not change with a simple getKey(), as i mentioned. The error is not there, nor in the base64, this is would at most (not really) change the clear text. – levy Feb 08 '19 at 13:43
  • @levy: It's not about the error not being in `getKey` - it's about that being necessary for us to be able to easily reproduce the problem. With that in place (although your code currently doesn't compile due to the method being directly in the namespace location) it was easy for me to reproduce the problem, then validate that shingo's answer fixes it. – Jon Skeet Feb 08 '19 at 14:29

1 Answers1

1

If you set KeySize, the key will be set to null (a random key is generated).

See the source

In fact you don't need to set KeySize because the program knows how to compute from the key.

shingo
  • 18,436
  • 5
  • 23
  • 42
  • I can confirm that commenting out the assignment to `KeySize` makes the code work fine. (I'd still remove the redundant bits of code, but that's a different matter.) – Jon Skeet Feb 08 '19 at 14:29
  • @shingo, you nailed it! By having a key size different, the algorithm ignores the key and generates a new one, which breaks decryption! This is not the case in Decryption, which ignores the size and uses the key. – levy Feb 08 '19 at 16:15
  • @jon, I just tested again without using Base64 as you suggested. If one does that the Java decryptor fails to decode the C# encryption. Like I said it would. I need the two programs in both languages to work with another.... – levy Feb 08 '19 at 16:21
  • @jon, this link https://stackoverflow.com/questions/23406135/error-rijndaelmanaged-padding-is-invalid-and-cannot-be-removed talks about the issue. I found the explanation about signed/unsigned bytes elsewhere in Stack Overflow but can't find the reference now. – levy Feb 08 '19 at 16:41
  • @levy: I think you've misunderstood either the problem in that question, or what I was saying in the comments. You *do* need to base64-encode the encrypted output. You *don't* need to base64-encode and then immediately base64-decode your input. If you're interested in more details, please post a new question just about that, including the working code and then the broken code, so I can add an answer. (It wouldn't be appropriate on this question.) – Jon Skeet Feb 08 '19 at 17:25
  • @levy: The difference isn't "by having a key size different" - it's that in Encrypt you set the Key and then the KeySize. In Decrypt you set the KeySize and then the Key. It's that ordering that's important. You don't need to set the KeySize in *either* place, given that you're setting the key. – Jon Skeet Feb 08 '19 at 17:27
  • @jon, thanks for the clarification, now i understand – levy Feb 08 '19 at 21:11