XJar: Spring-Boot JAR 包加/解密工具,避免源碼泄露以及反編譯。

XJar

GitHub: https://github.com/core-lib/xjar

Spring Boot JAR 安全加密運行工具,同時支持的原生JAR。

基于對JAR包內資源的加密以及拓展ClassLoader來構建的一套程序加密啟動,動態解密運行的方案,避免源碼泄露或反編譯。

功能特性

  • 無需侵入代碼,只需要把編譯好的JAR包通過工具加密即可。
  • 完全內存解密,杜絕源碼以及字節碼泄露或反編譯。
  • 支持所有JDK內置加解密算法。
  • 可選擇需要加解密的字節碼或其他資源文件,避免計算資源浪費。

環境依賴

JDK 1.7 +

使用步驟

<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>v2.0.6</version>
        </dependency>
    </dependencies>
</project>
// Spring-Boot Jar包加密
String password = "io.xjar";
XKey xKey = XKit.key(password);
XBoot.encrypt("/path/to/read/plaintext.jar", "/path/to/save/encrypted.jar", xKey);
// 危險加密模式,即不需要輸入密碼即可啟動的加密方式,這種方式META-INF/MANIFEST.MF中會保留密鑰,請謹慎使用!
String password = "io.xjar";
XKey xKey = XKit.key(password);
XBoot.encrypt("/path/to/read/plaintext.jar", "/path/to/save/encrypted.jar", xKey, XConstants.MODE_DANGER);
// Spring-Boot Jar包解密
String password = "io.xjar";
XKey xKey = XKit.key(password);
XBoot.decrypt("/path/to/read/encrypted.jar", "/path/to/save/decrypted.jar", xKey);
// Jar包加密
String password = "io.xjar";
XKey xKey = XKit.key(password);
XJar.encrypt("/path/to/read/plaintext.jar", "/path/to/save/encrypted.jar", xKey);
// 危險加密模式,即不需要輸入密碼即可啟動的加密方式,這種方式META-INF/MANIFEST.MF中會保留密鑰,請謹慎使用!
String password = "io.xjar";
XKey xKey = XKit.key(password);
XJar.encrypt("/path/to/read/plaintext.jar", "/path/to/save/encrypted.jar", xKey, XConstants.MODE_DANGER);
// Jar包解密
String password = "io.xjar";
XKey xKey = XKit.key(password);
XJar.decrypt("/path/to/read/encrypted.jar", "/path/to/save/decrypted.jar", xKey);

啟動命令

// 命令行運行JAR 然后在提示輸入密碼的時候輸入密碼后按回車即可正常啟動
java -jar /path/to/encrypted.jar
// 也可以通過傳參的方式直接啟動,不太推薦這種方式,因為泄露的可能性更大!
java -jar /path/to/encrypted.jar --xjar.password=PASSWORD
// 對于 nohup 或 javaw 這種后臺啟動方式,無法使用控制臺來輸入密碼,推薦使用指定密鑰文件的方式啟動
nohup java -jar /path/to/encrypted.jar --xjar.keyfile=/path/to/xjar.key

參數說明

參數名稱 參數含義 缺省值 說明
--xjar.password 密碼
--xjar.algorithm 密鑰算法 AES 支持JDK所有內置算法,如AES / DES ...
--xjar.keysize 密鑰長度 128 根據不同的算法選取不同的密鑰長度。
--xjar.ivsize 向量長度 128 根據不同的算法選取不同的向量長度。
--xjar.keyfile 密鑰文件 ./xjar.key 密鑰文件相對或絕對路徑。

密鑰文件

密鑰文件采用properties的書寫格式:

password: PASSWORD
algorithm: ALGORITHM
keysize: KEYSIZE
ivsize: IVSIZE
hold: HOLD

其中 algorithm/keysize/ivsize/hold 均有缺省值,當 hold 值不為 true | 1 | yes | y 時,密鑰文件在讀取后將自動刪除。

