在SpringBoot 项目简单实现一个 Jar 包加密,防止反编译

在现实场景中,某金融公司开发了一个基于 Spring Boot 的应用程序,该程序用于处理金融数据,具有高敏感性。为了防止该程序的核心代码(如数据加密、交易算法等)被反编译或篡改,公司希望通过加密 JAR 包并在运行时解密的方式来保护核心代码。

为实现这个需求,我们设计了一个简单的加密和解密方案,其中:

  1. 使用对称加密算法(AES)加密 JAR 文件。
  2. 在程序启动时动态解密 JAR 文件,并加载解密后的类。
  3. 将解密密钥存储在安全的密钥管理系统中(例如 AWS KMS 或 Azure Key Vault),并在启动时从安全存储中提取密钥。

实现步骤

1. 加密原始 JAR 文件

在构建完成 Spring Boot 项目后,获得一个原始的 JAR 文件(如 app.jar)。我们首先编写一个 Java 工具类 JarEncryptor,利用 AES 加密算法对该 JAR 文件进行加密。

java 复制代码
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";  // 原始 JAR 文件路径
        String encryptedJarPath = "app_encrypted.jar";  // 加密后的 JAR 文件路径
        String secretKey = generateSecretKey();  // 生成并输出密钥

        System.out.println("Secret Key (Store this securely): " + secretKey);

        encryptJar(jarPath, encryptedJarPath, secretKey);
    }

    // 生成 AES 密钥
    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());
    }

    // 加密 JAR 文件
    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);
            }
        }
    }
}

此程序生成了加密后的 JAR 文件 app_encrypted.jar,同时生成了 AES 密钥。该密钥需要妥善存储在安全的密钥管理系统中。

2. 动态解密和加载 JAR 文件

为了在程序启动时解密并加载 JAR,我们自定义 ClassLoader 来实现动态解密并加载类。在 Spring Boot 项目的启动类中,编写如下代码。

java 复制代码
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 {
        // 从密钥管理系统获取解密密钥(此处硬编码,仅为示例)
        String secretKey = "从密钥管理系统获取的AES密钥";

        // 解密并加载 JAR
        File encryptedJar = new File("app_encrypted.jar");
        SecureClassLoader loader = new SecureClassLoader(secretKey, encryptedJar);

        // 设置自定义 ClassLoader 并启动 Spring Boot 应用
        Thread.currentThread().setContextClassLoader(loader);
        SpringApplication.run(SecureBootApplication.class, args);
    }

    // 自定义 ClassLoader 实现解密逻辑
    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");
        }
    }
}

3. 安全运行和验证

此时,我们完成了加密 JAR 文件的动态解密和加载。完整的项目结构如下:

  • app.jar:原始 JAR 文件。
  • app_encrypted.jar:加密后的 JAR 文件。
  • JarEncryptor:用于加密 JAR 文件的工具类。
  • SecureBootApplication:Spring Boot 项目启动类,包含自定义 ClassLoader

注意事项

  • 密钥管理:解密密钥应当存储在安全的密钥管理系统中,避免直接在代码中硬编码。
  • 性能问题 :由于自定义 ClassLoader 需要解密每个类文件,因此首次加载可能存在性能开销。
  • 防护加强:可结合其他技术手段(如代码混淆、环境监测)提升安全性。

通过上述方案,我们有效防止了核心代码被反编译,即使攻击者获得加密的 JAR 文件,仍然无法直接查看到源码内容。

相关推荐
码界奇点4 分钟前
基于Spring Boot和Vue的多通道支付网关系统设计与实现
vue.js·spring boot·后端·毕业设计·鸿蒙系统·源代码管理
IT 行者4 分钟前
Spring Boot 升级之HTTP客户端调整:HttpExchange 与 Feign Client 深度对比分析
spring boot·后端·http
小蒜学长5 分钟前
python基于Python的医疗机构药品及耗材信息管理系统(代码+数据库+LW)
数据库·spring boot·后端·python
九月生5 分钟前
Spring Boot 自动装配原理深度剖析:以集成 Redis 为例
spring boot·redis
网安INF7 分钟前
防火墙的分类与部署详解
服务器·安全·网络安全·防火墙
invicinble10 分钟前
Spring Boot 内嵌 Tomcat 处理 HTTP 请求的全过程
spring boot·http·tomcat
乾元13 分钟前
AI 驱动的网络攻防演练与安全态势推演——从“规则检测”到“行为级对抗”的工程体系
网络·人工智能·安全·web安全·架构·自动化·运维开发
zhuzewennamoamtf20 分钟前
Linux驱动实现DMA支持
linux·spring boot·spring
踏浪无痕20 分钟前
像挑选书籍一样挑选技术:略读、精读,还是直接跳过?
后端·程序员·架构
金士镧(厦门)新材料有限公司20 分钟前
稀土氧化物:科技与环保领域的重要推动力
科技·安全·全文检索·生活·能源