核心是仅加密常量池中带@Encrypted注解或ENC_前缀的敏感字符串,用AES-128-CBC加密并附加HMAC-SHA256签名,通过自定义ClassLoader在findClass中解密,密钥通过JVM参数或KMS注入且严格隔离。
核心是让敏感类在磁盘上始终以密文存在,仅在 JVM 加载瞬间解密并进入内存,且明文生命周期极短。不加密整个 class 文件,只加密常量池中真正敏感的字符串字面量——比如 API 密钥、SQL 模板、硬编码 Token 等。
整文件 AES 加密会破坏 ClassFile 格式,JVM 直接抛 NoClassDefFoundError。正确做法是用 ASM 或 Javassist 扫描编译后的 .class,在常量池(Constant Pool)中定位带 @Encrypted 注解或以 ENC_ 开头的 CONSTANT_Utf8_info 条目:
"sk_test_xxx")替换成 Base64 编码的密文块,格式类似 "ENC_aGVsbG8=|iv:abcd|hmac:xyz"
javap 查看,结构合法,但敏感值不可读继承 ClassLoader,重写 findClass,跳过双亲委派,接管目标类加载全流程:
getResourceAsStream(name.replace('.', '/') + ".class") 读取加密字节流ASMClassReader 解析字节码,遍历常量池,识别 ENC_ 标记项super.defineClass(name, decryptedBytes, 0, len)
Arrays.fill(keyBytes, (byte) 0) 清空密钥内存密钥是整个方案最脆弱的一环,任何硬编码都会让保护形同虚设:
-Dapp.key=xxx
CLASS_ENCRYPT_KEY_PROD),启动时动态加载keyBytes 泄露Spring、Hibernate、Servlet 容器等会提前加载大量类,稍有不慎就会触发初始化失败:
static final String 字段Class.forName 或反射直接引用的类,否则类加载链路中断@ComponentScan)做白名单过滤,确保非敏感类走默认加载器-verbose:class 观察实际加载顺序,及时发现隐式依赖