2

To get the basics out of the way, I'm using Eclipse to code and create my executable jar files.

Some background research:
I'm aware that you can decompile jar files and see the source code. In fact, there are free programs out there like JD GUI that can easily decompile a jar file and reveal all its contents. This is an issue especially if you want to distribute a java program.
When I learned about this, I immediately began looking around for a solution, but I couldn't find a straight answer.

The issue:
My question is, how do you encrypt a jar file, or at least encrypt the class file so that it isn't readable if you decompile it?
One solution I keep seeing is that you can obfuscate your code, but the issue with this is that your code still has to be able to run, and therefore the obfuscation can be reverse engineered with a little effort. The only other solution I could think of is having two separate jar files so that one of the jar files is encrypted and requires the other jar file to decrypt it. Now, before you say "they could just decompile the other jar and use it to decrypt the first jar". My idea was using a cipher that would be used to decrypt the first jar. In theory, the user could know the exact decryption method, but it would be completely useless without the secret word (cipher).

My question:
How would I implement this? I'm asking how do I run a jar file to encrypt/decrypt another jar with a user-specified cipher?
Feel free to give example code, I'm looking for any help I can get.

Thanks in advance!

VoidTwo
  • 569
  • 1
  • 7
  • 26
  • See: https://stackoverflow.com/questions/2242055/is-java-code-obfuscation-actually-effective-vs-decompilers – Jacob G. Dec 05 '18 at 00:57
  • 2
    "but it would be completely useless without the secret word (cipher)...." But there is no place to keep the secret that isn't easy to find with basic tools like the jd gui you mentioned. – President James K. Polk Dec 05 '18 at 01:10
  • 1
    This is what software licenses are for... you can't stop someone from reverse engineering your code. Adding an encryption layer does nothing but add an extra step for the attacker. You're going to have to distribute the decryption key in some way to the end user, so what's to stop them from using it to decrypt the JAR? – Jake Holzinger Dec 05 '18 at 01:28
  • Curiosity causes me to ask: What programming concept worth stealing, would not be easier to reverse engineer simply by looking at the inputs and outputs, then forming a spec. from that & handing it to a software engineering team with the instructions "write this app."? As an aside, the software team would likely implement it better than the 'loan wolf' programmer who had the great idea in the first place. Oh, and that would be 100% legal in just about every country. I agree with @JakeHolzinger - a software license is the most practical approach. – Andrew Thompson Dec 05 '18 at 01:47
  • @JamesKPolk, you would be right except the decryption method wouldn't be as simple as if (cipher.equals(secretWord)) {decrypt();} It would be an algorithom using 'math' to decrypt the code using the exact cipher. Look into AES 256 to understand it better. – VoidTwo Dec 05 '18 at 01:56
  • And @JakeHolzinger. This isn't something I want to sell or distribute on a commercial scale. I want to use this like a password protected java program. For myself and a couple friends. – VoidTwo Dec 05 '18 at 02:02
  • @VoidTwo if you really want to do this you could simply run the executable JAR with a command line argument or environment variable that points to an encryption key, then you can implement a custom class loader that reads the encryption key, decrypts the necessary JAR, and loads the JAR it into the Java runtime. – Jake Holzinger Dec 05 '18 at 03:47
  • @JakeHolzinger, thank you. Do you mind demonstrating how I would do that? – VoidTwo Dec 05 '18 at 03:51

1 Answers1

3

Imagine you have a JAR file where some of the classes have been encrypted after compilation.

Let's say you have an encrypted class called SecureMain that you want to decrypt and execute:

public class SecureMain implements Runnable {

    @Override
    public void run() {
        System.out.println("Running...");
    }

}

Given the following JAR file structure:

org
`---stackoveflow
    `---example
        |---EncryptedClassLoader.class
        |---Main.class
        `---SecureMain.enc

Where the files with an enc file extension are encrypted using some secret key.

The EncryptedClassLoader can be implemented to decrypt the class file before it is loaded:

public class EncryptedClassLoader extends ClassLoader {

    private final SecretKeySpec key;

    public EncryptedClassLoader(SecretKeySpec key) {
        this.key = key;
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            String[] segments = name.split("\\.");
            String filePath = String.join("/", segments) + ".enc";
            byte[] bytes = decryptResource(filePath);
            return defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            throw new ClassNotFoundException("Unable to load class: " + name, e);
        }
    }

    private byte[] decryptResource(String filePath) throws Exception {
        File file = new File(getResource(filePath).getFile());
        byte[] bytes = Files.readAllBytes(file.toPath());
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(bytes);
    }

}

Then the Main class can be implemented to load the secret key from the command line arguments, then load an execute the encrypted SecureMain class:

public class Main {

    public static void main(String[] args) throws Exception {
        byte[] bytes = Base64.getDecoder().decode(args[0]);
        SecretKeySpec key = new SecretKeySpec(bytes, "AES");
        ClassLoader loader = new EncryptedClassLoader(key);
        Class<?> cls = loader.loadClass("com.stackoverflow.example.SecureMain");
        Runnable main = (Runnable) cls.newInstance();
        main.run();
    }

}

Finally the JAR should be executed with the secret key passed as an argument.

java -jar myjar.jar <base64 encoded key>
Jake Holzinger
  • 5,783
  • 2
  • 19
  • 33
  • Thank you so much. I'm going to test this later, so for now I'm not going to "accept the answer", just in case someone else has another solution. By the way, is that command executed in cmd/PowerShell? – VoidTwo Dec 05 '18 at 07:43