• 作者:老汪软件技巧
  • 发表时间:2024-11-10 10:01
  • 浏览量:

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

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

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

在构建完成 Spring Boot 项目后,获得一个原始的 JAR 文件(如 app.jar)。我们首先编写一个 Java 工具类 JarEncryptor,利用 AES 加密算法对该 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";  // 原始 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 项目的启动类中,编写如下代码。

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 文件的动态解密和加载。完整的项目结构如下:

注意事项

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