Protecting Java Codes: Encrypted Class Loader

Cloud services are adopted by both start-ups and enterprises in recent years. However, it comes security issues. At this point, developed codes differ from the data. Critical data should be stored as encrypted. On the other hand, developed codes are mostly installed on server vulnerably. For istance, Java projects could be installed on a server as a jar/ear extention file. This files include java classes hierarchically. However, there are several decompilers extract original java codes from class files.

a-beautiful-mind-crypto
Russell Crowe as John Nash

What if the developed code includes patentable algorithm? An enterprise might protect its intellectual property. In this case, installing the project on a server directly would be like turkeys voting for Christmas. So, what we are saying is that we should encrypt the important codes just as critical data, store them in cloud database, and decrypt it on runtime to protect intellectual property. In this way, custom codes would be still secure even if the cloud system is invaded because encryption key would not be stored on cloud system.

Suppose that we would like to protect the following code.

public class CoreClass {
 public static void main(String[] args) {
  System.out.println("hello world!");
 }
}

First of all, we need to compile it and generate a class file. Running a java file generates its class in Eclipse IDE. Alternatively, you might execute javac command in command prompt to generate class. The content of standard class file is partially readable and understanable as mentioned below. That’s why, these files are reversible. PS: pay attention that source file does not include package definition line.

plain_class
Plain Class Content

The following code would encrypt the class file.

public class ClassEncryption {
 public static void main(String[] args) throws Exception{
  String path = "C:\\crypto";
  String classname = "CoreClass";
  String algorithm = "AES";
  byte[] key = {75, 110, -45, -61, 101, -103, -26, -25, 55, -70, 19, 51, 66, -91, -35, 19}; //128 bit key

  System.out.println(Arrays.toString(key));
  encrypt(path, classname, algorithm, key);
 }
 public static void encrypt(String path, String classname, String algorithm, byte[] key) throws Exception{
  Path file = Paths.get(path+"\\"+classname+".class");
  byte[] content = Files.readAllBytes(file);
  Cipher encryption = Cipher.getInstance(algorithm);
  encryption.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, 0, key.length, algorithm));
  byte[] encryptedContent = encryption.doFinal(content);
  writeToFile(path, classname, encryptedContent);
 }
 public static void writeToFile(String path, String filename, byte[] content) throws Exception{
  FileOutputStream out = new FileOutputStream(path+"\\encrypted\\"+filename+".class");
  out.write(content);
  out.close();
 }
}

Encrypted class content is neither readable nor understanable anymore as indicated below.

encrypted_class
Encrypted Class Content

Of course, encryption key should be generated randomly with the following code instead of using static key.

KeyGenerator generator = KeyGenerator.getInstance(algorithm);
generator.init(128); //generate 128 bit key
SecretKey secretKey = generator.generateKey();
byte[] key = secretKey.getEncoded();

Now, we need to develop custom class loader as illustrated below. Standard class loader is responsible for loading classes. This action is handled by findClass method. We would overwrite this method, and put decryption process before defining class.

public class EncryptedClassLoader extends ClassLoader{
 public Class findClass(String path, String name, String algorithm, byte[] key) throws Exception{
  byte[] b = loadClassData(path, name, algorithm, key);
  return defineClass(name, b, 0, b.length);
 }

 private byte[] loadClassData(String path, String name, String algorithm, byte[] key) throws Exception {
  Path file = Paths.get(path+"\\"+name+".class");
  byte[] encryptedContent = Files.readAllBytes(file);
  //System.out.println(new String(encryptedContent));

  Cipher decryption = Cipher.getInstance(algorithm);
  decryption.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, 0, key.length, algorithm));
  byte[] decryptedContent = decryption.doFinal(encryptedContent);
  //System.out.println(new String(decryptedContent));

  return decryptedContent;
 }
}

Finally, we could decrypt a class file on runtime as shown below.

public class CryptoCodeProject {
 public static void main(String[] args) throws Exception{
  String path = "C:\\crypto\\encrypted"; //source of encrypted file
  String classname = "CoreClass"; //name of encrypted file
  String algorithm = "AES";
  byte[] key = {75, 110, -45, -61, 101, -103, -26, -25, 55, -70, 19, 51, 66, -91, -35, 19}; //128 bit key

  EncryptedClassLoader myClassLoader = new EncryptedClassLoader();
  Class dynamicClass = myClassLoader.findClass(path, classname, algorithm, key);

  Method m = dynamicClass.getMethod("main", String[].class);
  m.invoke(null, new Object[] {null});
 }
}

Thus, core class is performed successfully. hello world! is dumped even though executed code doesn’t include this action.

encrypted-class-loader-output
Encrypted Class Greets The World!

So, we’ve mentinoned how to protect important codes on an external system. AES algorithm is applied to secure the system. AES and other Symetric key algorithms deliver high performance. That’s why, decryption can be handled on runtime very fast. Finally, I’ve shared the project on my GitHub profile.

Hopefully, you would not be like turkeys voting for Christmas.

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