參數名稱 參數含義 缺省值 說明
password 密碼 密碼字符串
algorithm 密鑰算法 AES 支持JDK所有內置算法,如AES / DES ...
keysize 密鑰長度 128 根據不同的算法選取不同的密鑰長度。
ivsize 向量長度 128 根據不同的算法選取不同的向量長度。
hold 是否保留 false 讀取后是否保留密鑰文件。

進階用法

默認情況下,即沒有提供過濾器的時候,將會加密所有資源其中也包括項目其他依賴模塊以及第三方依賴的 JAR 包資源,
框架提供使用過濾器的方式來靈活指定需要加密的資源或排除不需要加密的資源。

  • 硬編碼方式

// 假如項目所有類的包名都以 com.company.project 開頭,那只加密自身項目的字節碼即可采用以下方式。
XBoot.encrypt(
        "/path/to/read/plaintext.jar", 
        "/path/to/save/encrypted.jar", 
        "io.xjar", 
        (entry) -> {
            String name = entry.getName();
            String pkg = "com/company/project/";
            return name.startsWith(pkg);
        }
    );
  • 表達式方式

// 1. 采用Ant表達式過濾器更簡潔地來指定需要加密的資源。
XBoot.encrypt(plaintext, encrypted, password, new XJarAntEntryFilter("com/company/project/**"));

XBoot.encrypt(plaintext, encrypted, password, new XJarAntEntryFilter("mapper/*Mapper.xml"));

XBoot.encrypt(plaintext, encrypted, password, new XJarAntEntryFilter("com/company/project/**/*API.class"));

// 2. 采用更精確的正則表達式過濾器。
XBoot.encrypt(plaintext, encrypted, password, new XJarRegexEntryFilter("com/company/project/(.+)"));

XBoot.encrypt(plaintext, encrypted, password, new XJarRegexEntryFilter("mapper/(.+)Mapper.xml"));

XBoot.encrypt(plaintext, encrypted, password, new XJarRegexEntryFilter("com/company/project/(.+)/(.+)API.class"));
  • 混合方式

當過濾器的邏輯復雜或條件較多時可以將過濾器分成多個,并且使用 XKit 工具類提供的多個過濾器混合方法混合成一個,XKit 提供 “與” “或” “非” 三種邏輯運算的混合。

// 1. 與運算,即所有過濾器都滿足的情況下才滿足,mix() 方法返回的是this,可以繼續拼接。
XEntryFilter and = XKit.and()
    .mix(new XJarAntEntryFilter("com/company/project/**"))
    .mix(new XJarAntEntryFilter("*/**.class"));

XEntryFilter all = XKit.all()
    .mix(new XJarAntEntryFilter("com/company/project/**"))
    .mix(new XJarAntEntryFilter("*/**.class"));

// 2. 或運算,即任意一個過濾器滿足的情況下就滿足,mix() 方法返回的是this,可以繼續拼接。
XEntryFilter or = XKit.or()
    .mix(new XJarAntEntryFilter("com/company/project/**"))
    .mix(new XJarAntEntryFilter("mapper/*Mapper.xml"));

XEntryFilter any = XKit.any()
    .mix(new XJarAntEntryFilter("com/company/project/**"))
    .mix(new XJarAntEntryFilter("mapper/*Mapper.xml"));

// 3. 非運算,即除此之外都滿足,該例子中即排除項目或其他模塊和第三方依賴jar中的靜態文件。
XEntryFilter not  = XKit.not(
        XKit.or()
            .mix(new XJarAntEntryFilter("static/**"))
            .mix(new XJarAntEntryFilter("META-INF/resources/**"))
);

注意事項

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <!-- 需要將executable和embeddedLaunchScript參數刪除,目前還不能支持對該模式Jar的加密!后面將會支持該方式的打包。 
    <configuration>
        <executable>true</executable>
        <embeddedLaunchScript>...</embeddedLaunchScript>
    </configuration>
    -->
</plugin>

Spring Boot + JPA(Hibernate) 問題

如果項目中使用了 JPA 且實現為Hibernate時,由于Hibernate自己解析加密后的Jar文件,所以無法正常啟動,
可以采用以下解決方案

  1. clone XJar-Agent-Hibernate ,使用 mvn clean package 編譯出 xjar-agent-hibernate-${version}.jar 文件
  2. 采用 java -javaagent:xjar-agent-hibernate-${version}.jar -jar your-spring-boot-app.jar 命令啟動

