源盾(CodeShield)用户使用手册
版本:v1.8.2
适用对象:使用源盾保护 Java/Kotlin 字节码的研发、运维与安全工作工程师。
目录
- 源盾是什么?(30 秒入门)
- 第一章 快速上手
- 1.1 先认识三个关键词
- 1.2 第一步:拿到许可证密钥
- 1.3 第二步:安装源盾插件
- 1.4 第三步:生成包裹密码
- 1.5 第四步:加密构建
- 1.6 第五步:运行验证
- 1.7 第一次就卡住了?常见问题速查
- 第二章 原理与整体流程
- 2.1 工作原理:构建期加密,运行期解密
- 2.2 加密产物"三件套"与部署
- 2.3 密码体系:包裹密码如何保护密钥
- 2.4 性能影响:解密只发生在类装载时
- 第三章 由浅入深的示例
- 3.1 示例 1:命令行工具 JAR(最简单)
- 3.2 示例 2:Spring Boot 应用
- 3.3 示例 3:字符串加密与排除规则
- 3.4 示例 4:混合模式 + 名称混淆(进阶)
- 第四章 加密模式与混淆策略
- 4.1 决策图总览
- 4.2 M1 全加密
- 4.3 M2 方法体加密(默认)
- 4.4 M1 + M2 混合模式
- 4.5 名称混淆:加密之外的第二道锁
- 4.6 加密范围:白名单模型
- 4.7 自动分析:bceAnalyze
- 第五章 许可证与授权
- 5.1 许可证概述
- 5.2 配置许可证密钥
- 5.3 在线校验与机器激活
- 5.4 离线许可证文件
- 5.5 查看本机硬件指纹
- 5.6 许可证常见问题
- 第六章 Gradle 插件详解
- 6.1 插件应用方式
- 6.2
bceProtect {}DSL 配置项 - 6.3 任务清单
- 6.4 常见 Gradle 错误与排查
- 第七章 Maven 插件详解
- 7.1 插件坐标与生命周期绑定
- 7.2 常用配置参数
- 7.3 常见 Maven 错误与排查
- 第八章 CLI 独立使用
- 8.1
bce-protect参数说明 - 8.2 加密流程示例
- 8.3 提取与使用 Native Agent
- 第九章 高级功能
- 9.1 字符串加密与
excludeStrings - 9.2 名称混淆详解与映射文件
- 9.3 依赖 JAR 同步加密:
extraJars - 9.4 运行时启动与 Native Agent 参数
- 第十章 框架兼容性
- 10.1 Spring / Spring Boot
- 10.2 JPA / Hibernate
- 10.3 CGLIB / AOP
- 10.4 Jackson / 序列化
- 10.5 OSGi / 热部署
- 第十一章 常见问题与故障排查
- 11.1 构建阶段错误
- 11.2 运行时错误
- 11.3 Agent DLL 相关错误
- 11.4 性能与兼容性问题
- 11.5 从哪里获取 agent 动态链接库?
- 附录
- 附录 A:插件 ID 与 Maven 坐标速查表
- 附录 B:DSL / XML 配置示例合集
- 附录 C:术语表
源盾是什么?(30 秒入门)
一句话:给你的 Java / Kotlin 程序"加锁"。
用反编译工具(JD-GUI、jadx 等)打开一个普通 JAR,源码逻辑一目了然——别人可以轻松抄走你的核心算法、业务规则。源盾在构建时把 class 文件加密,加密后的 JAR 反编译出来全是乱码 / 占位符 / 无意义符号;程序运行时,由 Native Agent 在类加载时透明解密,业务代码零改动。
整个过程其实只有三步:
- 拿到许可证密钥(商业授权,每次加密构建必须校验);
- 生成包裹密码(源盾自动生成的一串随机字符,不是自己设的);
- 配置插件 → 加密构建 → 挂 Agent 运行。
阅读指引
- 想直接上手:跟 第一章 快速上手 走一遍;
- 想先搞懂原理:看 第二章 原理与整体流程;
- 想边看边做:直接跟 第三章 由浅入深的示例;
- 查具体配置 / 排错:用左侧目录直达对应章节。
第一章 快速上手
1.1 先认识三个关键词
| 关键词 | 大白话解释 | 详见 |
|---|---|---|
| 许可证密钥(license key) | 源盾的"入场券"。只在加密构建时校验,加密后的程序运行时不校验、不联网。向塔尔旺科技商务购买获取。 | 第五章 |
| 包裹密码(password) | 源盾自动生成的"钥匙",用于派生加解密密钥。构建脚本里只出现包裹后的字符串,永远没有明文密码。 | 1.4 |
| Native Agent | 运行时的"开锁人"。启动命令挂载后,JVM 类加载时自动解密,业务代码零改动。 | 9.4 |
1.2 第一步:拿到许可证密钥
源盾是商业授权产品,每次加密构建都会校验许可证密钥,未配置或校验失败时构建直接中止。
- 向塔尔旺科技商务购买,获取许可证密钥,格式:
XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX-XX; - 也支持 在线购买入口;
- 支持在线校验(默认,首次使用自动激活当前机器)与离线许可证文件(无外网环境)两种方式,详见 第五章。
保密提示:许可证密钥等同授权凭证,请勿提交到公开仓库或随产品分发给最终客户。
重要:许可证密钥只在加密构建时使用。加密完成后的受保护 JAR,在客户机器上运行时不需要 许可证密钥、不会做任何许可证校验、不联网——运行时只有 Native Agent 负责解密,与许可证密钥无关。 所以你可以放心地把加密后的程序分发给任意客户 / 部署到任意环境。
1.3 第二步:安装源盾插件
源盾提供 Gradle 和 Maven 两种插件,按你的构建工具二选一(不用构建工具、想独立加密,见 第八章 CLI 独立使用)。
环境准备
| 组件 | 最低要求 |
|---|---|
| JDK | 8+(源码/目标 1.8) |
| Gradle | 7+(插件使用场景) |
| Maven | 3.6+(插件使用场景) |
| 操作系统 | Windows x64 / Linux x86_64 / macOS arm64(Apple Silicon,macOS 14+);插件内嵌三平台二进制,自动按系统提取 |
| 许可证密钥 | 有效的源盾许可证密钥(1.2) |
Gradle
settings.gradle(pluginManagement 必须是该文件第一条语句):
pluginManagement {
repositories {
mavenCentral() // 源盾插件从这里解析
gradlePluginPortal() // 其他插件兜底,保留
}
}
build.gradle:
plugins {
id 'java'
id 'com.telecwin.codeshield.jvm' version '1.8.2'
}
v1.8.1 起插件发布在 Maven Central。若不便修改
settings.gradle,也可用buildscript {} + apply plugin方式引入,见 6.1 插件应用方式。
Maven
pom.xml:
<plugin>
<groupId>com.telecwin.codeshield</groupId>
<artifactId>codeshield-maven-plugin</artifactId>
<version>1.8.2</version>
<executions>
<execution>
<goals>
<goal>protect</goal>
<goal>extract-agent</goal>
</goals>
</execution>
</executions>
</plugin>
Maven 默认从 Maven Central 解析插件,无需额外配置仓库。
插件内嵌 Windows / Linux / macOS 三平台二进制,自动按系统提取,无需关心平台差异。
1.4 第三步:生成包裹密码
一句话:包裹密码是源盾自动生成的"钥匙",作用是加强密码安全性、让构建脚本里永远不出现明文密码。
它解决什么问题?
加密 JAR 需要一把"密码"来派生密钥。如果直接把密码明文写进 build.gradle / pom.xml,它就会出现在构建脚本、CI 日志、代码仓库里——任何看到脚本的人,都等于拿到了你的加解密密钥(明文密码进脚本,等于把保险箱钥匙贴在保险箱上)。
源盾的包裹机制是这样做的:
- 源盾用
SecureRandom自动生成一个 16 字节的高熵随机密码(2^128 种可能,无法猜测、无法暴力破解); - 用工具内置的密钥把它包裹(加密)成一串 60 字符的随机字符;
- 你只需要把包裹后的字符串写进构建脚本——插件、CLI、Agent 内置同一把密钥,运行时自动解开包裹还原密码;
- 即使脚本 / CI 日志泄露,别人拿到的也只是包裹串:没有内置密钥解不开,随机密码本身也无法猜测。
所以:包裹密码 = 源盾生成的高熵密码 + 包裹编码后的产物。它不是你自己任意设置的, 设置它的目的就是不让密码以明文形式出现。
为什么不能自己随便设?
| 自己设密码 | 源盾自动生成 + 包裹 | |
|---|---|---|
| 明文暴露 | 必须把明文写进脚本,否则工具不知道 | 脚本里只有包裹串,永不明文 |
| 密码强度 | 大概率是弱密码(短、常见词),易被猜测 / 暴力破解 | 16 字节 SecureRandom 高熵随机,不可猜测 |
| 重复使用 | 多项目容易复用同一密码,一处泄露处处泄露 | 每次生成都不同;且每次包裹用随机 IV,两次包裹结果不相关 |
| 丢失恢复 | —— | 无法找回,只能重新生成并重新加密(见下) |
怎么生成?
Gradle:
./gradlew bceGeneratePassword --no-daemon
Maven:
mvn com.telecwin.codeshield:codeshield-maven-plugin:1.8.2:generate-password
输出示例(每次随机):
============================================================
BCE 加密密码(请妥善保存,丢失将无法解密)
============================================================
RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ=
============================================================
注意事项
- 妥善保存:包裹密码是解密的唯一凭证。丢失无法找回,只能重新生成,并用新密码重新加密(旧密码加密的产物作废)。
- 加密与运行必须用同一个:加密构建和运行时
-agentpath参数必须使用同一个包裹密码。 - 每次生成都不同:包裹密码是随机生成的,每次运行
bceGeneratePassword的结果都不一样。 - 禁止明文密码:源盾不接受明文密码,未使用包裹形态的密码会被插件 / CLI / Agent 直接拒绝。
1.5 第四步:加密构建
把许可证密钥和包裹密码填入配置,执行加密。
Gradle(build.gradle):
bceProtect {
licenseKey = 'YOUR-LICENSE-KEY' // 1.2 拿到的许可证密钥
password = 'RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ=' // 1.4 生成的包裹密码
}
./gradlew bceProtect --no-daemon
产物:build/libs/<artifact>-<version>-protected.jar,同目录自动输出 bce_agent.dll。
Maven(pom.xml,在 1.3 的插件声明中补上 configuration):
<configuration>
<licenseKey>YOUR-LICENSE-KEY</licenseKey>
<password>RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ=</password>
</configuration>
mvn package
产物:target/<artifact>-<version>-protected.jar,target/bce_agent.dll。
1.6 第五步:运行验证
加密后的 JAR 不能直接 java -jar 启动,需要挂 Native Agent:
java -agentpath:./bce_agent.dll=RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ= \
-jar build/libs/myapp-1.0.0-protected.jar
验证保护效果:用 JD-GUI / jadx 打开 *-protected.jar,方法体显示为乱码 / 占位符——保护生效。
Linux / macOS 的启动命令,以及 Spring Boot、Tomcat、Docker 等部署形态的挂载方式, 见 9.4 运行时启动与 Native Agent 参数。
1.7 第一次就卡住了?常见问题速查
| 现象 | 原因 | 处理 |
|---|---|---|
Plugin ... was not found |
插件仓库未配置 | settings.gradle 的 pluginManagement 加 mavenCentral(),见 1.3 |
BCE license key must be configured |
未配置许可证密钥 | 配置 licenseKey 或环境变量 BCE_LICENSE_KEY |
Plaintext password rejected |
密码未使用包裹形态 | 用 bceGeneratePassword 生成后替换,见 1.4 |
Online license validation failed |
连不上许可证服务器 | 检查网络 / 防火墙放行 api.license.telecwin.com;或改用离线 bce.lic(见 5.4) |
运行报 BCEProtected class file cannot be loaded |
没挂 Agent | 启动命令加 -agentpath:...,见 1.6 |
| Gradle 命令卡住 / 挂起 | 未加 --no-daemon |
所有 Gradle 命令追加 --no-daemon |
第二章 原理与整体流程
2.1 工作原理:构建期加密,运行期解密
三个要点:
- 构建期:源盾在打包时对 class 文件加密,产出"受保护 JAR";反编译只能看到乱码 / 占位符 / 无意义符号;
- 运行期:启动命令挂载 Native Agent(
-agentpath:<agent>=<包裹密码>),JVM 在类加载时自动解密,业务代码零改动; - 必须配对:加密时用的包裹密码,运行时必须用同一个,否则解密失败。
2.2 加密产物"三件套"与部署
一次加密构建产出三样东西,部署时需要一起交付:
| 产物 | 说明 |
|---|---|
*-protected.jar |
加密后的 JAR,平台无关,可分发到任意平台 |
| Agent 动态库 | bce_agent.dll(Windows)/ libbce_agent.so(Linux)/ libbce_agent.dylib(macOS),按目标机器平台选择 |
| 包裹密码 | 与加密时同一个,运行时通过 -agentpath:...=<密码> 传入 |
常见部署形态的挂载方式见 9.4(含 Docker / Kubernetes 用
JAVA_TOOL_OPTIONS 注入的写法)。
加密保护报告(Gradle)
每次加密构建后,Gradle 插件自动生成加密保护报告,用于核对哪些类被加密、交付审计:
- 位置:
build/reports/bce-protect-report.txt(构建日志会打印[BCE] Report file: ...路径); - 内容:生成时间、加密的 JAR 清单(输入/输出)、类总数 / 全量加密类数 / 方法级加密类数、
字符串加密与名称混淆开关,以及具体加密类清单(
[V1]全量加密 /[V2]方法级加密 /[OBF]名称混淆类)。
建议:加密后先看报告——确认敏感类都出现在
[V1]/[V2]/[OBF]清单中,再交付。
2.3 密码体系:包裹密码如何保护密钥
加解密用的密钥并不是"包裹密码"本身,而是一条从包裹密码派生的密钥链:
包裹密码(构建脚本中,60 字符随机串)
│ ① 工具内置密钥解开包裹
▼
明文密码(16 字节,仅存在于工具进程内存)
│ ② 派生主密钥
▼
主密钥
│ ③ 为每个类单独派生
▼
类密钥 × N(每个 class 一把,加密该类字节码)
- 包裹层(①)解决"密码怎么安全交给工具":内置密钥封装在工具二进制内,用户无需保管,脚本里永不出现明文;
- 派生层(②③)解决"一个密码保护所有类":每个类使用独立的类密钥,单个类被攻破不影响其他类;
- 所以包裹密码丢失 = 整条密钥链断裂:无法找回,只能重新生成并重新加密。
2.4 性能影响:解密只发生在类装载时
结论:Native Agent 对程序性能几乎没有影响。
原理:解密只发生在类装载时的一次性操作——JVM 首次加载某个类时,Agent 解密一次并缓存, 之后该类直接复用;运行期的每次方法调用、每次业务执行都不再涉及解密。类装载是 JVM 启动阶段的 固有步骤,解密只是其中极小的一个环节。
实测数据(内部性能测试):
| 指标 | 实测值 |
|---|---|
| 单个类解密开销(微基准) | ~2 μs/类 |
| 端到端 JVM 启动 | 加密后 ≈ 未加密的 1.07× |
| 运行期开销 | 零(解密结果缓存,之后不再解密) |
所以:加密带来的开销集中在启动阶段的类装载环节,实测仅约 1.07×(几乎感知不到); 运行期没有任何额外开销,业务执行速度不受影响。
第三章 由浅入深的示例
以下示例按难度递进,全部基于真实的插件用法。建议逐个跟随,第 4 个示例是"加密 + 混淆"的组合推荐形态。
3.1 示例 1:命令行工具 JAR(最简单)
场景:一个纯 Java 命令行工具,无框架依赖。核心是一个许可证校验类,不希望被别人反编译抄走。
源码 src/main/java/com/example/tools/LicenseValidator.java:
package com.example.tools;
public class LicenseValidator {
private static final String SECRET = "internal-license-seed-2026";
/** 核心校验算法:反编译后应当不可见 */
public static boolean isValid(String key) {
if (key == null || !key.startsWith("CS-")) return false;
return key.length() == 16 && key.substring(3).chars().allMatch(Character::isDigit);
}
}
配置 build.gradle(最小配置):
plugins {
id 'java'
id 'com.telecwin.codeshield.jvm' version '1.8.2'
}
bceProtect {
licenseKey = 'YOUR-LICENSE-KEY'
password = 'RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ='
}
加密并运行:
./gradlew bceProtect --no-daemon
java -agentpath:./bce_agent.dll=RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ= \
-jar build/libs/tool-1.0.0-protected.jar
验证保护效果:用 JD-GUI / jadx 打开 tool-1.0.0-protected.jar,LicenseValidator 的方法体是乱码 / 占位符,看不到校验逻辑。
默认走 M2 方法体加密(4.3):保留类结构,仅加密方法体与字符串。
3.2 示例 2:Spring Boot 应用
场景:一个 Spring Boot Web 应用,有 Controller 和内部逻辑。框架要扫描 Bean,所以必须用兼容框架的 M2 模式。
源码(参考 springboot-test 项目):
package com.example.demo;
import org.springframework.web.bind.annotation.*;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(@RequestParam(defaultValue = "world") String name) {
return "Hello, " + name + "! This app is protected by CodeShield.";
}
@GetMapping("/secret")
public String secret() {
String key = "codeshield-demo-secret-value-2026";
return "Protected secret: " + key;
}
}
配置 pom.xml(Maven 项目):
<plugin>
<groupId>com.telecwin.codeshield</groupId>
<artifactId>codeshield-maven-plugin</artifactId>
<version>1.8.2</version>
<configuration>
<licenseKey>YOUR-LICENSE-KEY</licenseKey>
<password>RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ=</password>
</configuration>
<executions>
<execution>
<goals>
<goal>protect</goal>
<goal>extract-agent</goal>
</goals>
</execution>
</executions>
</plugin>
加密并运行:
mvn package
java -agentpath:./bce_agent.dll=RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ= \
-jar target/demo-0.0.1-SNAPSHOT-protected.jar
验证:访问 http://localhost:8080/hello 与 /secret,功能与加密前完全一致(Spring 扫描、参数绑定、嵌套 JAR 均正常)。
3.3 示例 3:字符串加密与排除规则
场景:在示例 1 基础上,发现反编译后 SECRET = "internal-license-seed-2026" 这样的字符串常量依然可见(M2 只加密方法体,常量池中的字符串默认也加密,但部分框架前缀的字符串需要排除,否则会破坏框架运行)。
配置 build.gradle:
bceProtect {
licenseKey = 'YOUR-LICENSE-KEY'
password = 'RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ='
encryptStrings = true
excludeStrings = [
'java.',
'javax.',
'org.springframework.',
'org.hibernate.',
'com.fasterxml.jackson.',
'jakarta.',
'org.slf4j.',
'ch.qos.logback.',
'org.apache.tomcat.'
]
}
要点:
encryptStrings = true(默认开启):常量池中用户可见字符串替换为等长占位符,反编译显示为________,运行时由 Agent 还原;excludeStrings:框架前缀的字符串不能加密(类名、注解、反射字符串等),否则框架找不到类 / 注解而报错。上面的列表是常用框架前缀,可直接使用。
验证:重新反编译,internal-license-seed-2026 已变为 ___...___;程序运行正常。
3.4 示例 4:混合模式 + 名称混淆(进阶)
场景:核心算法类(CryptoHelper、LicenseValidator)用 M1 全加密(整个 class 变 Stub,连方法签名都看不到),业务类用 M2;再叠加名称混淆,把私有方法 / 字段名打乱——"加密管内容、混淆管名字",双重保护。
M1 白名单 bce-full-encrypt.yaml:
# bce-full-encrypt.yaml
fullEncryptClasses:
- com.example.tools.CryptoHelper
- com.example.tools.LicenseValidator
excludePackages:
- com.example.controller
- com.example.entity
配置 build.gradle:
bceProtect {
licenseKey = 'YOUR-LICENSE-KEY'
password = 'RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ='
configFile = 'bce-full-encrypt.yaml' // M1 白名单(未列出的类自动走 M2)
// ===== 名称混淆:加密之外的第二道锁 =====
obfuscationEnabled = true
obfuscationClasses = ['com.example.tools.LicenseValidator']
obfuscationGenerateMapping = true // 输出 mapping.txt 供排查定位
}
执行并验证:
./gradlew bceProtect --no-daemon
CryptoHelper/LicenseValidator:整个类被加密为 Stub(M1);- 其余类:方法体加密(M2);
LicenseValidator的私有方法 / 字段名被打乱为a、b、c(混淆);build/bce/obfuscation-mapping.txt:记录原始名 → 混淆名的映射,排查问题用。
第四章 加密模式与混淆策略
4.1 决策图总览
两种加密模式 + 一种进阶组合 + 一把"第二道锁":
| 方案 | 保留的内容 | 反编译后能看到 | 适合 |
|---|---|---|---|
| M2 方法体加密(默认) | 完整类结构 + 方法签名 | 类名、方法名、字段名、字符串(未排除时加密),方法体为乱码 | 绝大多数类,尤其是被框架扫描 / 反射的类 |
| M1 全加密 | 仅类名 | 只剩一个空壳类名,其余全部不可见 | 纯内部工具类、算法、License 校验 |
| M1 + M2 混合 | 按类分别指定 | 核心类空壳,业务类方法体乱码 | 同一 JAR 内两类都有 |
| + 名称混淆 | 名字也被打乱 | 方法名 / 字段名变成 a b c |
任何想防"逻辑结构分析"的场景 |
4.2 M1 全加密
- 对整个 class 文件加密,替换为仅保留类名的 Stub;
- 反编译后只剩类名,方法、字段、注解、字节码全部不可见;
- 不适合 Spring 组件、抽象类、枚举、注解、SPI 类(框架无法扫描);
- 适合:纯内部工具类、加密算法、License 校验、离线工具。
4.3 M2 方法体加密(默认)
- 保留完整类结构(类名、注解、字段、方法签名、接口);
- 仅加密方法体与字符串常量;
- 兼容 Spring、JPA、AOP/CGLIB、Jackson 等框架;
- 默认输出模式,密钥派生高度优化,启动开销极低。
4.4 M1 + M2 混合模式
通过 YAML 显式指定 M1 类,其余类自动走 M2:
# bce-full-encrypt.yaml
fullEncryptClasses:
- com.example.util.CryptoHelper
- com.example.algorithm.AESUtil
excludePackages:
- com.example.controller
- com.example.entity
excludeClasses:
- com.example.*Test
在 build.gradle 中引用:
bceProtect {
password = '...'
configFile = 'bce-full-encrypt.yaml'
}
4.5 名称混淆:加密之外的第二道锁
加密(M1 / M2)管"内容",名称混淆管"名字"——两者结合才是最保险的。
为什么光加密不够?
以 M2 为例:方法体的字节码被加密了,但类名、方法名、字段名、字符串常量依然保留。 反编译后攻击者虽然看不到实现,却仍能看出:
- 哪个类负责支付、哪个类负责密钥管理(类名);
- 每个类提供了哪些操作、类之间如何调用(方法名、调用关系);
- 敏感字符串(如 SQL、接口地址)直接可见。
对攻击者来说,"知道结构"就已经泄露了一半商业机密。加密只藏住"怎么做",藏不住"做了什么"。
名称混淆做什么?
把指定类的私有方法 / 字段 / 类名打乱为 a、b、c 这类无意义符号,并输出
mapping.txt 映射文件(原始名 → 混淆名)供排查定位。反编译结果失去可读性,逻辑结构分析成本大幅上升。
推荐组合
| 场景 | 推荐方案 | 说明 |
|---|---|---|
| 框架类(Spring / JPA 等,必须 M2) | M2 + 名称混淆 | 默认最保险组合,兼顾兼容与结构隐藏 |
| 纯内部算法类(可 M1) | M1 + 名称混淆 | 内容、名字全部隐藏,防护最强 |
| 快速验证 / 试点 | 仅 M2(默认) | 先跑通流程,再叠加混淆 |
建议:凡是敏感的类(核心算法、密钥管理、License 校验、业务规则等),都启用名称混淆。 加密管"内容",混淆管"名字"——只加密不混淆,反编译后仍能看出类的结构与调用关系; 两者结合才是完整保护。
最小配置:
bceProtect {
password = '...'
obfuscationEnabled = true
obfuscationClasses = ['com.example.tools.LicenseValidator']
obfuscationGenerateMapping = true
}
混淆只作用于你指定的类,不影响框架扫描与序列化,可放心与 M2 组合使用。完整配置与注意事项见 9.2。
4.6 加密范围:白名单模型
源盾采用白名单(opt-in)模型:
- 未列入
fullEncryptClasses的类不会被 M1 全加密; - 默认全部走 M2 方法体加密;
- 建议从 1~2 个工具类开始试点,验证运行后再扩展。
4.7 自动分析:bceAnalyze
./gradlew bceAnalyze --no-daemon
输出:
build/reports/bce-full-encrypt-candidates.yaml(M1 候选)build/reports/bce-obfuscation-suggestions.yaml(混淆候选)
自动排除规则:
- 接口 / 抽象类 / 枚举 / 注解;
- 含 Spring / JPA 等框架注解的类;
- 含
main方法的类; - SPI 类、Spring AutoConfig 类;
- 匹配
excludePackages/excludeClasses的类。
第五章 许可证与授权
5.1 许可证概述
源盾是商业授权产品。每一次执行加密构建(Gradle bceProtect、Maven protect、CLI bce-protect)都会校验许可证密钥,未配置或校验失败时构建会直接中止。
许可证密钥只作用于加密构建,不影响运行时: - 校验发生在构建期,与加密工具(插件 / CLI)绑定; - 加密完成后的产物是自包含的——运行端只需 JDK + Agent 库 + 包裹密码,不需要许可证密钥; - 客户机器运行时不联网、不校验许可证密钥,无任何授权回传; - 因此受保护 JAR 可自由分发、部署到任意环境(含 Docker / 内网 / 客户现场)。
- 许可证密钥(license key)格式:
XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX-XX, 从这里购买许可证密钥。 - 支持两种授权方式:
- 在线校验(默认):构建时连接许可证服务器完成验证,首次使用自动激活当前机器。
- 离线许可证文件:使用
.lic许可证文件在本地完成验证,适合无法访问外网的 CI / 内网环境。 - 许可证服务器地址、账号等参数已内置在加密工具中,用户只需提供许可证密钥。
保密提示:许可证密钥等同授权凭证,请勿提交到公开仓库或随产品分发给最终客户。
5.2 配置许可证密钥
Gradle(build.gradle):
bceProtect {
licenseKey = 'YOUR-LICENSE-KEY'
password = '...'
}
Maven(pom.xml):
<configuration>
<licenseKey>YOUR-LICENSE-KEY</licenseKey>
<password>...</password>
</configuration>
CLI:
bce-protect.exe input.jar output.jar <password> --license-key=YOUR-LICENSE-KEY
环境变量(三种用法通用,适合 CI 保密注入):
# Windows PowerShell
$env:BCE_LICENSE_KEY="YOUR-LICENSE-KEY"
# Linux/macOS
export BCE_LICENSE_KEY="YOUR-LICENSE-KEY"
构建脚本中的
licenseKey优先级高于环境变量BCE_LICENSE_KEY。建议本地开发写在脚本里,CI 用环境变量注入。
5.3 在线校验与机器激活
默认模式。加密构建时流程如下:
- 加密工具携带许可证密钥 + 本机硬件指纹,向许可证服务器发起校验。
- 首次使用该密钥的机器会自动完成激活(按硬件指纹绑定),无需手工操作。
- 校验通过后,自动在当前工作目录生成离线许可证文件
bce.lic,供后续离线校验使用。 - 开始加密。
多机器授权:一张许可证密钥可绑定多台机器(取决于授权策略的机器数上限 maxMachines)。
每台新机器首次使用时同样自动激活,直到占满名额。机器数达到上限后,新机器会收到类似下面的提示:
License machine limit reached: 3/3 machine(s) already bound to this license.
Please deactivate a previously bound machine first, or contact your license administrator to increase the machine limit.
其中 M/N 表示"已绑定 M 台 / 上限 N 台"。此时请先在不再使用的旧机器上解绑(联系塔尔旺科技管理员),或追加授权。
注意事项:
- 构建机器需要能访问许可证服务器(HTTPS,默认
api.license.telecwin.com)。如公司有出口防火墙,请放行该域名。 - 每张许可证密钥可激活的机器数量由购买的授权策略决定(单机或多机)。机器数用满后,新机器会报
machine limit reached,需联系塔尔旺科技释放不用的机器或追加授权。 - 更换构建机器(如 CI 迁移)前,建议先联系管理员确认可激活机器数。
5.4 离线许可证文件
适用于无法访问许可证服务器的内网 / 隔离环境。
获取方式:
- 在能联网的机器上成功执行一次加密构建,工作目录会自动生成
bce.lic(推荐); - 或由塔尔旺科技根据你的硬件指纹签发后交付。
使用方式:
Gradle:
bceProtect {
licenseKey = 'YOUR-LICENSE-KEY'
licenseFile = file('bce.lic')
password = '...'
}
Maven:
<configuration>
<licenseKey>YOUR-LICENSE-KEY</licenseKey>
<licenseFile>${project.basedir}/bce.lic</licenseFile>
<password>...</password>
</configuration>
CLI:
# 配置了 license-file 时仍先尝试在线,失败自动回退离线;--offline 强制离线
bce-protect.exe input.jar output.jar <password> \
--license-key=YOUR-LICENSE-KEY \
--license-file=bce.lic --offline
环境变量:BCE_LICENSE_FILE 指向 .lic 文件路径。
注意事项:
- 离线文件与许可证密钥、机器指纹绑定,三者必须匹配:把 A 机器的
bce.lic拿到 B 机器使用会校验失败。 bce.lic含授权信息,请与密钥同等保密;.gitignore中建议加入*.lic。- 许可证密钥到期后离线文件同样失效,需重新获取。
5.5 查看本机硬件指纹
许可证密钥按机器硬件指纹绑定。排查激活问题或申请离线签发时,管理员可能需要你提供指纹:
./gradlew bcePrintFingerprint --no-daemon
该任务会打印本机的硬件指纹分项信息(主板、CPU、磁盘等),整段复制给许可证管理员即可。
5.6 许可证常见问题
| 现象 / 报错 | 原因 | 处理 |
|---|---|---|
BCE license key must be configured |
未配置许可证密钥 | 配置 licenseKey 或环境变量 BCE_LICENSE_KEY |
License key is required(CLI) |
未传 --license-key |
传参或设置 BCE_LICENSE_KEY |
Online license validation failed ... Connection refused / timeout |
无法连接许可证服务器 | 检查网络与防火墙放行 api.license.telecwin.com;或改用离线 bce.lic |
license is already activated on another machine |
密钥已绑定其他机器,可激活数已满 | 联系塔尔旺科技释放旧机器或追加授权 |
NOT_FOUND / License not found |
密钥不存在或拼写错误 | 核对密钥(注意 0/O、1/I),仍失败联系我们 |
license has expired |
许可证密钥已到期 | 联系我们续期,续期后离线文件需重新获取 |
No offline license file available for fallback |
在线失败且未配置离线文件 | 联网成功一次生成 bce.lic,或配置 licenseFile |
| 离线校验失败 | bce.lic 与密钥/机器不匹配或文件损坏 |
在绑定机器上重新生成,核对密钥一致 |
第六章 Gradle 插件详解
6.1 插件应用方式
两种方式任选其一。区别不在 Gradle 新旧版本(源盾要求 Gradle 7+,两种语法均支持),而在插件解析路径的配置位置。v1.8.1 起插件发布在 Maven Central(未发布到 Gradle Plugin Portal)。
| 方式 | 版本要求 | 适用场景 |
|---|---|---|
plugins {} 块 |
语法 Gradle 2.1+(2015-09 引入,5.0 起脱离孵化);声明自定义插件仓库需 pluginManagement(Gradle 3.5+) |
推荐。插件从 Maven Central 解析,只需在 settings.gradle 声明 mavenCentral() 为插件仓库 |
buildscript {} + apply plugin |
任意 Gradle 版本(1.x 起) | 不便改 settings.gradle 时;仓库直接写在 build.gradle。官方示例 bce-test 即采用此方式 |
方式一:plugins {}(需在 settings.gradle 配置 pluginManagement)
settings.gradle(pluginManagement 必须是该文件第一条语句):
pluginManagement {
repositories {
mavenCentral() // 源盾插件从这里解析(v1.8.1+)
gradlePluginPortal() // 其他插件的兜底的,保留
}
}
build.gradle:
plugins {
id 'com.telecwin.codeshield.jvm' version '1.8.2'
}
不配置
pluginManagement直接用plugins {}会报:Plugin [id: 'com.telecwin.codeshield.jvm', version: '1.8.1'] was not found in any of the following sources。 原因:Gradle 默认只查 Gradle Plugin Portal,源盾插件的 marker 发布在 Maven Central。
方式二:buildscript {} + apply plugin(任意 Gradle 版本)
buildscript {
repositories {
mavenCentral() // v1.8.1 起插件在 Maven Central
}
dependencies {
classpath 'com.telecwin.codeshield:codeshield-gradle-plugin:1.8.2'
}
}
apply plugin: 'com.telecwin.codeshield.jvm'
注意:请勿使用 Maven Central 上的 1.8.0(其插件 POM 缺运行时依赖,运行会报
NoClassDefFoundError),1.8.1 起已修复。
6.2 bceProtect {} DSL 配置项
bceProtect {
// ===== 必填 =====
licenseKey = 'YOUR-LICENSE-KEY' // 许可证密钥(或设 BCE_LICENSE_KEY)
password = '...'
// ===== 许可证密钥(可选)=====
licenseFile = file('bce.lic') // 离线许可证文件,见第五章
offline = false // true 时强制离线校验
// ===== 输入/输出 =====
inputJar = file('build/libs/myapp.jar')
outputJar = file('build/libs/myapp-protected.jar')
// ===== 字符串加密 =====
encryptStrings = true
excludeStrings = ['java.', 'org.springframework']
// ===== M1 全加密白名单(YAML)=====
configFile = 'bce-full-encrypt.yaml'
// ===== 额外 JAR 同步加密 =====
extraJars = ['common-utils', 'com.example:common-utils', 'libs/custom.jar']
// ===== Native Agent 输出 =====
outputAgentDll = true // 默认 true
agentDllName = 'bce_agent.dll' // 默认 'bce_agent.dll'
// ===== 名称混淆 =====
obfuscationEnabled = false
obfuscationClasses = ['com.example.SecretUtil']
obfuscationAdaptClassStrings = true
obfuscationGenerateMapping = true
}
6.3 任务清单
| 任务 | 作用 | 输出 |
|---|---|---|
bceGeneratePassword |
生成包裹密码 | stdout |
bceProtect |
校验许可证密钥,加密主 JAR 与 extraJars |
protected.jar、bce_agent.dll、报告 |
bceAnalyze |
扫描并推荐 M1 候选类与混淆候选 | YAML 报告 |
bcePrintFingerprint |
打印本机硬件指纹(用于许可证激活/排障) | stdout |
6.4 常见 Gradle 错误与排查
| 错误 | 原因 | 修复 |
|---|---|---|
Plugin com.telecwin.codeshield.jvm was not found |
未发布到本地/远程仓库 | 先执行 ./gradlew :bce-gradle-plugin:publishToMavenLocal --no-daemon |
BCE license key must be configured |
未配置许可证密钥 | 配置 licenseKey 或 BCE_LICENSE_KEY,见第五章 |
Online license validation failed |
无法连接许可证服务器 / 密钥无效 | 见 5.6 许可证常见问题 |
Plaintext password rejected |
密码未使用包裹形态 | 运行 bceGeneratePassword 后使用其输出 |
bce-protect.exe not found |
插件 jar 内未嵌入 exe | 先构建 :bce-native-protect:nativeImage 并重新发布插件 |
| Gradle daemon 挂起 | 未加 --no-daemon |
所有 Gradle 命令追加 --no-daemon |
第七章 Maven 插件详解
7.1 插件坐标与生命周期绑定
<plugin>
<groupId>com.telecwin.codeshield</groupId>
<artifactId>codeshield-maven-plugin</artifactId>
<version>1.8.2</version>
<configuration>
<licenseKey>YOUR-LICENSE-KEY</licenseKey>
<password>...</password>
</configuration>
<executions>
<execution>
<goals>
<goal>protect</goal>
</goals>
</execution>
</executions>
</plugin>
protect 默认绑定到 package 阶段。
7.2 常用配置参数
| XML 字段 | 说明 | 默认值 |
|---|---|---|
licenseKey |
许可证密钥(或设 BCE_LICENSE_KEY) |
必填 |
licenseFile |
离线许可证文件路径 | 空(在线校验) |
offline |
是否强制离线校验 | false |
password |
包裹密码 | 必填 |
inputJar |
输入 JAR | ${project.build.directory}/${project.build.finalName}.jar |
outputJar |
输出 JAR | ...-protected.jar |
encryptStrings |
是否加密字符串 | true |
excludeStrings |
字符串加密排除前缀 | 空 |
configFile |
M1 白名单 YAML | 空 |
extraJars |
额外加密 JAR | 空 |
outputAgentDll |
是否提取 agent DLL | true |
agentDllName |
agent DLL 文件名 | bce_agent.dll |
Maven 配置标签名 = Java 字段名(如
password),不是@Parameter(property = "bce.password")中的属性名。
7.3 常见 Maven 错误与排查
| 错误 | 原因 | 修复 |
|---|---|---|
Parameter 'bce.password' is unknown |
使用了 <bce.password> 标签 |
改为 <password> |
BCE license key must be configured |
未配置许可证密钥 | 配置 <licenseKey> 或 BCE_LICENSE_KEY,见第五章 |
Plaintext password rejected |
密码未使用包裹形态 | 运行 generate-password goal |
Failed to parse plugin descriptor |
插件 jar 缺少 plugin.xml |
重新发布 bce-maven-plugin |
第八章 CLI 独立使用
8.1 bce-protect 参数说明
适合未使用 Gradle/Maven 的项目或 CI 独立步骤。bce-protect 为 GraalVM Native Image 单文件可执行程序,Windows 下为 bce-protect.exe,Linux 下为 bce-protect,参数完全一致。
# 生成密码
bce-protect.exe --generate-password
# 打印本机硬件指纹(许可证激活/排障用)
bce-protect.exe --print-fingerprint
# 加密 JAR(在线校验许可证密钥)
bce-protect.exe \
input.jar \
output-protected.jar \
RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ= \
true "java.,org.springframework" \
--full-encrypt=com.example.CryptoHelper,com.example.AESUtil \
--license-key=YOUR-LICENSE-KEY
# 加密 JAR(强制离线校验)
bce-protect.exe input.jar output-protected.jar <password> \
--license-key=YOUR-LICENSE-KEY \
--license-file=bce.lic --offline
# 运行时
java -agentpath:./bce_agent.dll=RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ= \
-jar output-protected.jar
许可证密钥相关参数:
| 参数 | 说明 |
|---|---|
--license-key=<key> |
许可证密钥(必填,或设 BCE_LICENSE_KEY) |
--license-file=<path> |
离线许可证文件(或设 BCE_LICENSE_FILE) |
--offline |
强制离线校验,不连接许可证服务器 |
--print-fingerprint |
打印本机硬件指纹后退出 |
8.2 加密流程示例
- 准备待加密的
input.jar。 - 执行
--generate-password获得包裹密码。 - 执行加密命令,指定许可证密钥、是否启用字符串加密、排除前缀、M1 类列表。
- 将输出
output-protected.jar与bce_agent.dll一起部署。
8.3 提取与使用 Native Agent
CLI 加密后,可在输出目录找到 bce_agent.dll(Windows)、libbce_agent.so(Linux)或 libbce_agent.dylib(macOS)。
运行时通过 -agentpath: 指定:
# Windows
java -agentpath:/opt/codeshield/bce_agent.dll=YOUR_PASSWORD -jar app-protected.jar
# Linux
java -agentpath:/opt/codeshield/libbce_agent.so=YOUR_PASSWORD -jar app-protected.jar
# macOS(Apple Silicon)
java -agentpath:/opt/codeshield/libbce_agent.dylib=YOUR_PASSWORD -jar app-protected.jar
macOS 特别注意:
libbce_agent.dylib必须带可执行权限(-x)。 JVM 需要可执行权限才能加载 agent 库,文件不带可执行位会加载失败(报agent library failed Agent_OnLoad)。 - 插件 / CLI 提取 agent 时会自动设置执行位,正常使用无需手动处理; - 但通过 scp / 下载 / 打包解压 / 邮件附件 等方式把 dylib 拷贝到部署机器时,执行位通常会丢失, 需要在目标机器上执行:
bash chmod +x libbce_agent.dylib
- 判断方法:
ls -l libbce_agent.dylib,权限位应包含x(如-rwxr-xr-x)。
启动脚本注意事项(macOS zsh)
若用启动脚本拼接多个 JVM 参数,请注意 zsh 默认不对未加引号的变量做词分割:
字符串变量会整串作为一个参数传给 java,导致 agent 拿到的密码尾随后续参数而校验失败
(报错 Plaintext password rejected / agent library failed Agent_OnLoad)。
请用数组传参:
#!/bin/zsh
APP_HOME="$(cd "$(dirname "$0")" && pwd)"
cd "$APP_HOME"
VM_PARAMS=(
"-agentpath:./libbce_agent.dylib=YOUR_PASSWORD"
"-Djava.awt.headless=false"
)
exec ./jre/bin/java "${VM_PARAMS[@]}" -cp "lib/*" com.example.Main
要点:
- 每个参数一个数组元素,密码整体放在引号内;
- 用 "${VM_PARAMS[@]}" 展开数组;
- bash 脚本不受此影响(bash 默认做词分割),但同样推荐数组写法。
全平台 agent 一次性解压
插件 JAR 内同时打包了全部平台的 agent 库,可一次性解压,方便跨平台分发(受保护 JAR 是平台无关的,按目标机器挑对应平台的库即可):
# Gradle
./gradlew bceExtractAgentAll # 默认输出到 build/libs/
# Maven
mvn com.telecwin.codeshield:codeshield-maven-plugin:extract-agent # 默认输出到 target/
# 只解本平台(平铺,不建子目录):
mvn com.telecwin.codeshield:codeshield-maven-plugin:extract-agent -Dbce.agent.host.only=true
解压后的目录布局(以 macOS 为本平台为例):
build/libs/ 或 target/
├── libbce_agent.dylib ← 本平台,直接可用(-agentpath 指它)
├── windows-x86_64/bce_agent.dll
├── linux-x86_64/libbce_agent.so
└── macos-aarch64/libbce_agent.dylib
根目录平铺的是当前平台的库(保持 -agentpath:./<库名> 用法不变),子目录按
<os>-<arch>/ 命名包含全部平台的库。非 Windows 库解压后自动带可执行位。
第九章 高级功能
9.1 字符串加密与 excludeStrings
bceProtect {
encryptStrings = true
excludeStrings = [
'java.',
'javax.',
'org.springframework.',
'org.hibernate.',
'com.fasterxml.jackson.',
'jakarta.',
'org.slf4j.',
'ch.qos.logback.',
'org.apache.tomcat.'
]
}
效果:反编译工具中字符串显示为等长下划线 ________,运行时由 Agent 还原。
9.2 名称混淆详解与映射文件
bceProtect {
obfuscationEnabled = true
obfuscationClasses = ['com.example.SecretUtil', 'com.example.CryptoHelper']
obfuscationAdaptClassStrings = true
obfuscationGenerateMapping = true
}
- 仅混淆指定类的私有方法/字段(可选类名);
mapping.txt输出到build/bce/obfuscation-mapping.txt;- 与 M1 全加密混用时:
bce-full-encrypt.yaml中的fullEncryptClasses必须使用原始类名,因为混淆发生在 M1 匹配之前,插件会用混淆后的名称去匹配。
9.3 依赖 JAR 同步加密:extraJars
源盾不仅加密项目自身产生的 JAR,也支持对依赖库进行加密——这样依赖库里的核心逻辑同样受到保护,项目发布、打包更省心。
三种指定方式:
| 写法 | 匹配规则 | 示例 |
|---|---|---|
'common-utils' |
按依赖名(artifactId)匹配 | 只写依赖名,无需 group 与版本号 |
'com.example:common-utils' |
按 group:artifactId 匹配 | 精确到组,仍无需版本号 |
'libs/my-custom.jar' |
按文件路径 | 本地未发布到仓库的 jar |
bceProtect {
extraJars = [
'common-utils', // 按依赖名(不写版本号)
'com.example:common-utils', // 按 group:依赖名(不写版本号)
'libs/my-custom.jar' // 按相对路径
]
}
为什么可以(也建议)不写版本号?
- 匹配的是"当前解析到的依赖":升级依赖版本号后,
extraJars配置无需任何修改,重新构建即自动对新版本生效; - 避免"升级了依赖却忘了同步加密配置"的隐患——依赖库依然被加密保护,不会因为版本升级而漏保护。
输出:加密后的额外 JAR 输出到 build/bce-extra/,加密策略(M1 / M2 / 名称混淆)与主 JAR 完全一致。
9.4 运行时启动与 Native Agent 参数
加密后的 JAR 不能直接 java -jar 启动,必须由 Native Agent 在类加载时透明解密。
业务代码、框架配置、部署脚本均无需修改,运行端仅需 JDK 8+。
三平台启动命令:
# Windows
java -agentpath:./bce_agent.dll=YOUR_PASSWORD -jar app-protected.jar
# Linux
java -agentpath:./libbce_agent.so=YOUR_PASSWORD -jar app-protected.jar
# macOS(Apple Silicon)
java -agentpath:./libbce_agent.dylib=YOUR_PASSWORD -jar app-protected.jar
参数说明:
-agentpath:后接 agent 库的绝对或相对路径。=后接包裹密码,必须与加密时使用的是同一个包裹密码。- 多个 JVM 参数顺序无特殊要求。
- agent 库的获取途径见 11.5 从哪里获取 agent 动态链接库? 与 8.3 提取与使用 Native Agent。
常见部署形态:
| 部署形态 | 挂载方式 |
|---|---|
命令行 java -jar |
启动命令直接追加 -agentpath:<agent>=<包裹密码> |
| Spring Boot 可执行 JAR | 同上;嵌套 JAR 结构兼容(v1.1.1+),无需修改启动类与打包方式 |
| Tomcat / 应用服务器 | 将 -agentpath:... 追加到 CATALINA_OPTS(Tomcat)或服务器的 JVM 参数中,应用包本身无需调整 |
| Docker / Kubernetes | 镜像内置 agent 库后通过 ENV JAVA_TOOL_OPTIONS="-agentpath:/opt/codeshield/libbce_agent.so=<包裹密码>" 注入,启动命令保持不变 |
提示:未挂载 Agent 直接启动会报错
BCEProtected class file cannot be loaded——这是保护生效的正常表现,挂载 Native Agent 后即可正常运行。macOS 注意:
libbce_agent.dylib必须带可执行权限(-x),否则 JVM 无法加载该库 (报agent library failed Agent_OnLoad)。插件自动提取时已自动设置;若手动拷贝 / 解压分发,请先chmod +x libbce_agent.dylib。详见 8.3。
第十章 框架兼容性
10.1 Spring / Spring Boot
M2 保留完整类结构,因此:
| 场景 | 兼容 |
|---|---|
@ComponentScan |
✅ |
@Autowired |
✅ |
@Value 注入 |
✅ |
| AOP / CGLIB 代理 | ✅ |
| Spring Boot 嵌套 JAR | ✅(v1.1.1+) |
10.2 JPA / Hibernate
M2 保留字段与 @Entity 注解,实体扫描正常。
M1 会破坏实体类,抽象
@Entity父类已由bceAnalyze自动排除。
10.3 CGLIB / AOP
M2 保留方法签名,CGLIB 可正常生成代理子类。
10.4 Jackson / 序列化
运行时已还原为完整字节码,序列化/反序列化正常。
10.5 OSGi / 热部署
Native Agent 在类加载时透明解密,兼容动态类加载与热部署。
第十一章 常见问题与故障排查
11.1 构建阶段错误
| 错误 | 原因 | 修复 |
|---|---|---|
Plaintext password rejected |
密码未使用包裹形态 | 运行 bceGeneratePassword / generate-password |
BCE license key must be configured |
未配置许可证密钥 | 配置 licenseKey / BCE_LICENSE_KEY,见第五章 |
Online license validation failed |
连不上许可证服务器 / 密钥无效 / 机器数已满 | 见 5.6 许可证常见问题 |
Plugin ... was not found |
本地仓库未发布或缓存旧坐标 | 重新 publishToMavenLocal 或升级版本号 |
bce-protect.exe not found |
插件未嵌入 exe | 先构建 native-protect 并重新发布插件 |
Parameter 'bce.password' is unknown |
Maven 配置标签错误 | 使用 <password> 而非 <bce.password> |
11.2 运行时错误
| 错误 | 原因 | 修复 |
|---|---|---|
BCEProtected class file cannot be loaded |
未挂 Agent | 添加 -agentpath:./bce_agent.dll=... |
UnsatisfiedLinkError |
DLL 与 JVM 位数不匹配 | 确认使用 64 位 JVM |
NoClassDefFoundError: kotlin/... |
Kotlin stdlib 未加入 classpath | 运行时包含 kotlin-stdlib-*.jar |
failed to decode wrapped password |
包裹密码字符串损坏 | 重新生成并复制 |
11.3 Agent DLL 相关错误
| 错误 | 原因 | 修复 |
|---|---|---|
Native agent DLL not found |
未生成或未复制 DLL | Gradle 默认输出到 build/libs/;Maven 需配置 extract-agent goal |
| DLL 加载失败 | 系统缺少 VC++ 运行库 | 安装对应 Visual C++ Redistributable |
macOS 报 agent library failed Agent_OnLoad |
libbce_agent.dylib 缺少可执行权限 |
在部署机器上 chmod +x libbce_agent.dylib(详见 8.3) |
11.4 性能与兼容性问题
| 现象 | 原因 | 修复 |
|---|---|---|
| 加密后会影响运行性能吗? | 担心解密开销 | 不会。解密只在类装载时发生一次并缓存,运行期零开销;实测启动 ≈1.07× 基线,见 2.4 |
| Spring Boot 启动失败 | M1 错误地加密了框架类 | 将 Spring 组件从 fullEncryptClasses 移除 |
| 字符串加密后日志乱码 | 日志框架字符串被加密 | 将日志框架前缀加入 excludeStrings |
11.5 从哪里获取 agent 动态链接库?
agent 动态链接库(bce_agent.dll / libbce_agent.so / libbce_agent.dylib)来源有三种:
-
Gradle 插件自动提取(默认) 执行
./gradlew bceProtect --no-daemon后,bce_agent.dll会自动输出到build/libs/目录,与*-protected.jar同目录。 -
Maven 插件
extract-agentgoal 在插件配置中加入<goal>extract-agent</goal>,执行mvn package后,bce_agent.dll会输出到target/目录。 -
从插件 jar 中手动解压 如果 CI 环境需要单独获取 agent,可以从已发布的
codeshield-gradle-plugin-1.8.2.jar或codeshield-maven-plugin-1.8.2.jar中解压:META-INF/native/windows-x86_64/bce_agent.dll(Windows)、META-INF/native/linux-x86_64/libbce_agent.so(Linux)或META-INF/native/macos-aarch64/libbce_agent.dylib(macOS)。 -
商务交付包 企业客户也可以从塔尔旺科技提供的正式交付包中获取对应平台的 agent 二进制文件。
macOS 分发提醒:手动拷贝 / 解压的
libbce_agent.dylib会丢失可执行位(-x),部署前先chmod +x libbce_agent.dylib,否则 JVM 无法加载(报agent library failed Agent_OnLoad)。
附录
附录 A:插件 ID 与 Maven 坐标速查表
| 项目 | 值 |
|---|---|
| Gradle plugin id | com.telecwin.codeshield.jvm |
| Gradle 插件坐标 | com.telecwin.codeshield:codeshield-gradle-plugin:1.8.2 |
| Maven 插件坐标 | com.telecwin.codeshield:codeshield-maven-plugin:1.8.2 |
| DSL 块名 | bceProtect { ... } |
| Maven 配置标签 | <licenseKey>、<password>、<encryptStrings> 等 |
| 许可证密钥环境变量 | BCE_LICENSE_KEY、BCE_LICENSE_FILE |
附录 B:DSL / XML 配置示例合集
完整 Gradle 配置:
plugins {
id 'com.telecwin.codeshield.jvm' version '1.8.2'
}
bceProtect {
licenseKey = 'YOUR-LICENSE-KEY'
password = 'RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ='
encryptStrings = true
excludeStrings = ['java.', 'org.springframework']
configFile = 'bce-full-encrypt.yaml'
extraJars = ['common-utils']
outputAgentDll = true
agentDllName = 'bce_agent.dll'
obfuscationEnabled = true
obfuscationClasses = ['com.example.SecretUtil']
obfuscationAdaptClassStrings = true
obfuscationGenerateMapping = true
}
完整 Maven 配置:
<plugin>
<groupId>com.telecwin.codeshield</groupId>
<artifactId>codeshield-maven-plugin</artifactId>
<version>1.8.2</version>
<configuration>
<licenseKey>YOUR-LICENSE-KEY</licenseKey>
<password>RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ=</password>
<encryptStrings>true</encryptStrings>
<excludeStrings>java.,org.springframework</excludeStrings>
<configFile>bce-full-encrypt.yaml</configFile>
<outputAgentDll>true</outputAgentDll>
<agentDllName>bce_agent.dll</agentDllName>
</configuration>
<executions>
<execution>
<goals>
<goal>protect</goal>
<goal>extract-agent</goal>
</goals>
</execution>
</executions>
</plugin>
附录 C:术语表
| 术语 | 说明 |
|---|---|
| M1 全加密 | 对整个 class 文件加密为 Stub,只留类名 |
| M2 方法体加密 | 保留类结构,仅加密方法体与字符串(默认模式) |
| 包裹密码 | 源盾自动生成的高熵密码经包裹编码后的字符串(bceGeneratePassword 生成)。用于派生加解密密钥,构建脚本中不出现明文 |
| 名称混淆 | 将私有方法 / 字段 / 类名打乱为无意义符号,输出映射文件 |
| Native Agent | 运行时负责解密的动态链接库(bce_agent.dll / libbce_agent.so / libbce_agent.dylib) |
bceProtect |
Gradle/Maven 插件的加密任务/goal |
bceAnalyze |
扫描候选类的分析任务 |
bceGeneratePassword |
生成包裹密码的任务/goal |
bcePrintFingerprint |
打印本机硬件指纹的任务(许可证激活/排障用) |
| 许可证密钥(license key) | 源盾商业授权凭证,加密构建时校验 |
离线许可证文件(.lic) |
与密钥和机器指纹绑定的本地授权文件,用于无外网环境 |
| 硬件指纹 | 主板、CPU、磁盘等硬件特征,许可证密钥按机器绑定 |