Home > Programming > AES 128bit Cross Platform (Java and C#) Encryption Compatibility

AES 128bit Cross Platform (Java and C#) Encryption Compatibility

It seems quite a minor issue but doing cross platform encryption and decryption specifically AES was a bit a challenge for me. When you  get it working it just seems like no big change to the original code you where working on but you wasted fruitless hours debugging. Just sharing so that another person doesn’t have to waste time wondering why same settings produce a different cipher and going through a number of unhelpful posts where it ” works on my machine”.

I initially used ISO 10126 padding, but that seemed to produce a different cipher in C# and Java. I decided to go with no padding which leads me to the  cryptographic exception: Length of the data to decrypt is invalid. After some head banging I settled with PKCS7

Same encoding of strings has to be used in java and c#, other wise you will end up having different ciphers in java and c#.

The java code below uses a base64 util class from android SDK but you can replace it like with one from apache commons

C# encryption utility

public RijndaelManaged GetRijndaelManaged(String secretKey)
        {
            var keyBytes = new byte[16];
            var secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
            Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length));
            return new RijndaelManaged
            {
                Mode = CipherMode.CBC,
                Padding = PaddingMode.PKCS7,
                KeySize = 128,
                BlockSize = 128,
                Key = keyBytes,
                IV = keyBytes
            };
        }

        public byte[] Encrypt(byte[] plainBytes, RijndaelManaged rijndaelManaged)
        {
            return rijndaelManaged.CreateEncryptor()
                .TransformFinalBlock(plainBytes, 0, plainBytes.Length);
        }

        public byte[] Decrypt(byte[] encryptedData, RijndaelManaged rijndaelManaged)
        {
            return rijndaelManaged.CreateDecryptor()
                .TransformFinalBlock(encryptedData, 0, encryptedData.Length);
        }

        /// <summary>
        /// Encrypts plaintext using AES 128bit key and a Chain Block Cipher and returns a base64 encoded string
        /// </summary>
        /// <param name="plainText">Plain text to encrypt</param>
        /// <param name="key">Secret key</param>
        /// <returns>Base64 encoded string</returns>
        public String Encrypt(String plainText, String key)
        {
            var plainBytes = Encoding.UTF8.GetBytes(plainText);
            return Convert.ToBase64String(Encrypt(plainBytes, GetRijndaelManaged(key)));
        }

        /// <summary>
        /// Decrypts a base64 encoded string using the given key (AES 128bit key and a Chain Block Cipher)
        /// </summary>
        /// <param name="encryptedText">Base64 Encoded String</param>
        /// <param name="key">Secret Key</param>
        /// <returns>Decrypted String</returns>
        public String Decrypt(String encryptedText, String key)
        {
            var encryptedBytes = Convert.FromBase64String(encryptedText);
            return Encoding.UTF8.GetString(Decrypt(encryptedBytes, GetRijndaelManaged(key)));
        }

Java Encryption Utility

