工作原理
ClassFinal-maven-plugin
插件通过调用Java Native Interface(JNI
)实现对Java字节码的加密。具体来说,它会在编译阶段对类文件进行混淆和加密,然后在运行时动态解密这些类文件。
首先,插件会遍历项目中的所有类文件,并对其进行混淆处理。混淆过程包括重命名类名、方法名、字段名以及改变控制流结构等,目的是使反编译后的代码难以阅读。
接着,插件会对混淆后的类文件进行加密处理。加密过程采用了一种名为CFProtect
的算法,该算法基于AES
加密标准,具有较高的安全性。加密后的类文件存储为二进制格式,不能直接被Java虚拟机加载。当class被classloader加载时,真正的方法体会被解密注入。 运用的是Java Agent
可以去实现字节码插桩、动态跟踪分析。比如skywalking、arms
等就是Java Agent
技术。
最后,插件会生成一个代理模块(agent module)
,该模块负责在应用程序启动时加载,并负责解密加密的类文件。代理模块采用JVMTI(Java Virtual Machine Tool Interface)
技术实现,可以在运行时监控和控制Java虚拟机的行为。
开源地址,目前已经暂停维护了。
加密
命令加密方式
参数说明:
java -jar classfinal-fatjar-1.2.1.jar -file yourproject.jar \
-libjars a.jar,b.jar -packages com.yourpackage,com.yourpackage2 \
-exclude com.yourpackage.Main -pwd yourpassword -Y
- -file 加密的jar/war完整路径
- -packages 加密的包名(可为空,多个用","分割)
- -libjars jar/war包lib下要加密jar文件名(可为空,多个用","分割)
- -cfgfiles 需要加密的配置文件,一般是classes目录下的yml或properties文件(可为空,多个用","分割)
- -exclude 排除的类名(可为空,多个用","分割)
- -classpath 外部依赖的jar目录,例如/tomcat/lib(可为空,多个用","分割)
- -pwd 加密密码,如果是#号,则使用无密码模式加密
- -code 机器码,在绑定的机器生成,加密后只可在此机器上运行。在待部署机器上,执行java -jar classfinal-fatjar-1.2.1.jar -C命令生成机器码
- -Y 无需确认,不加此参数会提示确认以上信息
下载classfinal-fatjar-1.2.1.jar(下载地址)
加密效果
1.加密后,方法体被清空,保留方法参数、注解等信息.主要兼容swagger文档注解扫描
2.方法体被清空后,反编译只能看到方法名和注解,看不到方法体的具体内容
3.加密后的项目需要设置javaagent来启动,启动过程中解密class,完全内存解密,不留下任何解密后的文件
4.启动加密后的jar,生成xxx-encrypted.jar,这个就是加密后的jar文件,加密后不可直接执行
5.无密码启动方式,java-javaagent:xxx-encrypted.jar-jarxxx-encrypted.jar
6.有密码启动方式,java-javaagent:xxx-encrypted.jar=‘-pwd=密码’-jarxxx-encrypted.jar
结果: 生成 yourpaoject-encrypted.jar,这个就是加密后的jar文件;加密后的文件不可直接执行,需要配置javaagent。
注: 以上示例是直接用参数执行,也可以直接执行 java -jar classfinal-fatjar.jar按照步骤提示输入信息完成加密。
maven插件加密方式
<plugin>
<!--
1.加密后,方法体被清空,保留方法参数、注解等信息.主要兼容swagger文档注解扫描
2.方法体被清空后,反编译只能看到方法名和注解,看不到方法体的具体内容
3.加密后的项目需要设置javaagent来启动,启动过程中解密class,完全内存解密,不留下任何解密后的文件
4.启动加密后的jar,生成xxx-encrypted.jar,这个就是加密后的jar文件,加密后不可直接执行
5.无密码启动方式,java-javaagent:xxx-encrypted.jar-jarxxx-encrypted.jar
6.有密码启动方式,java-javaagent:xxx-encrypted.jar='-pwd=密码'-jarxxx-encrypted.jar
-->
<!-- 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配置要加入的包名,会把下面的所有类加密-->
<packages>com.yulang.proguard.demo.utils,com.yulang.proguard.demo.controller</packages>
<cfgfiles>*.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。
maven
插件的参数名称与直接运行的参数相同,请参考上节的参数说明。
无密码模式
加密时-pwd参数设为#,启动时可不用输入密码; 如果是war包,启动时指定参数 -nopwd,跳过输密码过程。
机器绑定
机器绑定只允许加密的项目在特定的机器上运行;
在需要绑定的机器上执行以下命令,生成机器码
java -jar classfinal-fatjar.jar -C
加密时用-code指定机器码。机器绑定可同时支持机器码+密码的方式加密。
启动加密后的jar
加密后的项目需要设置javaagent来启动,项目在启动过程中解密class,完全内存解密,不留下任何解密后的文件。
解密功能已经自动加入到 yourpaoject-encrypted.jar中,所以启动时-javaagent
与-jar
相同,不需要额外的jar包。
启动jar项目执行以下命令:
java -javaagent:yourpaoject-encrypted.jar='-pwd=0000000' -jar yourpaoject-encrypted.jar
//参数说明
// -pwd 加密项目的密码
// -pwdname 环境变量中密码的名字
或者不加pwd参数直接启动,启动后在控制台里输入密码,推荐使用这种方式:
java -javaagent:yourpaoject-encrypted.jar -jar yourpaoject-encrypted.jar
使用nohup命令启动时,如果系统支持gui,会弹出输入密码的界面,如果是纯命令行下,不支持gui,则需要在同级目录下的classfinal.txt或yourpaoject-encrypted.classfinal.txt中写入密码,项目读取到密码后会清空此文件。
密码读取顺序已经改为:参数获取密码||环境变量获取密码||密码文件获取密码||控制台输入密码||GUI输入密码||退出
具体操作流程
pom.xml
配置
<plugin>
<!--
1.加密后,方法体被清空,保留方法参数、注解等信息.主要兼容swagger文档注解扫描
2.方法体被清空后,反编译只能看到方法名和注解,看不到方法体的具体内容
3.加密后的项目需要设置javaagent来启动,启动过程中解密class,完全内存解密,不留下任何解密后的文件
4.启动加密后的jar,生成xxx-encrypted.jar,这个就是加密后的jar文件,加密后不可直接执行
5.无密码启动方式,java-javaagent:xxx-encrypted.jar-jarxxx-encrypted.jar
6.有密码启动方式,java-javaagent:xxx-encrypted.jar='-pwd=密码'-jarxxx-encrypted.jar
-->
<!-- 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配置要加入的包名,会把下面的所有类加密-->
<packages>com.zhubayi.classfinaldemo.controller,com.zhubayi.classfinaldemo.entity</packages>
<!-- <cfgfiles>application.properties</cfgfiles>-->
<excludes>org.spring</excludes>
<!-- <libjars>a.jar,b.jar</libjars>-->
<cfgfiles>*.yml</cfgfiles>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>classFinal</goal>
</goals>
</execution>
</executions>
</plugin>
使用mvn clean package
进行打包
生成了加密后的jar包。classfinal-demo-0.0.1-SNAPSHOT-encrypted.jar
反编译查看classfinal-demo-0.0.1-SNAPSHOT-encrypted.jar
直接使用java -jar
启动
java -jar classfinal-demo-0.0.1-SNAPSHOT-encrypted.jar
发送请求返回数据为空
- 启动包加密之后,方法体被清空,保留方法参数、注解等信息。
- 反编译只能看到方法名和注解,看不到方法体的具体内容。
- 启动过程中解密class,完全内存解密,不留下任何解密后的文件。
正确启动
无密码启动
java -javaagent:yourproject-encrypted.jar -jar yourproject-encrypted.jar
有密码启动
java -javaagent:yourproject-encrypted.jar='-pwd=密码' -jar yourproject-encrypted.jar