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

Vlog

You can either continue to read this tutorial or watch the following video. They both cover the same topic.


🙋‍♂️ You may consider to enroll my top-rated cryptography course on Udemy

Public Key Cryptography From Scratch

Java Class Loader

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

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

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});
 }
}

Running

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

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.


Like this blog? Support me on Patreon

Buy me a coffee


10 Comments

  1. Hello Sefik,

    Thanks a lot for sharing this. It works perfectly when I launch it. But I have a problem when I try to create the executable jar. It throws a compiling error:

    Class files on classpath not found or not accessible for : /path/of/Main.java
    ( Main.java is my encrypted class)

    In the CryptoCodeProject I look for the encrypted Main.java file path as follow:

    ClassLoader loader = CryptoCodeProject.class.getClassLoader();
    String path = loader.getResource(“nameofpackage”).toString().replace(“file:/”, “”); //source of encrypted file
    I dont Know what I’m doing wrong. Cau you please help me ?

    Thanks lot for your help.

    Kind regards

    Samuel

    1. Hello,

      In eclipse, right click to project > export > runnable jar file > activate the option “package required libraries into generated jar”. In this screen launch configuration should be “CryptoCodeProject – Crypto”. I picked export destination as my desktop and named the jar as crypto.jar. This generates a jar file on my desktop. BTW, encrypted class file is stored in the path C:\\crypto\\encrypted. You should confirm that.

      Then, you should open a command prompt and switch the current location you are working to your desktop. Now, only you need is to run the following command:

      java -cp crypto.jar com.crypto.CryptoCodeProject

      This will return “hello world!”

      I hope that following these steps would solve your problem. Please inform me about the solution.

  2. Hello Sefik,

    Thanks a lot for your comments. I’ve made a test and it works 100% OK.

    It was a very good article.

    Kind regards,

    Samuel

  3. Hi,

    Thanks for sharing this.

    I have doubt, how to protect custom class loader. Why I am asking this is if some body decompile our custom class loader, and change the logic to write decrypted class to a file, then they can decompile our secured code right.

    Then how do we over come that problem.

    Best regards,
    Janardhan.

    1. You’ve mentioned an important point. This cannot protect you class loader level attacks. This just aims to protect your code in repository level.

  4. Hi,

    Thank you so much for clariying that.

    Is there any way to overcome such class loader level attacks.
    Can you through some light on it please.

    Thanks in advance.

    Best regards,
    Janardhan

  5. Exception in thread “main” java.lang.NoClassDefFoundError: CoreClass (wrong name: com/Encript/CoreClass)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:757)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:636)
    at com.Encript.EncryptedClassLoader.findClass(EncryptedClassLoader.java:13)
    at com.Encript.CryptoCodeProject.main(CryptoCodeProject.java:13)

    1. It seems that you’ve encrypted a java file belonging to package com.Encrypt.CoreClass. You should not put package line in the java code. I’ve mentioned this in the post: “PS: pay attention that source file does not include package definition line.”

Comments are closed.