private final String characterEncoding = "UTF-8";
	private final String cipherTransformation = "AES/CBC/PKCS5Padding";
	private final String aesEncryptionAlgorithm = "AES";

	public  byte[] decrypt(byte[] cipherText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
	{
		Cipher cipher = Cipher.getInstance(cipherTransformation);
		SecretKeySpec secretKeySpecy = new SecretKeySpec(key, aesEncryptionAlgorithm);
		IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
		cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy, ivParameterSpec);
		cipherText = cipher.doFinal(cipherText);
		return cipherText;
	}

	public byte[] encrypt(byte[] plainText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
	{
		Cipher cipher = Cipher.getInstance(cipherTransformation);
		SecretKeySpec secretKeySpec = new SecretKeySpec(key, aesEncryptionAlgorithm);
		IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
		cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
		plainText = cipher.doFinal(plainText);
		return plainText;
	}

	private byte[] getKeyBytes(String key) throws UnsupportedEncodingException{
		byte[] keyBytes= new byte[16];
		byte[] parameterKeyBytes= key.getBytes(characterEncoding);
		System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
		return keyBytes;
	}

	/// <summary>
	/// Encrypts plaintext using AES 128bit key and a Chain Block Cipher and returns a base64 encoded string
	/// </summary>
	/// <param name="plainText">Plain text to encrypt</param>
	/// <param name="key">Secret key</param>
	/// <returns>Base64 encoded string</returns>
	public String encrypt(String plainText, String key) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException{
		byte[] plainTextbytes = plainText.getBytes(characterEncoding);
		byte[] keyBytes = getKeyBytes(key);
		return Base64.encodeToString(encrypt(plainTextbytes,keyBytes, keyBytes), Base64.DEFAULT);
	}

	/// <summary>
	/// Decrypts a base64 encoded string using the given key (AES 128bit key and a Chain Block Cipher)
	/// </summary>
	/// <param name="encryptedText">Base64 Encoded String</param>
	/// <param name="key">Secret Key</param>
	/// <returns>Decrypted String</returns>
	public String decrypt(String encryptedText, String key) throws KeyException, GeneralSecurityException, GeneralSecurityException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException{
		byte[] cipheredBytes = Base64.decode(encryptedText, Base64.DEFAULT);
		byte[] keyBytes = getKeyBytes(key);
		return new String(decrypt(cipheredBytes, keyBytes, keyBytes), characterEncoding);
	}

Hopefully this saves someone time.

About these ads
Categories: Programming Tags: , ,
  1. Rajesh
    January 19, 2012 at 12:43 pm | #1

    Thanks a lot man. You saved my time. :) It works like a charm.

  2. lward
    January 24, 2012 at 11:57 pm | #2

    This was very useful. Thanks for posting your solution.

  3. Adriano
    January 30, 2012 at 1:16 am | #3

    Amazing, I spent a few hours trying to put this encryption to work between java and c#,
    it’s working now, thanks to you.

    Thank you, realy. =)

  4. Carlos
    February 7, 2012 at 7:35 pm | #4

    This is wonderful!!, thanks

  5. February 29, 2012 at 2:53 pm | #5

    Thanks for the post. It was very usefull.

  6. Bas
    March 6, 2012 at 8:21 pm | #6

    Thank you so much… I have been struggling with this for ages!

  7. Parmeet
    March 9, 2012 at 8:03 am | #7

    Hi,

    I’ve downloaded the apache commons 1.6 jar but there seems to be a problem with “Base64.DEFAULT” which is coming as undefined. Could you point me to the correct version of commons library or perhaps let me know the value of “Base64.DEFAULT” if its a constant.

    Thanks,

    Parmeet

  8. June 19, 2012 at 4:11 pm | #9

    Thank you so much!!! I had about a week struggling with this, THANK YOU.

  9. lakers
    October 9, 2012 at 9:00 am | #10

    can I get the same logic in javascript?

  10. Sam
    October 13, 2012 at 11:48 pm | #11

    This work better than medicine…I was able to complete my small program in minutes; 128 and 256.
    Thanks
    TuKanSam

  11. Chuck
    November 28, 2012 at 6:46 pm | #12

    This worked great. Many thanks!

  12. lakers
    December 20, 2012 at 5:31 am | #13

    is it possible to implement the same logic in Objective C? please reply as soon as possible..

  13. December 20, 2012 at 6:19 am | #14

    Am not acquainted with Objective C.

  14. January 6, 2013 at 4:04 pm | #15

    I’ve used Java code in android and .Net wcf service perfectly. Thank you very much!

  15. Ajeet
    February 19, 2013 at 4:34 pm | #16

    Please any one help me that what will be in JAVA in line where Base64 is used in the above given code. Thanks for any help in this regards.

    • Govs
      March 6, 2013 at 3:50 pm | #17

      I have similar problem. I tried using
      byte[] cipheredBytes = Base64.base64Decode(EncryptedText).getBytes()
      But it gives me an exception javax.crypto.BadPaddingException: Given final block not properly padded.

      did you find any solution for this?

  16. Max
    February 27, 2013 at 7:05 pm | #18

    It is not a good idea to use the encryption key as initialization vector (IV) for the cipher block chaining. Encryption key and IV should be seperate values. Take a (human readable) pass phrase string and feed it into a cryptographic hash function like SHA256/384/512 to get a sufficient number of key bytes for both, encryption key AND initialization vector. Use the first 16 bytes of the hash value as encryption key and the next 16 bytes as IV.

  17. Govs
    March 6, 2013 at 3:47 pm | #19

    Hi,

    This is an awesome document. Really very helpful.

    I am trying to implement the same code for encryption in c# and decryption in java.
    I am not able to find the same method to get the byte array for cipheredBytes like yours at line 51 i.e. byte[] cipheredBytes = Base64.decode(encryptedText, Base64.DEFAULT).

    However I am using this method:
    byte[] cipheredBytes = Base64.base64Decode(encryptedText).getBytes();

    But I am getting an error
    javax.crypto.BadPaddingException: Given final block not properly padded

    How to implement the method like yours? What packege will I need to import for that?
    Please help

  18. March 14, 2013 at 11:31 am | #20

    I am facing the below errors pls give me a solutions

    Exception in thread “main” java.lang.RuntimeException: Stub!
    at android.util.Base64.encodeToString(Base64.java:8)
    at com.security.packs.Encry.encrypt(Encry.java:87)
    at com.security.packs.Encry.main(Encry.java:112)

  19. Evgeniy
    March 15, 2013 at 7:35 pm | #21

    Big thanks !!!
    Wonderful)

  20. Thomas
    May 30, 2013 at 7:26 am | #22

    As for the Base64.Default when not using the Android package, use the package org.apache.ws.security.util.Base64 and the method Base64.encode(encrypt(plainTextbytes,keyBytes, keyBytes)); (and obviously the counterpart decode…)

  21. Naturjoghurt
    June 22, 2013 at 11:08 am | #23

    Thank you very much!
    Nice solution solving all my Problems.

  22. Marcusdev
    July 16, 2013 at 3:21 pm | #24

    This also works with openssl.net which by default uses PKCS5Padding.

  23. September 5, 2013 at 2:56 am | #25

    Thank you very much for this.

  24. Balian
    September 8, 2013 at 3:39 am | #26

    Thanks a lot! It’s a perfect solution.

  25. NRHONLINE
    September 13, 2013 at 6:06 pm | #27

    Has anyone implemented this into a java applicatio
    n yet?

  26. David Tran
    September 16, 2013 at 5:59 pm | #28

    Hi I have a problem to convert to C# from java. Please help me out.

    javax.crypto.Cipher c = javax.crypto.Cipher.getInstance(“RSA/ECB/NOPADDING”);
    javax.security.cert.X509Certificate pcert = javax.security.cert.X509Certificate.getInstance(cer.RawData);
    c.init(javax.crypto.Cipher.ENCRYPT_MODE, pcert.getPublicKey());
    byte[] pdecrypt = pdecrypt = c.doFinal(p);

    I need the same thing but in C#

  27. Sridhar
    September 18, 2013 at 9:57 am | #29

    if we change RijndaelManaged to AesManged then the keys generated are not same in java and .net any solution?

  28. Sridhar
    September 18, 2013 at 10:26 am | #30

    and how to pass salt value in .net

  29. Dasu
    September 19, 2013 at 2:06 pm | #31

    Here is the Java encryption code and which is getting decrypted and woking fine:

    private static byte[] doThis(String message) {
    byte[] messageCrypte = null;
    try {
    // Certificate Input Stream
    // LA SSL Certificate to be passed.
    InputStream inStream = new FileInputStream(certificate);

    // X509Certificate created
    CertificateFactory cf = CertificateFactory.getInstance(“X.509″);
    X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
    inStream.close();

    // Getting Public key using Certficate
    PublicKey rsaPublicKey = (PublicKey) cert.getPublicKey();

    Cipher encryptCipher = Cipher.getInstance(“RSA/ECB/PKCS1Padding”, “SunJCE”);
    encryptCipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);

    byte[] messageACrypter = message.getBytes();
    // Encrypted String
    messageCrypte = encryptCipher.doFinal(messageACrypter);
    } catch (Exception e) {
    // TODO: Exception Handling
    e.printStackTrace();
    }
    return messageCrypte;
    }

    I tried to convert above code to c#.net but it was giving an exception saying that
    javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes in java dcryption. Please suggest me anything wrong in my .net code mentioned below:

    static byte[] doThis(string message)
    {

    X509Certificate2 cert = new X509Certificate2(@”C:\Data\ABC-rsa-public-key-certificate.cer”);
    byte[] aa = cert.GetPublicKey();

    RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
    RSAParameters RSAKeyInfo = new RSAParameters();
    // byte[] Exponent = { 1, 0, 1 };

    RSAKeyInfo = RSA.ExportParameters(false);
    //Set RSAKeyInfo to the public key values.
    RSAKeyInfo.Modulus = aa;
    //RSAKeyInfo.Exponent = Exponent;
    RSA.ImportParameters(RSAKeyInfo);
    byte[] bb = RSA.Encrypt(GetBytes(message), false);
    return bb;

    //Cipher encryptCipher = Cipher.getInstance(“RSA/ECB/PKCS1Padding”, “SunJCE”);
    //encryptCipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);

    //byte[] messageACrypter = message.
    //messageCrypte = encryptCipher.doFinal(messageACrypter);

    }

  30. September 26, 2013 at 12:55 pm | #32

    You rock!

  31. September 29, 2013 at 11:43 pm | #33

    I’m serious you rock, you saved me crap loads of time

  32. Vijay Muvva
    October 16, 2013 at 7:44 am | #34

    You are a life saver.. Thanks a ton.. Working perfectly..

  33. October 18, 2013 at 5:56 am | #35

    Can I get the same algorithm in C?

  34. Bas
    October 21, 2013 at 9:14 pm | #36

    Thanks man, saved me alot of time! :)

  35. eWoks
    December 14, 2013 at 6:56 pm | #37

    Thanks MAN !!!

  36. Nandkishor
    December 26, 2013 at 5:56 am | #38

    Thanks man, your code works perfectly and saved my a lot of time..
    You are great..:)

  37. Sal
    January 19, 2014 at 10:49 pm | #39

    Perfect, Thank you a lot man.

  38. Oyinda
    March 26, 2014 at 6:52 pm | #40

    Thanks so much. After weeks of frustration and misery. Waooooo You are a blessing. I have more than 25 webpages opened that I am reading to get this sorted out. This worked like magic

  39. April 8, 2014 at 5:55 pm | #41

    Thank you so much :)

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: