源盾 CodeShield · 用户使用手册 ← 返回首页

源盾(CodeShield)用户使用手册

版本:v1.8.2

适用对象:使用源盾保护 Java/Kotlin 字节码的研发、运维与安全工作工程师。


目录


源盾是什么?(30 秒入门)

一句话:给你的 Java / Kotlin 程序"加锁"

用反编译工具(JD-GUI、jadx 等)打开一个普通 JAR,源码逻辑一目了然——别人可以轻松抄走你的核心算法、业务规则。源盾在构建时把 class 文件加密,加密后的 JAR 反编译出来全是乱码 / 占位符 / 无意义符号;程序运行时,由 Native Agent 在类加载时透明解密,业务代码零改动。

图 1 源盾工作原理:构建期加密,运行期透明解密
构建期 · 开发者机器
业务源码
(.java / .kt)
编译器
javac / kotlinc
普通 JAR
源盾加密工具
bce-protect
受保护 JAR
(加密后的字节码)
+ Native Agent 动态库
反编译工具打开受保护 JAR 只能看到乱码 / 占位符 / 无意义符号
部署分发
受保护 JAR + Agent 库 + 同一个包裹密码
运行期 · 客户机器
启动命令
java -agentpath:agent=密码 -jar
JVM 类加载
Native Agent 透明解密
业务代码零改动
业务正常执行
(字节码已还原)
运行时不校验许可证、不联网:加密后的 JAR 可独立分发到任意客户机器运行
未挂 Agent 直接启动会报错:BCEProtected class file cannot be loaded —— 这是保护生效的正常表现
Text is not SVG - cannot display

整个过程其实只有三步:

  1. 拿到许可证密钥(商业授权,每次加密构建必须校验);
  2. 生成包裹密码(源盾自动生成的一串随机字符,不是自己设的);
  3. 配置插件 → 加密构建 → 挂 Agent 运行
图 2 首次使用源盾的完整流程
① 获取许可证密钥
联系塔尔旺科技商务购买
格式 XXXXXX-XXXXXX-...-XX
一次购买,绑定机器数内多台可用
② 引入源盾插件
Gradle:plugins {} 或 buildscript
Maven:pom.xml 插件坐标
Maven Central 获取,无需额外仓库
插件内嵌三平台
二进制,自动适配
③ 生成包裹密码
Gradle:./gradlew bceGeneratePassword
Maven:mvn ...:generate-password
自动生成的一串随机字符,不是自己设的
作用是让脚本里
不出现明文密码
④ 配置构建脚本
bceProtect { licenseKey = '...' ; password = '包裹密码' }
或 pom.xml 中 /
CI 也可用环境变量
BCE_LICENSE_KEY 注入
⑤ 执行加密构建
Gradle:./gradlew bceProtect
Maven:mvn package
首次构建自动激活当前机器(在线校验)
产出:-protected.jar
+ agent 动态库
+ bce.lic(离线用)
⑥ 部署三件套
受保护 JAR + agent 库 + 同一个包裹密码
受保护 JAR 平台无关,按目标机器选对应平台 agent
⑦ 运行
java -agentpath:./bce_agent.dll=包裹密码 -jar app-protected.jar
业务代码、框架配置、部署脚本均无需修改
Docker/容器用
JAVA_TOOL_OPTIONS 注入
许可证只在加密构建时校验 —— 加密后的程序运行时不需要许可证、不联网、不校验,可独立分发运行
Text is not SVG - cannot display

阅读指引


第一章 快速上手

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.gradlepluginManagement 必须是该文件第一条语句):

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 日志、代码仓库里——任何看到脚本的人,都等于拿到了你的加解密密钥(明文密码进脚本,等于把保险箱钥匙贴在保险箱上)。

源盾的包裹机制是这样做的:

  1. 源盾用 SecureRandom 自动生成一个 16 字节的高熵随机密码(2^128 种可能,无法猜测、无法暴力破解);
  2. 用工具内置的密钥把它包裹(加密)成一串 60 字符的随机字符;
  3. 你只需要把包裹后的字符串写进构建脚本——插件、CLI、Agent 内置同一把密钥,运行时自动解开包裹还原密码;
  4. 即使脚本 / 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 第四步:加密构建

把许可证密钥和包裹密码填入配置,执行加密。

Gradlebuild.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

Mavenpom.xml,在 1.3 的插件声明中补上 configuration):

<configuration>
    <licenseKey>YOUR-LICENSE-KEY</licenseKey>
    <password>RJreKJXd58bYAcizcFsClEFCRmCve0AM27YWaoqTpDQ=</password>
</configuration>
mvn package

产物:target/<artifact>-<version>-protected.jartarget/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.gradlepluginManagementmavenCentral(),见 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 工作原理:构建期加密,运行期解密

图 1 源盾工作原理(同开篇,此处附注)
构建期 · 开发者机器
业务源码
(.java / .kt)
编译器
javac / kotlinc
普通 JAR
源盾加密工具
bce-protect
受保护 JAR
(加密后的字节码)
+ Native Agent 动态库
反编译工具打开受保护 JAR 只能看到乱码 / 占位符 / 无意义符号
部署分发
受保护 JAR + Agent 库 + 同一个包裹密码
运行期 · 客户机器
启动命令
java -agentpath:agent=密码 -jar
JVM 类加载
Native Agent 透明解密
业务代码零改动
业务正常执行
(字节码已还原)
运行时不校验许可证、不联网:加密后的 JAR 可独立分发到任意客户机器运行
未挂 Agent 直接启动会报错:BCEProtected class file cannot be loaded —— 这是保护生效的正常表现
Text is not SVG - cannot display

