主流的加密方式有两种:
字节码混淆就是对类名、字段名、方法名进行替换,让其变得无意义,使其他人反编译后很难读懂,但并不影响逻辑。
字节码转换是指对编译后的class文件进行加密,在类加载的时候再解密。加密直接使用加密算法;解密通过类加载器,基于-agentJava:xxx.jar,通过Premain-Class向Instrumentation注入ClassFileTransformer,然后在ClassFileTransformer中解密。
代码混淆,上手最简单,加密级别比较低,也容易破解;字节码转换相对加密安全系数较高,所以我们采用后者进行加密。
字节码转换方式有两个主流的开源框架:
可以避免源码泄露以及反编译。该项目的实现方式是对 class 文件完全加密,修改了 jar 中的 META-INF/MANIFEST.MF ,将原有的Main-Class修改为Jar-Main-Class,并增加Main-Class,启动Main-Class时,在原有类加载器的同级别中增加一个自定义的类加载器,通过该类加载器实现加密文件的解密,然后反射调用Jar-Main-Class对应类的 main 方法去启动应用。支持 SpringBoot 应用。
整体不错,对 SpringBoot 支持也好,其逻辑就是基于-agentJava:xxx.jar这一套原理,加密时对 class 文件做了两次处理,一次是对 class 文件的字节码完全加密,一次是对 class 文件混淆,这个混淆是保留成员变量和方法,只对方法的内部实现进行隐藏;解密时,判断如果该类是自己加密过的,就找到加密的字节码进行解密,如果不是自己加密的就跳过。其对 class 文件混淆,就是方便 SpringBoot 这种三方框架直接分析 class 文件。
另外针对 Jar 包的一次性加密,可以直接使用该网站加密:在线加密。
首先导入依赖:
jitpack.io https://jitpack.io com.github.core-lib xjar 4.0.2
通过测试类对目标jar包进行加密处理:
public class MainTest { public static void main(String[] args) throws Exception { XCryptos.encryption() .from("/Users/wshuo/target/demo1-0.0.1-SNAPSHOT.jar") .use("io.xjar") .include("/com/example/**/*.class") .to("/Users/wshuo/Downloads/encrypted.jar"); } }
其中 use 方法里的参数即为用于加密的密码。
可以得到三个文件:
其中第二个是 go 编写的脚本文件,称之为 go 启动器,需要在有 go 开发环境的机器上编译:
go build ./xjar.go
编译完成之后会得到一个xjar的可执行文件,如果是 windows 系统则为xjar.exe。
sudo ./xjar java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED -jar ./demo1-0.0.1-SNAPSHOT.xjar
最后执行该可执行文件即可。
执行 xjar 的时候发现报错:
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field private final jdk.internal.loader.URLClassPath java.net.URLClassLoader.ucp accessible: module java.base does not "opens java.net" to unnamed module @34340fab at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178) at java.base/java.lang.reflect.Field.setAccessible(Field.java:172) at io.xjar.reflection.XReflection.field(XReflection.java:20) at io.xjar.boot.XBootClassLoader.(XBootClassLoader.java:41) at io.xjar.boot.XJarLauncher.createClassLoader(XJarLauncher.java:31) at org.springframework.boot.loader.ExecutableArchiveLauncher.createClassLoader(ExecutableArchiveLauncher.java:109) at org.springframework.boot.loader.Launcher.launch(Launcher.java:55) at io.xjar.boot.XJarLauncher.launch(XJarLauncher.java:26) at io.xjar.boot.XJarLauncher.main(XJarLauncher.java:22) panic: exit status 1
网上给出相关错误原因,以及解决方案,但是未生效。
https://www.coder.work/article/61641
https://stackoverflow.com/questions/41265266
尝试了以下命令,都不行:
sudo ./xjar java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED -jar ./encrypted.jar sudo ./xjar java --permit-illegal-access -jar ./encrypted.jar sudo ./xjar java --illegal-access -jar ./encrypted.jar
报错如下:
Unrecognized option: --illegal-access Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit. panic: exit status 1
执行下面的命令:
sudo ./xjar java --illegal-access=warn --add-opens java.base/jdk.internal.loader=ALL-UNNAMED -jar ./encrypted.jar
这段命令的意思是,在启动虚拟机的时候,增加一个选项表示违法访问,出现以下提示信息:
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option --illegal-access=warn; support was removed in 17.0
说明强制非法访问,已经在 JDK17 中移除了。不可以再使用 illegal-access 选项来访问 JDK 的内部元素。
出现这个错误的原因是 JDK9 往后引入了Java Platform Module System(模块化)的概念,每个模块都是强封装的,而我们启动 JAR 包需要用到反射去访问目标类,这里提示没有权限;那我们只能在命令里增加参数,来特定打开某些需要打开的包才能正常启动项目,下面的命令增加了启动参数,JAR 包可以正常执行。
sudo ./xjar java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED -jar ./encrypted.jar
如果觉得以上加密操作很麻烦,还需要编写单元测试,XJar 还提供了 maven 插件,可以在打包的同时对 jar 包进行加密。使用方法如下:
jitpack.io https://jitpack.io com.github.core-lib xjar-maven-plugin 4.0.2 build package 123456 /org/cowave/**/*.class
参数名称 | 命令参数名称 | 参数说明 | 类型 | 缺省值 | 示例值 |
---|---|---|---|---|---|
password | -Dxjar.password | 密码字符串 | String | 必须 | 任意字符串, 123456 |
algorithm | -Dxjar.algorithm | 加密算法名称 | String | AES/CBC/PKCS5Padding | JDK内置加密算法, 如:AES/CBC/PKCS5Padding 和 DES/CBC/PKCS5Padding |
keySize | -Dxjar.keySize | 密钥长度 | int | 128 | 根据加密算法而定, 56, 128, 256 |
ivSize | -Dxjar.ivSize | 密钥向量长度 | int | 128 | 根据加密算法而定, 128 |
sourceDir | -Dxjar.sourceDir | 源jar所在目录 | File | ${project.build.directory} | 文件目录 |
sourceJar | -Dxjar.sourceJar | 源jar名称 | String | ${project.build.finalName}.jar | 文件名称 |
targetDir | -Dxjar.targetDir | 目标jar存放目录 | File | ${project.build.directory} | 文件目录 |
targetJar | -Dxjar.targetJar | 目标jar名称 | String | ${project.build.finalName}.xjar | 文件名称 |
includes | -Dxjar.includes | 需要加密的资源路径表达式 | String[] | 无 | io/xjar/** , mapper/*Mapper.xml , 支持Ant表达式 |
excludes | -Dxjar.excludes | 无需加密的资源路径表达式 | String[] | 无 | static/** , META-INF/resources/** , 支持Ant表达式 |
AES一般指高级加密标准,它是当今使用最广泛的对称分组密码算法之一;DES算法为密码体制中的对称密码体制,又被称为美国数据加密标准。
加密操作的命令就简化为了:
mvn xjar:build -Dxjar.password=io.xjar
和打包操作一起:
mvn clean install -Dxjar.password=io.xjar
命令执行完成之后会在编译 target 目录会出现三个文件:
继续编译 go 文件:
go build ./xjar.go
编译完成之后得到一个 xjar 文件,执行下面的命令:
sudo ./xjar java --add-opens java.base/jdk.internal.loader=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED -jar ./demo1-0.0.1.xjar
项目即可正常启动。
首先点击下载,得到一个 classfinal-fatjar-1.2.1.jar 文件。
然后执行加密操作:
java -jar classfinal-fatjar-1.2.1.jar -file demo1-0.0.1.jar -packages com.example -pwd 123456 -Y
-Y 表示跳过人机交互的提示信息。
参数说明 -file 加密的jar/war完整路径 -packages 加密的包名(可为空,多个用","分割) -libjars jar/war包lib下要加密jar文件名(可为空,多个用","分割) -cfgfiles 需要加密的配置文件,一般是classes目录下的yml或properties文件(可为空,多个用","分割) -exclude 排除的类名(可为空,多个用","分割) -classpath 外部依赖的jar目录,例如/tomcat/lib(可为空,多个用","分割) -pwd 加密密码,如果是#号,则使用无密码模式加密 -code 机器码,在绑定的机器生成,加密后只可在此机器上运行 -Y 无需确认,不加此参数会提示确认以上信息
执行命令:
java -javaagent:demo1-0.0.1-encrypted.jar='-pwd 123456' -jar demo1-0.0.1-encrypted.jar
同样的,classfinal 也支持使用 maven 插件直接打包执行。
net.roseboy classfinal-maven-plugin 1.2.1 123456 com.example application.yml package classFinal
运行 mvn package 时会在 target 下自动加密生成 yourpaoject-encrypted.jar 。
前面提到的绑定机器码,可以使用以下命令在指定机器上获取机器码:
java -jar classfinal-fatjar.jar -C
加密时用-code指定机器码。机器绑定可同时支持机器码 + 密码的方式加密。
和 XJar 方式相比,不需要依赖其他环境,加密后直接运行即可,更加方便快捷。
道高一尺,魔高一丈;各种加密方式也只是增加逆向工程的难度。我们在实际使用中,在保护好加密密码的同时,也要用其他手段保护我们的项目。
而且这个 XJar ,使用 Google 搜索的第三条就是 Xjar加密破解 😰,原因是都是开源项目,使用者通过研究加密原理可以很容易破解。所以就需要我们对加密方式也保密,让破解者无从下手。
通过比较本文提供的两种开源项目,从简单、易用、安全性考虑,ClassFinal 更胜一筹,推荐使用。