Jar 包加密

news/2025/2/7 6:36:47/

Jar包加密

一、分类

主流的加密方式有两种:

  1. 字节码混淆
  2. 字节码转换

1.1 字节码混淆

字节码混淆就是对类名、字段名、方法名进行替换,让其变得无意义,使其他人反编译后很难读懂,但并不影响逻辑。

1.2 字节码转换

字节码转换是指对编译后的class文件进行加密,在类加载的时候再解密。加密直接使用加密算法;解密通过类加载器,基于-agentJava:xxx.jar,通过Premain-ClassInstrumentation注入ClassFileTransformer,然后在ClassFileTransformer中解密。

代码混淆,上手最简单,加密级别比较低,也容易破解;字节码转换相对加密安全系数较高,所以我们采用后者进行加密。

二、开源框架

字节码转换方式有两个主流的开源框架:

  1. XJar
  2. ClassFinal

2.1 XJar

可以避免源码泄露以及反编译。该项目的实现方式是对 class 文件完全加密,修改了 jar 中的 META-INF/MANIFEST.MF ,将原有的Main-Class修改为Jar-Main-Class,并增加Main-Class,启动Main-Class时,在原有类加载器的同级别中增加一个自定义的类加载器,通过该类加载器实现加密文件的解密,然后反射调用Jar-Main-Class对应类的 main 方法去启动应用。支持 SpringBoot 应用。

2.2 ClassFinal

整体不错,对 SpringBoot 支持也好,其逻辑就是基于-agentJava:xxx.jar这一套原理,加密时对 class 文件做了两次处理,一次是对 class 文件的字节码完全加密,一次是对 class 文件混淆,这个混淆是保留成员变量和方法,只对方法的内部实现进行隐藏;解密时,判断如果该类是自己加密过的,就找到加密的字节码进行解密,如果不是自己加密的就跳过。其对 class 文件混淆,就是方便 SpringBoot 这种三方框架直接分析 class 文件。


另外针对 Jar 包的一次性加密,可以直接使用该网站加密:在线加密。

三、XJar实践

3.1 功能特性

  1. 无代码侵入,只需要把编译好的 JAR 包通过工具加密即可;
  2. 完全内存解密,降低源码和字节码泄露/反编译的风险;
  3. 支持所有 JDK 内置加解密算法;
  4. 动态生成 Go 启动器, 保护密码不泄露;

3.2 使用步骤

首先导入依赖:

<project><!-- 设置 jitpack.io 仓库 --><repositories><repository><id>jitpack.io</id><url>https://jitpack.io</url></repository></repositories><!-- 添加 XJar 依赖 --><dependencies><dependency><groupId>com.github.core-lib</groupId><artifactId>xjar</artifactId><version>4.0.2</version><!-- <scope>test</scope> --></dependency></dependencies>
</project>

通过测试类对目标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 方法里的参数即为用于加密的密码。

可以得到三个文件:

  1. encrypted.jar
  2. xjar.go
  3. xjar_agentable.go

其中第二个是 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

最后执行该可执行文件即可。

3.3 报错

执行 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 @34340fabat 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.<init>(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

3.4 拓展

如果觉得以上加密操作很麻烦,还需要编写单元测试,XJar 还提供了 maven 插件,可以在打包的同时对 jar 包进行加密。使用方法如下:

<project><!-- 设置 jitpack.io 插件仓库 --><pluginRepositories><pluginRepository><id>jitpack.io</id><url>https://jitpack.io</url></pluginRepository></pluginRepositories><!-- 添加 XJar Maven 插件 --><build><plugins><plugin><groupId>com.github.core-lib</groupId><artifactId>xjar-maven-plugin</artifactId><version>4.0.2</version><executions><execution><goals><goal>build</goal></goals><phase>package</phase><!-- 或使用<phase>install</phase>--><configuration><!-- 不建议将密码放在这里可以放在命令行参数中使用 -Dxjar.password=123456 --><password>123456</password><includes><include>/org/cowave/**/*.class</include></includes><!-- optional<algorithm/><keySize/><ivSize/><excludes><exclude/></excludes><sourceDir/><sourceJar/><targetDir/><targetJar/>--></configuration></execution></executions></plugin></plugins></build>
</project>
参数名称命令参数名称参数说明类型缺省值示例值
password-Dxjar.password密码字符串String必须任意字符串, 123456
algorithm-Dxjar.algorithm加密算法名称StringAES/CBC/PKCS5PaddingJDK内置加密算法, 如:AES/CBC/PKCS5Padding 和 DES/CBC/PKCS5Padding
keySize-Dxjar.keySize密钥长度int128根据加密算法而定, 56, 128, 256
ivSize-Dxjar.ivSize密钥向量长度int128根据加密算法而定, 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 目录会出现三个文件:

  1. demo1-0.0.1.xjar
  2. xjar.go
  3. xjar_agentable.go

继续编译 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实践

4.1 功能特性

  1. 无需修改原项目代码,只要把编译好的 jar/war 包用本工具加密即可
  2. 加密后的 jar 包可直接使用命令运行
  3. 支持加密 WEB-INF/libBOOT-INF/lib 下的依赖 jar 包
  4. 支持机器码绑定,仅允许在指定机器上运行
  5. 支持加密配置文件

4.2 使用步骤

首先点击下载,得到一个 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

4.3 拓展

同样的,classfinal 也支持使用 maven 插件直接打包执行。

<plugin><!-- https://gitee.com/roseboy/classfinal --><groupId>net.roseboy</groupId><artifactId>classfinal-maven-plugin</artifactId><version>1.2.1</version><configuration><password>123456</password><!--加密打包之后pom.xml会被删除,不用担心在jar包里找到此密码--><packages>com.example</packages><cfgfiles>application.yml</cfgfiles><!--<excludes>org.spring</excludes>--><!--<libjars>a.jar,b.jar</libjars>--></configuration><executions><execution><phase>package</phase><goals><goal>classFinal</goal></goals></execution></executions>
</plugin>

运行 mvn package 时会在 target 下自动加密生成 yourpaoject-encrypted.jar 。

前面提到的绑定机器码,可以使用以下命令在指定机器上获取机器码:

java -jar classfinal-fatjar.jar -C

加密时用-code指定机器码。机器绑定可同时支持机器码 + 密码的方式加密。

和 XJar 方式相比,不需要依赖其他环境,加密后直接运行即可,更加方便快捷。

五、总结

道高一尺,魔高一丈;各种加密方式也只是增加逆向工程的难度。我们在实际使用中,在保护好加密密码的同时,也要用其他手段保护我们的项目。

而且这个 XJar ,使用 Google 搜索的第三条就是 Xjar加密破解 😰,原因是都是开源项目,使用者通过研究加密原理可以很容易破解。所以就需要我们对加密方式也保密,让破解者无从下手。

通过比较本文提供的两种开源项目,从简单、易用、安全性考虑,ClassFinal 更胜一筹,推荐使用。


http://www.ppmy.cn/news/44769.html

相关文章

ObjectBox一种基于中心点的无锚点目标检测方法

ObjectBox: From Centers to Boxes for Anchor-Free Object Detection 论文地址&#xff1a;https://arxiv.org/pdf/2207.06985.pdf 官方代码&#xff1a;https://github.com/MohsenZand/ObjectBox 基于中心点的无锚点目标检测方法是一种目标检测方法&#xff0c;其思路是将目…

代码随想录训练营第56天|583.两个字符串的删除操作、72.编辑距离

583.两个字符串的删除操作、72.编辑距离 583.两个字符串的删除操作 思路1–最长公共子序列 对于该题&#xff0c;也许我们可以先求出两个字符串的最长公共子序列&#xff0c;让这两个序列变成这个最长公共子序列即可&#xff0c;这样直接用两个字符串的长度之和减去最长公共…

Vue学习笔记(4. 生命周期)

1. 生命周期写法&#xff08;vue2与vue3比对&#xff09; 创建前&#xff1a;vue3 setup, vue2 beforeCreate //组件创建前执行的函数 创建后&#xff1a;vue3 setup, vue2 created //组件创建后执行的函数 挂载前&#xff1a;vue3 onBeforeMount, vue2 beforeMount //挂…

算法时间复杂度计算

目录 1.时间复杂度计算 1.1 时间复杂度例题 1.1.1例题 1.1.2例题 1.1.3例题 1.1.4例题 1.2时间复杂度leetcode例题 1.时间复杂度计算 首先&#xff0c;我们需要了解时间复杂度是什么&#xff1a;算法的时间复杂度是指算法在编写成可执行程序后&#xff0c;运行时需要耗费…

Java Menu

基本数据类型 Read More 【待重构】java,ruby,mysql 数据类型 运算符 对照学习笔记 和 equals 区别是什么&#xff1f; hashCode() 和 equals() 的关系List.equals和CollectionUtils.isEqualCollection的区别equals、Objects.equals、Objects.deepEquals区别和联系 Java三大特性…

MATLAB 不同格式点云文件的读取与显示(1)

不同格式的点云文件读取与显示(1) 一、背景介绍1.点云数据结构2.文件格式介绍2.1.ply文件2.2.pcd文件2.3.las文件2.4.txt文件二、不同格式文件读取与显示1.ply格式文件2.pcd格式文件3.txt格式文件三、其他操作1.修改显示背景色2.添加图例的显示3.improtdata(teapot.txt)报错一…

联诚发携多款创新产品及解决方案惊艳亮相ISLE 2023展!

这里写自定义目录标题4月7日-9日&#xff0c;ISLE 2023国际智慧显示及系统集成展览会在深圳国际会展中心&#xff08;宝安新馆&#xff09;隆重举行。来自全球各地1000余家企业参与展出&#xff0c;展出面积达8万㎡&#xff0c;吸引了众多业内专家、企业家以及广大观众前来观看…

API 接口应该如何设计?如何保证安全?如何签名?如何防重?

说明&#xff1a;在实际的业务中&#xff0c;难免会跟第三方系统进行数据的交互与传递&#xff0c;那么如何保证数据在传输过程中的安全呢&#xff08;防窃取&#xff09;&#xff1f;除了https的协议之外&#xff0c;能不能加上通用的一套算法以及规范来保证传输的安全性呢&am…