三个要点:

  1. 构建期:源盾在打包时对 class 文件加密,产出"受保护 JAR";反编译只能看到乱码 / 占位符 / 无意义符号;
  2. 运行期:启动命令挂载 Native Agent(-agentpath:<agent>=<包裹密码>),JVM 在类加载时自动解密,业务代码零改动
  3. 必须配对:加密时用的包裹密码,运行时必须用同一个,否则解密失败。

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 启动阶段的 固有步骤,解密只是其中极小的一个环节。

图 4 启动耗时对比:加密前后几乎无差异
2026-08-28T10:31:04.413960 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/
图 5 单类解密开销:约 2 微秒,仅类装载时发生一次
2026-08-28T10:31:04.470582 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/

实测数据(内部性能测试):

指标 实测值
单个类解密开销(微基准) ~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.jarLicenseValidator 的方法体是乱码 / 占位符,看不到校验逻辑。

默认走 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:混合模式 + 名称混淆(进阶)

场景:核心算法类(CryptoHelperLicenseValidator)用 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 的私有方法 / 字段名被打乱为 abc(混淆);
  • build/bce/obfuscation-mapping.txt:记录原始名 → 混淆名的映射,排查问题用。

关于混淆的更多细节(能混淆什么、注意事项、与 M1 混用的坑),见 4.59.2


第四章 加密模式与混淆策略

4.1 决策图总览

图 3 加密模式与混淆决策:先选加密模式,再决定是否叠加名称混淆
加密模式与混淆决策
我的类需要保护吗?
否 → 不加密,跳过
类会被框架反射 / 扫描吗?
Spring 组件 / JPA 实体 /
CGLIB 代理 / 序列化对象
M2 方法体加密(默认)
保留类结构:类名 / 注解 / 字段 / 方法签名
仅加密方法体与字符串常量
框架兼容性最好
是(会被框架使用)
M1 全加密
整个 class 加密为 Stub,只留类名
方法 / 字段 / 注解 / 字节码全部不可见
纯内部工具类 / 算法 / License 校验
否(纯内部使用)
混合模式(进阶)
核心算法类 M1 + 业务类 M2
YAML 白名单显式指定 M1 类
bce-full-encrypt.yaml
两类都有 → 分开指定
进一步保护关键类,增强破解难度
名称混淆隐藏:类名 / 方法名 / 字段名 / 字符串
★ 推荐:加密 + 名称混淆
M2(或 M1)+ 名称混淆
私有方法 / 字段 / 类名打乱为无意义符号
输出 mapping.txt 映射文件
加密管内容,混淆管名字 —— 双重保护最保险
仅加密即可
(默认 M2 已包含字符串加密)
Text is not SVG - cannot display

两种加密模式 + 一种进阶组合 + 一把"第二道锁":

方案 保留的内容 反编译后能看到 适合
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、接口地址)直接可见。

对攻击者来说,"知道结构"就已经泄露了一半商业机密。加密只藏住"怎么做",藏不住"做了什么"

名称混淆做什么?

把指定类的私有方法 / 字段 / 类名打乱为 abc 这类无意义符号,并输出 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 配置许可证密钥

Gradlebuild.gradle):

bceProtect {
    licenseKey = 'YOUR-LICENSE-KEY'
    password = '...'
}

Mavenpom.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 在线校验与机器激活

默认模式。加密构建时流程如下:

  1. 加密工具携带许可证密钥 + 本机硬件指纹,向许可证服务器发起校验。
  2. 首次使用该密钥的机器会自动完成激活(按硬件指纹绑定),无需手工操作。
  3. 校验通过后,自动在当前工作目录生成离线许可证文件 bce.lic,供后续离线校验使用。
  4. 开始加密。

多机器授权:一张许可证密钥可绑定多台机器(取决于授权策略的机器数上限 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 离线许可证文件

适用于无法访问许可证服务器的内网 / 隔离环境。

获取方式

  1. 在能联网的机器上成功执行一次加密构建,工作目录会自动生成 bce.lic(推荐);
  2. 或由塔尔旺科技根据你的硬件指纹签发后交付。

使用方式

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/O1/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.gradlepluginManagement 必须是该文件第一条语句):

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.jarbce_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 未配置许可证密钥 配置 licenseKeyBCE_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 加密流程示例

  1. 准备待加密的 input.jar
  2. 执行 --generate-password 获得包裹密码。
  3. 执行加密命令,指定许可证密钥、是否启用字符串加密、排除前缀、M1 类列表。
  4. 将输出 output-protected.jarbce_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

参数说明:

常见部署形态

部署形态 挂载方式
命令行 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)来源有三种:

  1. Gradle 插件自动提取(默认) 执行 ./gradlew bceProtect --no-daemon 后,bce_agent.dll 会自动输出到 build/libs/ 目录,与 *-protected.jar 同目录。

  2. Maven 插件 extract-agent goal 在插件配置中加入 <goal>extract-agent</goal>,执行 mvn package 后,bce_agent.dll 会输出到 target/ 目录。

  3. 从插件 jar 中手动解压 如果 CI 环境需要单独获取 agent,可以从已发布的 codeshield-gradle-plugin-1.8.2.jarcodeshield-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)。

  4. 商务交付包 企业客户也可以从塔尔旺科技提供的正式交付包中获取对应平台的 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_KEYBCE_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、磁盘等硬件特征,许可证密钥按机器绑定