靜態文件瀏覽器無法加載完成問題

由于靜態文件被加密后文件體積變大,Spring Boot 會采用文件的大小作為 Content-Length 頭返回給瀏覽器,
但實際上通過 XJar 加載解密后文件大小恢復了原本的大小,所以瀏覽器認為還沒接收完導致一直等待服務端。
由此我們需要在加密時忽略靜態文件的加密,實際上靜態文件也沒加密的必要,因為即便加密了用戶在瀏覽器
查看源代碼也是能看到完整的源碼的。通常情況下靜態文件都會放在 static/ 和 META-INF/resources/ 目錄下,
我們只需要在加密時通過過濾器排除這些資源即可,可以采用以下的過濾器:

XKit.not(
        XKit.or()
            .mix(new XJarAntEntryFilter("static/**"))
            .mix(new XJarAntEntryFilter("META-INF/resources/**"))
);

或通過插件配置排除

<plugin>
    <groupId>com.github.core-lib</groupId>
    <artifactId>xjar-maven-plugin</artifactId>
    <version>v2.0.6</version>
    <executions>
        <execution>
            <goals>
                <goal>build</goal>
            </goals>
            <phase>package</phase>
            <!-- 或使用
            <phase>install</phase>
            -->
            <configuration>
                <password>io.xjar</password>
                <excludes>
                    <exclude>static/**</exclude>
                    <exclude>META-INF/resources/**</exclude>
                </excludes>
            </configuration>
        </execution>
    </executions>
</plugin>

插件集成

XJar-Maven-Plugin
GitHub: https://github.com/core-lib/xjar-maven-plugin

對于Spring Boot 項目或模塊,該插件要后于 spring-boot-maven-plugin 插件執行,有兩種方式:

  • 將插件放置于 spring-boot-maven-plugin 的后面,因為其插件的默認 phase 也是 package
  • 將插件的 phase 設置為 install(默認值為:package),打包命令采用 mvn clean install
<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>v2.0.6</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                        </goals>
                        <phase>package</phase>
                        <!-- 或使用
                        <phase>install</phase>
                        -->
                        <configuration>
                            <password>io.xjar</password>
                            <includes>
                                <include>com/company/project/**</include>
                                <include>mapper/*Mapper.xml</include>
                            </includes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

也可以通過Maven命令執行

mvn xjar:build -Dxjar.password=io.xjar
mvn xjar:build -Dxjar.password=io.xjar -Dxjar.targetDir=/directory/to/save/target.xjar

但通常情況下是讓XJar插件綁定到指定的phase中自動執行,這樣就能在項目構建的時候自動構建出加密的包。

mvn clean package -Dxjar.password=io.xjar
mvn clean install -Dxjar.password=io.xjar -Dxjar.targetDir=/directory/to/save/target.xjar

參數說明

參數名稱 命令參數名稱 參數說明 參數類型 缺省值 示例值
password -Dxjar.password 密碼字符串 String 必須 任意字符串,io.xjar
algorithm -Dxjar.algorithm 加密算法名稱 String AES JDK內置加密算法,如:AES / DES
keySize -Dxjar.keySize 密鑰長度 int 128 根據加密算法而定,56,128,256
ivSize -Dxjar.ivSize 密鑰向量長度 int 128 根據加密算法而定,128
mode -Dxjar.mode 加密模式 int 0 0:普通模式 1:危險模式(免密碼啟動)
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[] com/company/project/** , mapper/*Mapper.xml , 支持Ant表達式
excludes -Dxjar.excludes 無需加密的資源路徑表達式 String[] static/** , META-INF/resources/** , 支持Ant表達式

注意:

當 includes 和 excludes 同時使用時即加密在includes的范圍內且排除了excludes的資源。

更多文檔:XJar-Maven-Plugin

版本記錄

  • v2.0.6
    1. 解決多jar包啟動時無法找到準確的MANIFEST.MF導致無法正常啟動的問題
  • v2.0.5
    1. 升級LoadKit依賴版本
    2. 修復ANT表達式無法正確匹配*/通配符的問題
  • v2.0.4
    1. 解決危險模式不支持ubuntu系統的問題
  • v2.0.3
    1. 過濾器泛型協變支持
    2. xjar-maven-plugin 支持 includes 與 excludes 同時起效,當同時設置時即加密在includes范圍內但又不在excludes范圍內的資源
  • v2.0.2
    1. 原生jar增加密鑰文件的啟動方式,解決類似 nohup 和 javaw 的后臺啟動方式無法通過控制臺輸入密碼的問題
  • v2.0.1
    1. 增加密鑰文件的啟動方式,解決類似 nohup 和 javaw 的后臺啟動方式無法通過控制臺輸入密碼的問題
    2. 修復解密后沒有刪除危險模式中在MANIFEST.MF中保留的密鑰信息
  • v2.0.0
    1. 支持內嵌JAR包資源的過濾加解密
    2. 不兼容v1.x.x的過濾器表達式,統一采用相對于 classpath 資源URL的過濾表達式
  • v1.1.4
    1. 支持 Spring-Boot 以ZIP方式打包,即依賴外部化方式啟動。
    2. 修復無加密資源時無法啟動問題
  • v1.1.3
    1. 實現危險模式加密啟動,即不需要輸入密碼!
    2. 修復無法使用 System.console(); 時用 new Scanner(System.in) 替代。
  • v1.1.2
    1. 避免用戶由于過濾器使用不當造成無法啟動的風險
  • v1.1.1
    1. 修復bug
  • v1.1.0
    1. 整理目錄結構
    2. 增加正則表達式/Ant表達式過濾器和“非”(!)邏輯運算過濾器
    3. 將XEntryFilters工具類整合在XKit類中
    4. 缺省過濾器情況下Spring-Boot JAR包加密的資源只限定在 BOOT-INF/classes/ 下
  • v1.0.9
    1. 修復對Spring-Boot 版本依賴的bug
  • v1.0.8
    1. 支持以Maven插件方式集成
  • v1.0.7
    1. 將sprint-boot-loader依賴設為provide
    2. 將XEntryFilter#filter(E entry); 變更為XEntryFilter#filtrate(E entry);
    3. 將Encryptor/Decryptor的構造函數中接收多個過濾器參數變成接收一個,外部提供XEntryFilters工具類來實現多過濾器混合成一個,避免框架自身的邏輯限制了使用者的過濾邏輯實現。
  • v1.0.6
    1. 采用LoadKit作為資源加載工具
  • v1.0.5
    1. 支持并行類加載,需要JDK1.7+的支持,可提升多線程環境類加載的效率
    2. Spring-Boot JAR 包加解密增加一個安全過濾器,避免無關資源被加密造成無法運行
    3. XBoot / XJar 工具類中增加多個按文件路徑加解密的方法,提升使用便捷性
  • v1.0.4 小優化
  • v1.0.3 增加Spring-Boot的FatJar加解密時的缺省過濾器,避免由于沒有提供過濾器時加密后的JAR包不能正常運行。
  • v1.0.2 修復中文及空格路徑的問題
  • v1.0.1 升級detector框架
  • v1.0.0 第一個正式版發布

協議聲明

Apache-2.0

加入群聊

QQ 950956093

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,925評論 6 342
  • 入門 介紹 Spring Boot Spring Boot 使您可以輕松地創建獨立的、生產級的基于 Spring ...
    Hsinwong閱讀 16,941評論 2 89
  • 昨天很晚女兒看我還在讀書有點不高興,雖然嘴上沒說但能看出來。 早飯時她吃著稀飯和牛肉羅卜包子,直夸好吃。問...
    真情姐妹蔣鳳娟閱讀 189評論 0 3
  • “如何戰勝強迫性思維”。 這是一個心理學,研究的是如何對待我們不想要的想法。《白熊實驗》是一本對心理學的學科發展有...
    忎染閱讀 1,254評論 0 0