AES 256 without JCE Unlimited Strength Jurisdiction Policy Files

Once upon a time I wanted my Java application to encrypt/decrypt some data. I decided to use AES (Advanced Encryption Standard, Rijndael) encryption algorithm with 256-bit key. AES is supported by Java SE by a built-in JCA (Java Cryptography Architecture) provider so in theory no 3rd party library needed. So I turned first to JCA-based solution. However to use 256-bit keys with AES we need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files. At least we have to do it with Sun/Oracle JVM. This document explains cryptographic key limits in JCA and here is an interesting citation:

If stronger algorithms are needed (for example, AES with 256-bit keys), the JCE Unlimited Strength Jurisdiction Policy Files must be obtained and installed in the JDK/JRE.

A working example of a code for encryption/decryption with 256-bit AES using JCA:

import java.nio.charset.Charset;
import java.security.Key;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import net.iharder.Base64;

public class AesExample {

	private static final Charset utf8Charset = Charset.forName("UTF-8");

	// placeholder, real key should be loaded from a key store
	static Key loadAes256bitKey() {
		byte[] aesKey = new byte[32];
		for (int i = 0; i < aesKey.length; ++i) {
			aesKey[i] = (byte)i;
		}
		return new SecretKeySpec(aesKey, "AES");
	}

	// placeholder, real IV should be somehow better
	static byte[] loadIvBytes() {
		byte[] data = new byte[16];
		for (int i = 0; i < data.length; ++i) {
			data[i] = (byte)i;
		}
		return data;
	}	

	// opmode: javax.crypto.Cipher.ENCRYPT_MODE or javax.crypto.Cipher.DECRYPT_MODE
	static Cipher getJcaCipher(int opmode) {
		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			Key key = loadAes256bitKey();
			byte[] iv = loadIvBytes();
			cipher.init(opmode, key, new IvParameterSpec(iv));
			return cipher;
		} catch (Exception ex) {
			throw new RuntimeException("Cannot intialize cipher. " + ex.getMessage(), ex);
		}
	}

	static String encode(String value) {
		try {
			byte[] encoded = getJcaCipher(Cipher.ENCRYPT_MODE).doFinal(value.getBytes(utf8Charset));
			return Base64.encodeBytes(encoded);
		} catch (Exception ex) {
			throw new RuntimeException("Data encryption failed. " + ex.getMessage(), ex);
		}
	}

	static String decode(String encodedStr) {
		try {
			byte[] encoded = Base64.decode(encodedStr);
			byte[] decoded = getJcaCipher(Cipher.DECRYPT_MODE).doFinal(encoded);
			return new String(decoded, utf8Charset);
		} catch (Exception ex) {
			throw new RuntimeException("Data decryption failed. " + ex.getMessage(), ex);
		}
	}

	public static void main(String... args) {

		for (String inputText : Arrays.asList("", "a", "ab", "abc", "And here we have some longer message.")) {
			String encoded = encode(inputText);
			String decoded = decode(encoded);
			System.out.format("%40s => %s => '%s' (%b)\n", inputText, encoded, decoded, inputText.equals(decoded));
		}
	}
}

And maven dependencies for the above:

<dependency>
    <groupId>net.iharder</groupId>
    <artifactId>base64</artifactId>
    <version>2.3.9</version>
</dependency>

NOTE: the above example will not work with Sun/Oracle JVM without JCE Unlimited Strength Jurisdiction Policy Files. Installing these files turned out to be really annoying, at least for me. Solutions:

  1. Use another JVM instead of Oracle’s one, that is allowing stronger cryptography. Here I’ve found something about it (but haven’t tested): http://derjan.io/blog/2013/03/15/nevermind-jce-unlimited-strength-use-openjdk
  2. Use another encryption library to create a solution that is not depending on a JVM used… which is why I’ve created this article.

Bouncy Castle to the rescue!

The JVM-independent solution for using 256-bit AES is to use Bouncy Castle library and stick to its own API. It should not be a problem as the library is really stable and alive. I’ve seen it for the first time in a production-running code in 2007 and the last version of the library was released in December 2016 (as of 09.01.2017 it was 1.56). My solution was based on this handy article: BouncyCastle Lightweight API encryption example by “ofsdeveloper99”.

Let’s add following imports to the above example:

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;

Let’s add following methods:

	static PaddedBufferedBlockCipher getBcCipher(boolean forEncryption) {
		try {
			Key key = loadAes256bitKey();
			byte[] iv = loadIvBytes();
			CipherParameters params = new ParametersWithIV(new KeyParameter(key.getEncoded()), iv);
			PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding());
			cipher.init(forEncryption, params);
			return cipher;
		} catch (Exception ex) {
			throw new RuntimeException("Cannot intialize Bouncy Castle cipher. " + ex.getMessage(), ex);
		}
	}

	static String encode2(String value) {
		try {
			PaddedBufferedBlockCipher cipher = getBcCipher(true);
			byte[] input = value.getBytes(utf8Charset);
			byte[] output = new byte[cipher.getOutputSize(input.length)];
			int len = cipher.processBytes(input, 0, input.length, output, 0);
			len += cipher.doFinal(output, len);
			return Base64.encodeBytes(output, 0, len);
		} catch (Exception e) {
			throw new RuntimeException("Data encryption failed. " + e.getMessage(), e);
		}
	}

	static String decode2(String encodedStr) {
		try {
			PaddedBufferedBlockCipher cipher = getBcCipher(false);
			byte[] encoded = Base64.decode(encodedStr);
			byte[] output = new byte[cipher.getOutputSize(encoded.length)];
			int len = cipher.processBytes(encoded, 0, encoded.length, output, 0);
			len += cipher.doFinal(output, len);
			return new String(output, 0, len, utf8Charset);
		} catch (Exception e) {
			throw new RuntimeException("Data decryption failed. " + e.getMessage(), e);
		}
	}

And let’s change the main method to this:

	public static void main(String... args) {

		for (String inputText : Arrays.asList("", "a", "ab", "abc", "And here we have some longer message.")) {
			String encoded = encode(inputText);
			String encoded2 = encode2(inputText);
			String decoded = decode2(encoded);
			System.out.format("%40s => %s => '%s' (%b, %b)\n", inputText, encoded2, decoded, encoded.equals(encoded2), inputText.equals(decoded));
		}
	}

We need to add this dependency as well:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.56</version>
</dependency>

Now the output should be:

                                         => 6cPvirI0U+bwdJzWNueojg== => '' (true, true)
                                       a => L854J/PY0dXgLD70k+z+sQ== => 'a' (true, true)
                                      ab => JOZH1E2V4fpZ+Ips93ZIfw== => 'ab' (true, true)
                                     abc => 6YtQ2v/uDI5Se7p4Weg3Ew== => 'abc' (true, true)
   And here we have some longer message. => LRY2Ny5DM1v2AMIhx1h7/edAZdHDOS+N0oywoERCSxAHpFvq45LtPC8wNkxapVXY => 'And here we have some longer message.' (true, true)

As you can see, both implementations of encryption/decryption with use of 256-bit AES are interchangeable. But the second one, based on Bouncy Castle, is JVM-independent. Forget JCE Unlimited Strength Jurisdiction Policy Files!!!

Advertisements

About krzysztoftomaszewski

I've got M.Sc. in software engineering. I graduated in 2005 at Institute of Computer Science, Warsaw University of Technology, Faculty of Electronics and Information Technology. I'm working on computer software design and engineering continuously since 2004.
This entry was posted in Java and tagged , , , . Bookmark the permalink.

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