August 14, 2014

AES Encryption between Java and C#

Abstract
You have data which needs to be shared securely between two applications. To to do this you need encryption, say AES.  However, one application is Java the other is C#. The purpose of this article is to show example code for AES encryption and decryption between Java and C#.

System Requirements
The code was developed and run using the following system setup. If yours is different, it may work but no guarantees.
  • JDK 1.7.0_65
  • NetBeans 8.0
  • Maven 3.0.5 (bundled with NetBeans)
  • Microsoft Visual C# 2010 Express
  • Microsoft .NET Framework 4
The rest of the software dependencies can be found in the project's pom.xml

Download Project
If you just want to look at the project demonstrating the solution, download the code example from GitHub (https://github.com/mjremijan/Aes).

Run Projects
The project is unique in that it contains both Java and C# source code.  There is a top-level Maven POM project named ferris-aes.  Underneath that are two Maven modules:
ferris-aes-java and ferris-aes-csharp.

The first is ferris-aes-java.  This is the Java source code and it's a normal Maven Java project. The main() method class is org.ferris.aes.main.Main. Run this within NetBeans and you'll see the example encryption and decryption in the NetBeans Output window.  Nothing new and exciting here.  When running the Java project, you'll get output which looks like figure 1.

Figure 1: Java example AES encryption output

The second is ferris-aes-csharp. This is the C# source code. Although this is setup as a Maven module, Maven is not configured to compile and run the C# code.  You need
Microsoft Visual C# 2010 Express.  Point Microsoft Visual C# 2010 Express to the 
ferris-aes-csharp\EncryptDecryptTest\ directory and open the EncryptDecryptTest.csproj project file.  Once you have the C# project open in Microsoft Visual C# 2010 Express, you can use Microsoft Visual C# 2010 Express to run it. When running the C# project, you'll get output which looks like figure 2.

Figure 2: C# example AES encryption output

As you can see from figure 1 and 2, Java and C# are both encrypting the string 
please encrypt me  
The encryption is identical: 
0xNutDDR/9pWVku4LrBbS3qq4/4erTWlrEaJUfTTHa4=  
This means Java and C# both produce identical encrypted data, can share it with each other, and successfully decrypt it.

Java Source Code
Listing 1 shows the Java source code.  The name of the class is AesBase64Wrapper since the responsibility of the class is to provide a convenient wrapper around AES encryption and Base64 encoding.  Base64 encoding is used since encryption produces bytes which don't translate into visible screen characters.  Without Base64 encoding it is harder to determine if Java and C# are producing the same encryption.  Let's take a look at the code in listing 1 in more detail. 

Listing 1: Java AesBase64Wrapper class
package org.ferris.aes.crypto;

import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

/**
 *
 * @author Michael Remijan mjremijan@yahoo.com @mjremijan
 */
public class AesBase64Wrapper {

    private static String IV = "IV_VALUE_16_BYTE"; 
    private static String PASSWORD = "PASSWORD_VALUE"; 
    private static String SALT = "SALT_VALUE"; 

    public String encryptAndEncode(String raw) {
        try {
            Cipher c = getCipher(Cipher.ENCRYPT_MODE);
            byte[] encryptedVal = c.doFinal(getBytes(raw));
            String s = getString(Base64.encodeBase64(encryptedVal));
            return s;
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    public String decodeAndDecrypt(String encrypted) throws Exception {
        byte[] decodedValue = Base64.decodeBase64(getBytes(encrypted));
        Cipher c = getCipher(Cipher.DECRYPT_MODE);
        byte[] decValue = c.doFinal(decodedValue);
        return new String(decValue);
    }

    private String getString(byte[] bytes) throws UnsupportedEncodingException {
        return new String(bytes, "UTF-8");
    }

    private byte[] getBytes(String str) throws UnsupportedEncodingException {
        return str.getBytes("UTF-8");
    }

    private Cipher getCipher(int mode) throws Exception {
        Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] iv = getBytes(IV);
        c.init(mode, generateKey(), new IvParameterSpec(iv));
        return c;
    }

    private Key generateKey() throws Exception {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        char[] password = PASSWORD.toCharArray();
        byte[] salt = getBytes(SALT);

        KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
        SecretKey tmp = factory.generateSecret(spec);
        byte[] encoded = tmp.getEncoded();
        return new SecretKeySpec(encoded, "AES");
    }
}

The Java code is straight forward. The most important part of the code are the constants defined at the top: IV, PASSWORD, and SALT. These constants need to be shared with the C# code, which we will look at next.

C# Source Code
Listing 2 shows the C# source code.  There is a classed named AesBase64Wrapper just like in the Java code and it has identical public methods.  Recall, the responsibility of the class is to provide a convenient wrapper around AES encryption and Base64 encoding.  Base64 encoding is used since encryption produces bytes which don't translate into visible screen characters.  Without Base64 encoding it is harder to determine if Java and C# are producing the same encryption.  Let's take a look at the code in listing 2 in more detail.

Listing 2: C# AesBase64Wrapper class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace EncryptDecryptTest
{
    class Program
    {
        class AesBase64Wrapper
        {
            private static string IV = "IV_VALUE_16_BYTE";
            private static string PASSWORD = "PASSWORD_VALUE";
            private static string SALT = "SALT_VALUE";

            public static string EncryptAndEncode(string raw)
            {
                using (var csp = new AesCryptoServiceProvider())
                {
                    ICryptoTransform e = GetCryptoTransform(csp, true);
                    byte[] inputBuffer = Encoding.UTF8.GetBytes(raw);
                    byte[] output = e.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
                    string encrypted = Convert.ToBase64String(output);
                    return encrypted;
                }
            }

            public static string DecodeAndDecrypt(string encrypted)
            {
                using (var csp = new AesCryptoServiceProvider())
                {
                    var d = GetCryptoTransform(csp, false);
                    byte[] output = Convert.FromBase64String(encrypted);
                    byte[] decryptedOutput = d.TransformFinalBlock(output, 0, output.Length);
                    string decypted = Encoding.UTF8.GetString(decryptedOutput);
                    return decypted;
                }
            }

            private static ICryptoTransform GetCryptoTransform(AesCryptoServiceProvider csp, bool encrypting)
            {
                csp.Mode = CipherMode.CBC;
                csp.Padding = PaddingMode.PKCS7;
                var spec = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(PASSWORD), Encoding.UTF8.GetBytes(SALT), 65536);
                byte[] key = spec.GetBytes(16);


                csp.IV = Encoding.UTF8.GetBytes(IV);
                csp.Key = key;
                if (encrypting)
                {
                    return csp.CreateEncryptor();
                }
                return csp.CreateDecryptor();
            }
        }

        static void Main(string[] args)
        {
            string encryptMe;
            string encrypted;
            string decrypted;

            encryptMe = "please encrypt me";
            Console.WriteLine("encryptMe = " + encryptMe);

            encrypted = AesBase64Wrapper.EncryptAndEncode(encryptMe);
            Console.WriteLine("encypted: " + encrypted);

            decrypted = AesBase64Wrapper.DecodeAndDecrypt(encrypted);
            Console.WriteLine("decrypted: " + decrypted);

            Console.WriteLine("press any key to exit....");
            Console.ReadKey();
        }
    }
}

In Listing 2, see the same three constants: IV, PASSWORD, and SALT.  The values are identical to the Java code, which of course is expected if you want the two code bases to be able to encrypt and decrypt the same.  There is not much more to it than that.  The code to do the encryption and decryption is not very long, but figuring out the right classes and configuration to use is the tricky part.

Conclusion
This article demonstrates AES encryption between Java and C#. I hope this article helped.

References
http://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html

http://docs.oracle.com/javase/7/docs/api/javax/crypto/spec/IvParameterSpec.html

http://docs.oracle.com/javase/7/docs/api/javax/crypto/SecretKeyFactory.html

http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory

http://docs.oracle.com/javase/7/docs/api/javax/crypto/spec/PBEKeySpec.html

http://docs.oracle.com/javase/7/docs/api/javax/crypto/spec/SecretKeySpec.html

http://msdn.microsoft.com/en-us/library/system.security.cryptography.aescryptoserviceprovider%28v=vs.110%29.aspx

http://msdn.microsoft.com/en-us/library/system.security.cryptography.ciphermode%28v=vs.110%29.aspx

http://msdn.microsoft.com/en-us/library/system.security.cryptography.paddingmode%28v=vs.110%29.aspx

http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes%28v=vs.110%29.aspx


stop

9 comments:

  1. Thanks for your code. Work fine
    But i have an issue. To complete the encryptation of a string it takes several seconds or a minute
    is very slow
    Can you give me some advice?

    ReplyDelete
  2. Typically the first time take longer than subsequent encryption attempts. There are a lot of factors so I can't give much advice without more information.

    ReplyDelete
  3. You have same encryption for ios..?

    ReplyDelete
  4. No I'm sorry I don't any encryption code for ios.

    ReplyDelete