1

First off, I want to be able to encrypt some data in one of my Java Class and write it in a text file on the phone. Then my Unity class in c# read it, decrypt it and the data can be used.

At the moment, my java class can encrypt and decrypt his own data. My C# can do the same as well. Problem is, my c# code can't decrypt what java previously encrypted. I'm 100% sure they have the same key (printed a log so compare and it's the same). There seems to be something different between my encryption in java and c#.

Here is the error I'm getting when trying to decrypt in c# what was previously crypted by java:

    03-22 13:32:57.034 14264 14351 E Unity   : CryptographicException: Bad PKCS7 padding. Invalid length 197.
    03-22 13:32:57.034 14264 14351 E Unity   :   at Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException (PaddingMode padding, Int32 length, Int32 position) [0x00000] in <filename unknown>:0
    03-22 13:32:57.034 14264 14351 E Unity   :   at Mono.Security.Cryptography.SymmetricTransform.FinalDecrypt (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) [0x00000] in <filename unknown>:0
    03-22 13:32:57.034 14264 14351 E Unity   :   at Mono.Security.Cryptography.SymmetricTransform.TransformFinalBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) [0x00000] in <filename unknown>:0
    03-22 13:32:57.034 14264 14351 E Unity   :   at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock (System.Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) [0x00000] in <filename unknown>:0
    03-22 13:32:57.034 14264 14351 E Unity   :   at GoogleReader.Decrypt (System.String text) [0x00000] in <filename unknown>:0

JAVA CODE :

public static String key;

public static String Crypt(String text)
{       
    try
    {
        // Get the Key
        if(com.UQAC.OceanEmpire.UnityPlayerActivity.myInstance != null){
            key = Base64.encodeToString(com.UQAC.OceanEmpire.UnityPlayerActivity.myInstance.key.getEncoded(),Base64.DEFAULT);
            com.UQAC.OceanEmpire.UnityPlayerActivity.myInstance.SendMessageToUnity(key);
        } else {
            return "";
        }
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");

        byte[] iv = { 1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1, 7, 7, 7, 7 };
        IvParameterSpec ivspec = new IvParameterSpec(iv);

        Key secretKey = new SecretKeySpec(Base64.decode(key,Base64.DEFAULT), "AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);

        return Base64.encodeToString(cipher.doFinal(text.getBytes()),Base64.DEFAULT);
    }
    catch (Exception e)
    {
        System.out.println("[Exception]:"+e.getMessage());
    }
    return null;
}

public static String Decrypt(String text)
{
    try
    {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");

        byte[] iv = { 1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1, 7, 7, 7, 7 };
        IvParameterSpec ivspec = new IvParameterSpec(iv);

        Key SecretKey = new SecretKeySpec(Base64.decode(key,Base64.DEFAULT), "AES");
        cipher.init(Cipher.DECRYPT_MODE, SecretKey, ivspec);

        byte DecodedMessage[] = Base64.decode(text, Base64.DEFAULT);
        return new String(cipher.doFinal(DecodedMessage));
    }
    catch (Exception e)
    {
        System.out.println("[Exception]:"+e.getMessage());
    }
    return null;
}

C# CODE :

public static string keyStr;

public static string Decrypt(string text)
    {
        RijndaelManaged aes = new RijndaelManaged();
        aes.BlockSize = 128;
        aes.KeySize = 256;
        aes.Mode = CipherMode.ECB;
        //aes.Padding = PaddingMode.None;

        byte[] keyArr = Convert.FromBase64String(keyStr);
        byte[] KeyArrBytes32Value = new byte[32];
        Array.Copy(keyArr, KeyArrBytes32Value, Math.Min(KeyArrBytes32Value.Length, keyArr.Length));

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

        aes.Key = KeyArrBytes32Value;
        aes.IV = IVBytes16Value;

        ICryptoTransform decrypto = aes.CreateDecryptor();

        byte[] encryptedBytes = Convert.FromBase64CharArray(text.ToCharArray(), 0, text.Length);

        byte[] decryptedData = decrypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);

        return Encoding.UTF8.GetString(decryptedData);
    }

    public static string Encrypt(string text)
    {
        RijndaelManaged aes = new RijndaelManaged();
        aes.BlockSize = 128;
        aes.KeySize = 256;
        aes.Mode = CipherMode.ECB;

        byte[] keyArr = Convert.FromBase64String(keyStr);
        byte[] KeyArrBytes32Value = new byte[32];
        Array.Copy(keyArr, KeyArrBytes32Value, Math.Min(KeyArrBytes32Value.Length, keyArr.Length));

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

        aes.Key = KeyArrBytes32Value;
        aes.IV = IVBytes16Value;

        ICryptoTransform encrypto = aes.CreateEncryptor();

        byte[] plainTextByte = Encoding.UTF8.GetBytes(text);
        byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
        return Convert.ToBase64String(CipherText);
    }

Test to encrypt and decrypt a string in C#

Original String : Hello World
Crypted : 7Zmd4yvxgR6Mg0nUDQumBA==
Decrypted : Hello World

Test to encrypt and decrypt a string in Java

Original String : Hello World
Crypted : zQjSpJqU8YkHhMDHw8wuTQ==
Decrypted : Hello World

The Key during those test :

FuVf/CNYkHdyBqejq3eoHQ==

As you can see, they have the same key but encrypted the data differently. I can't seem to figure out what I'm doing wrong.

2
  • Are the AES block and keysize different in the Java implementation? Commented Mar 22, 2018 at 18:46
  • How am I suppose to know that. In my post, you can see the Java code I'm using to crypt and decrypt. Here is the function where I write my string to a file imgur.com/a/jNp7o Commented Mar 22, 2018 at 18:50

2 Answers 2

1

Found the solution ! Everything works.

After following some hints, here is the fix:

  1. Made sure everything was in ECB. In the Java insted of having this:

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");

I simply put this in both crypt and decrypt functions :

Cipher cipher = Cipher.getInstance("AES");
  1. In C#, the key size and IV size need to depend on the actual original Key and the IV array (not an arbitrary number like 32 or 16, need to be the actualy size of the key)

  2. Insted of writing a string to the file, I now write directly the byte as they are directly after I encrypted them. This made it easier for the C# side to just straight up get everything and decrypt it.

So the major problem was to write strings in the file insted of bytes. I don't know why this caused an error (error : invalid block size).

Warning: Using ECB mode is insecure, in some circumstances, it is trivial to retrieve the plaintext. Don't use this code in production or in any scenario where security is required.

Sign up to request clarification or add additional context in comments.

1 Comment

can you share your full encryption decryption class ?
0

Your C# code is in ECB mode but your Java code is in CBC mode.

Did you actually write the code you have or just copy and paste it? Looks like the latter. Don't copy and paste security code. You should be writing it with intent and understanding. Not "oh look here I'll throw this together and hope it works".

Also, the way you handle your IV is very insecure. Never use a fixed IV. Under the right circumstances it can completely reveal your plaintext. Use a randomly generated IV for every encryption operation.

9 Comments

I inspired myself from multiple post on the internet about encryptiong in Javan and C#. After 2 days, I finally made it work on both platforms seperatly. Now I need to make them work together, but I'm still learning about encryption implementation in Java and C#. How would you change my Java code to be in CBC exactly? Note that this is not ment to be distributed, it's not an official app to be release. It's for a prototype I'm working on.
If you need to ask, you clearly don't understand the code. Go through each part for both languages, learn what every single line actually does, and it should become obvious what the issue is.
I understand each step, my comments were in French so I deleted them. In C#, you can specify block size, padding and mode, etc. You could of just straight up said that I need to replace AES/CBC/PKCS7PADDING in the definition of the cipher with AES/ECB/PKCS7PADDING.
That's really not what you need to do. You want both to be in CBC mode, not ECB. ECB mode is insecure.
You're welcome. I'm not trying to be harsh, I understand you are still studying. It is just important to be very pedantic when it comes to security-related code. I try to encourage people to learn the subject in-depth as much as they can before implementing something themselves. Hope you understand. Good luck.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.