在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 文件,仍然无法直接查看到源码内容。

相关推荐
禹中一只鱼8 分钟前
【IDEA 出现 `IDE error occurred`】
java·ide·spring boot·intellij-idea
卓怡学长13 分钟前
基于 SpringBoot 的生活信息分享平台,从 0 到 1 完整实现(附源码 + 数据库)
java·数据库·spring boot·tomcat·maven
hero.fei17 分钟前
RoaringBitmap在SpringBoot中的使用以及与BitSet对比
java·spring boot·spring
arvin_xiaoting20 分钟前
OpenClaw学习总结_IV_认证与安全_3:Authorization与Policies详解
学习·安全
知识分享小能手21 分钟前
MongoDB入门学习教程,从入门到精通,MongoDB 安全完全指南(19)
学习·安全·mongodb
Lyyaoo.24 分钟前
【JAVA基础面经】线程安全的单例模式
java·安全·单例模式
汽车仪器仪表相关领域24 分钟前
NHXJ-02汽车悬架检验台 实操型实战手册
人工智能·功能测试·测试工具·算法·安全·单元测试·可用性测试
AI服务老曹25 分钟前
异构计算与边缘协同:基于 Spring Boot 的 AI 视频管理平台架构深度解析
人工智能·spring boot·音视频
EasyGBS31 分钟前
国密GB35114+国标GB28181平台EasyGBS双标护航为交通视频监控筑牢安全防线
安全
好大哥呀31 分钟前
如何在Spring Boot中配置数据库连接?
数据库·spring boot·后端