Information Security 8 min read

Encrypting and Dynamically Decrypting Spring Boot JAR Files for Secure Deployment

This article demonstrates a practical approach to protect sensitive Spring Boot applications by encrypting the JAR with AES, storing the key in a secure vault, and using a custom ClassLoader to decrypt and load classes at runtime, while highlighting key management, performance, and additional hardening considerations.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Encrypting and Dynamically Decrypting Spring Boot JAR Files for Secure Deployment

In a real‑world scenario, a financial company needs to protect the core code of a Spring Boot application (e.g., data encryption, transaction algorithms) from reverse engineering. The solution encrypts the JAR file with symmetric AES, stores the decryption key in a secure key‑management service (such as AWS KMS or Azure Key Vault), and dynamically decrypts the JAR at startup.

Step 1 – Encrypt the original JAR

After building the Spring Boot project and obtaining app.jar , a Java utility JarEncryptor is used to encrypt the JAR with AES. The tool generates an AES key and writes the encrypted JAR as app_encrypted.jar .

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.SecureRandom;
import java.util.Base64;

public class JarEncryptor {
    private static final String AES = "AES";
    private static final int KEY_SIZE = 128;

    public static void main(String[] args) throws Exception {
        String jarPath = "app.jar"; // original JAR path
        String encryptedJarPath = "app_encrypted.jar"; // encrypted JAR path
        String secretKey = generateSecretKey(); // generate and output key
        System.out.println("Secret Key (Store this securely): " + secretKey);
        encryptJar(jarPath, encryptedJarPath, secretKey);
    }

    // generate AES key
    public static String generateSecretKey() throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance(AES);
        keyGen.init(KEY_SIZE, new SecureRandom());
        SecretKey secretKey = keyGen.generateKey();
        return Base64.getEncoder().encodeToString(secretKey.getEncoded());
    }

    // encrypt JAR file
    public static void encryptJar(String jarPath, String encryptedJarPath, String secretKey) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(secretKey), AES);
        Cipher cipher = Cipher.getInstance(AES);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        try (FileInputStream fis = new FileInputStream(jarPath);
             FileOutputStream fos = new FileOutputStream(encryptedJarPath);
             CipherOutputStream cos = new CipherOutputStream(fos, cipher)) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                cos.write(buffer, 0, bytesRead);
            }
        }
    }
}

The program outputs the encrypted JAR ( app_encrypted.jar ) and the AES key, which must be stored securely.

Step 2 – Dynamic decryption and loading

A custom ClassLoader is created to decrypt the encrypted JAR at runtime. The Spring Boot entry class uses this loader to set the thread context class loader before launching the application.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.security.Key;
import java.util.Base64;
import java.util.jar.JarInputStream;
import java.util.jar.JarEntry;

@SpringBootApplication
public class SecureBootApplication {

    public static void main(String[] args) throws Exception {
        // Retrieve decryption key from key management system (hard‑coded here for demo)
        String secretKey = "
";

        // Decrypt and load JAR
        File encryptedJar = new File("app_encrypted.jar");
        SecureClassLoader loader = new SecureClassLoader(secretKey, encryptedJar);

        // Set custom ClassLoader and start Spring Boot
        Thread.currentThread().setContextClassLoader(loader);
        SpringApplication.run(SecureBootApplication.class, args);
    }

    // Custom ClassLoader implementing decryption logic
    static class SecureClassLoader extends ClassLoader {
        private final Key secretKey;
        private final File encryptedJarFile;

        public SecureClassLoader(String base64Key, File encryptedJarFile) throws Exception {
            this.secretKey = new SecretKeySpec(Base64.getDecoder().decode(base64Key), "AES");
            this.encryptedJarFile = encryptedJarFile;
        }

        @Override
        protected Class
findClass(String name) throws ClassNotFoundException {
            try (JarInputStream jarIn = new JarInputStream(new FileInputStream(encryptedJarFile))) {
                JarEntry entry;
                Cipher cipher = Cipher.getInstance("AES");
                cipher.init(Cipher.DECRYPT_MODE, secretKey);
                while ((entry = jarIn.getNextJarEntry()) != null) {
                    if (entry.getName().replace("/", ".").equals(name + ".class")) {
                        byte[] encryptedClassData = jarIn.readAllBytes();
                        byte[] classData = cipher.doFinal(encryptedClassData);
                        return defineClass(name, classData, 0, classData.length);
                    }
                }
            } catch (Exception e) {
                throw new ClassNotFoundException("Class " + name + " not found or decryption failed", e);
            }
            throw new ClassNotFoundException("Class " + name + " not found");
        }
    }
}

Step 3 – Secure operation and verification

The final project structure includes the original app.jar , the encrypted app_encrypted.jar , the JarEncryptor utility, and the SecureBootApplication with its custom ClassLoader.

Key considerations

Key management: Store decryption keys in a secure vault; never hard‑code them in source code.

Performance impact: Custom ClassLoader decrypts each class on first load, which may add overhead.

Additional hardening: Combine with code obfuscation, environment monitoring, and other techniques to further protect the application.

By following this approach, the core code is protected from decompilation; even if an attacker obtains the encrypted JAR, they cannot directly view the source.

JavaSpring BootClassLoaderSecure CodingAESJAR encryption
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.