20 KiB
自研授权 SDK 设计方案
日期:2026-05-18 背景:比特安索授权云费用过高,决定优先推进自研授权方案。 原则:与比特安索双线共存,Provider 可扩展架构,后续可接入更多第三方授权方式。
目录
1. 决策摘要
| 决策项 | 选择 | 理由 |
|---|---|---|
| 授权服务形态 | 混合模式 | 有网络时在线验证(心跳/租约续期),断网时本地缓存可用至离线宽限期 |
| 加密体系 | 非对称签名 RSA-256 + AES-256-GCM 加密载荷 | 离线验签 + 内容加密防窥探,每个 license 独立 AES 密钥防批量破解 |
| 授权粒度 | 完整属性:有效期 + 终端限制 + 并发用户数 + 使用次数 + 特性开关 | 对齐现有比特业务属性,功能上不降级 |
| 后端集成 | 复用 API + Webhook 双服务 | 签发走 API(管理操作需认证鉴权),SDK 交互走 Webhook(高频快速 2xx) |
| 比特兼容 | 双线共存 + Provider 可扩展架构 | 后续可接入更多第三方 |
| 终端识别 | 硬件指纹分层采集 + 稳定度评分兜底 | 强指纹精确匹配,弱指纹分配服务器 UUID,管理员可手动释放 |
| Rust 架构 | 单 cdylib + trait 多 Provider | 公共模块共享,扩展第三方只需增加 trait 实现 |
| SDK 交互安全 | Nonce + Timestamp + HMAC 签名防重放 | 简单有效,无需序列号同步 |
2. 总体架构
2.1 全系统组件图
┌─────────────────────────────────────────────────────────────────┐
│ 客户现场 │
│ ┌──────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 客户应用 │───▶│ Java SDK │ │ 许可证配置文件 │ │
│ └──────────┘ │ AuthProvider impl │◀───│ ~/.craftlabs/ │ │
│ │ ┌──────────────┐ │ │ license_cache │ │
│ │ │BitAnswerProv │ │ │ device_id │ │
│ │ ├──────────────┤ │ └──────────────────┘ │
│ │ │SelfHostedPrv │ │ │
│ │ └──────┬───────┘ │ │
│ └────────┼────────┘ │
│ │ JNI │
│ ┌────────▼────────┐ │
│ │ Rust craft-core │ libcraftlabs_auth_core │
│ │ ◇ trait Provider│ │
│ │ ┌─────────────┐ │ │
│ │ │BitAnswer │ │──────▶ 比特安索云 │
│ │ ├─────────────┤ │ │
│ │ │SelfHosted │ │──HTTPS▶ license-webhook │
│ │ └─────────────┘ │ │
│ │ device.rs │ 硬件指纹分层采集 │
│ │ crypto.rs │ HKDF+AES-GCM+RSA验签 │
│ │ security/ │ 反调试/完整性/混淆 │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 创飞机房 / 云 │
│ │
│ ┌──────────────────────────┐ ┌──────────────────────────┐ │
│ │ license-webhook-ingress │ │ delivery-platform-api │ │
│ │ :8081 │ │ :8080 │ │
│ │ SDK 在线端点: │ │ 许可证签发端点: │ │
│ │ /license/v1/activate │◀──│ /api/v1/licenses │ │
│ │ /license/v1/heartbeat │ │ /api/v1/licenses/{id} │ │
│ │ /license/v1/check │ │ /api/v1/licenses/{id}/ │ │
│ │ /license/v1/release │ │ revoke │ │
│ │ │ │ │ │
│ │ 事件回调 ─────────────▶ │ │ 合同/SN/终端/审计 │ │
│ └──────────────────────────┘ └──────────────────────────┘ │
│ │ │ │
│ └──────────┬─────────────────┘ │
│ ▼ │
│ ┌──────────────┐ │
│ │ PostgreSQL 15│ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2.2 Rust 层模块结构(改造后)
native/craft-core/src/
├── lib.rs # cdylib 入口 + C ABI 路由
├── trait_provider.rs # Provider trait 定义 + select_provider
│
├── provider_bitanswer/ # 现有逻辑封装
│ ├── mod.rs
│ ├── activate.rs
│ ├── license.rs
│ └── heartbeat.rs
│
├── provider_selfhosted/ # ★ 本次核心交付
│ ├── mod.rs # SelfHostedProvider impl Provider
│ ├── activate.rs # HTTPS POST → webhook:8081
│ ├── license.rs # 验签 + 解密 + 离线校验
│ ├── heartbeat.rs # HTTPS 心跳 + 租约续期
│ ├── protocol.rs # 请求/响应序列化
│ └── cache.rs # 许可证本地加密存储
│
├── device.rs # ★ 硬件指纹分层采集
├── crypto.rs # ★ HKDF + AES-256-GCM + RSA 验签
├── session.rs # 泛化 session(去 bit_handle)
├── error.rs # 自研 + 比特错误码体系
│
└── security/ # 不变
├── mod.rs
├── anti_debug.rs
├── integrity.rs
├── obfuscation.rs
└── string_encrypt.rs
2.3 Provider trait 契约
pub trait Provider: Send + Sync {
fn initialize(&mut self, ctx: &CraftContext, config: &AuthConfig)
-> Result<(), LicenseError>;
fn activate(&self, ctx: &CraftContext, license_key: &str)
-> Result<ActivateResponse, LicenseError>;
fn check_license(&self, ctx: &CraftContext)
-> Result<LicenseStatus, LicenseError>;
fn heartbeat(&self, ctx: &CraftContext)
-> Result<HeartbeatResponse, LicenseError>;
fn has_feature(&self, ctx: &CraftContext, name: &str) -> bool;
fn release(&mut self, ctx: &CraftContext) -> Result<(), LicenseError>;
fn get_license_info(&self, ctx: &CraftContext) -> LicenseInfoFFI;
fn close(&mut self);
}
2.4 Java 层变更
| 模块 | 变更 |
|---|---|
craftlabs-auth-core |
接口不变;SelfhostedConfigSection 扩展 offlineGraceDays、heartbeatIntervalHours 等字段;FeatureMapping 扩展 selfhostedFeatureKey |
craftlabs-auth-bitanswer |
不变(双线共存) |
craftlabs-auth-selfhosted |
从桩改为真实实现:System.loadLibrary("craftlabs_auth_core") |
craftlabs-auth-tests |
新增 SelfHostedProviderTest、MultiProviderSmokeTest |
2.5 关键架构约束
- 单一 cdylib:
craftlabs_auth_core包含所有 provider,通过 trait 路由 - Java 侧无感知:各 AuthProvider 均调 NativeBridge,路由在 Rust 层完成
- 双线 classpath 隔离:BitAnswer 和 SelfHosted 不同 Maven 模块
- 契约不变:AuthProvider 接口 7 方法不变、AuthConfig record 不变
- Schema 兼容:selfhosted 段向后兼容扩展新字段
3. 许可证协议与数据模型
3.1 License JSON 结构
{
"version": 1,
"license_id": "01JQXYZ...", // ULID
"issued_at": "2026-05-18T10:00:00Z",
// 载荷:AES-256-GCM 加密后的 Base64 密文
// 解密后为 LicensePayload:{ tenant_id, product, grant, constraints, features, custom }
"payload": "A8f3Kd9s...base64url...",
"signature": {
"algorithm": "RS256",
"key_id": "kp_2026_q2", // 支持密钥轮换
"value": "MEUCIQDx..." // RS256 签名(对密文 payload 签名)
}
}
3.2 LicensePayload(解密后明文)
{
"tenant_id": "craftlabs-wharf-prod",
"product": "wharf-inspection-v2",
"grant": {
"type": "perpetual", // perpetual | subscription | trial
"not_before": "2026-05-01T00:00:00Z",
"not_after": "2027-05-01T00:00:00Z",
"offline_grace_days": 7,
"heartbeat_interval_hours": 24
},
"constraints": {
"max_devices": 5,
"max_concurrent_users": 0, // 0=不限制
"max_activations": 0
},
"features": {
"advanced_analytics": true,
"real_time_monitor": false,
"api_export": true
},
"custom": { // 扩展字段
"contract_ref": "CT-2026-0042",
"project_id": "wharf-nansha-phase2"
}
}
3.3 签发与校验流程
签发(服务器侧):
LicensePayload 明文
→ AES-256-GCM 加密(key = HKDF(编译期盐, license_id))
→ 密文 payload (Base64)
→ RSA-SHA256 签名(对密文签名)
→ 完整 license.json
校验(Rust SDK 侧):
license.json
→ RSA 公钥验签(快速排除伪造)
→ HKDF 派生 AES 密钥
→ AES-256-GCM 解密
→ 时间窗口校验(not_before ≤ now ≤ not_after + 离线宽限期)
→ 特性/约束校验
3.4 在线交互协议
端点:license-webhook-ingress:8081/license/v1/*
| 端点 | 方法 | 请求体 | 成功响应 | 错误响应 |
|---|---|---|---|---|
/activate |
POST | {license_key, device_fingerprint} |
200 {status:"activated", device_id, license_payload} |
409 终端满 / 403 已吊销 / 422 无效 |
/heartbeat |
POST | {license_key, device_hash, local_time} |
200 {status:"ok", lease_renewed_until} |
410 已过期/吊销 |
/check |
POST | {license_key, device_hash} |
200 {status:"valid", features, not_after} |
410 已过期/吊销 |
/release |
POST | {license_key, device_hash} |
200 {status:"released"} |
— |
防重放:每个请求携带 X-Craft-Nonce、X-Craft-Timestamp、X-Craft-Signature(HMAC-SHA256),服务器时间窗口 5 分钟 + Nonce 去重。
3.5 签发端点(API 侧)
POST /api/v1/licenses # 创建/签发许可证
GET /api/v1/licenses # 分页查询
GET /api/v1/licenses/{licenseId} # 详情
POST /api/v1/licenses/{id}/revoke # 吊销
GET /api/v1/licenses/{id}/activations # 激活记录
POST /api/v1/licenses/{id}/activations/{aid}/release # 释放设备
3.6 数据库表(新增)
| 表 | 用途 |
|---|---|
platform_licenses |
许可证主表(license_id、有效期、约束、状态、签名快照) |
platform_license_features |
特性开关(license_id, feature_key, enabled) |
platform_license_activations |
终端激活记录(license_id, device_hash, stability_score, status) |
platform_license_heartbeats |
心跳审计(可选,视量级) |
platform_license_keys |
RSA 密钥对管理(key_id, public_key, private_key) |
platform_license_policies |
策略模板(默认有效期、终端数、特性等) |
4. Rust 层核心逻辑
4.1 新增依赖
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
rsa = { version = "0.9", features = ["sha2"] }
aes-gcm = "0.10"
hkdf = "0.12"
base64 = "0.22"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
chrono = { version = "0.4", features = ["serde"] }
4.2 关键数据结构
pub struct DeviceFingerprint {
pub composite_hash: String, // SHA-256(layer1|layer2|layer3|layer4)
pub stability_score: u8, // 0~100,影响匹配策略
pub layers: Vec<FingerprintLayer>,
}
pub struct LicenseStatus {
pub licensed: bool,
pub not_after: Option<i64>,
pub features: HashMap<String, bool>,
pub device_count: u32,
pub max_devices: u32,
pub heartbeat_due: Option<i64>,
}
4.3 硬件指纹分层采集
| 层级 | 权重 | 来源 |
|---|---|---|
| Layer 1 硬件 | 40 | Linux: DMI product_uuid / Windows: SMBIOS UUID / macOS: IOPlatformUUID |
| Layer 2 OS | 30 | Linux: /etc/machine-id / Windows: MachineGuid / macOS: 同 L1 |
| Layer 3 存储 | 20 | 根文件系统 UUID (blkid/VolumeSerialNumber) |
| Layer 4 网络 | 10 | 物理网卡 MAC(跳过 docker/tap/lo/veth) |
稳定度评分:Layer1=有+Layer2=有 → 70(强指纹);仅 Layer2 有 → 40;仅 Layer4 → 10。
兜底:稳定度 < 20 时,服务器分配 UUID 并加密存储到 ~/.craftlabs/device_id,管理员可手动吊销释放配额。
4.4 离线宽限期
fn check_license离线(&self) -> Result<LicenseStatus, LicenseError> {
// 1. 加载缓存许可证
// 2. 检查距离上次在线心跳的天数
// 3. 超过 offline_grace_days → OfflineGraceExceeded
// 4. 未超过 → 时间窗口校验(not_before/not_after)
// 5. 返回 LicenseStatus(device_count=0 标记离线)
}
4.5 错误码体系
pub enum LicenseError {
// 通用
Success, ConfigMissing, Network, NotInitialized,
// 签名/加密
InvalidFormat, InvalidSignature, SignatureMismatch, CryptoError,
DecryptionFailed, CorruptedPayload, LicenseIdMismatch,
// 许可状态
NotYetValid, Expired, NoCachedLicense,
OfflineGraceExceeded { days_offline, max_days },
InvalidLicense, LicenseRevoked,
DeviceLimitReached, ConcurrentUserLimitReached, ActivationLimitReached,
// 兼容比特
BitAnswerStatus(u32),
UnknownStatus(u16),
}
4.6 本地缓存
~/.craftlabs/
├── device_id # 硬件指纹或服务器 UUID
├── license_cache.json # AES-256-GCM 加密的许可证副本
│ # 加密密钥 = SHA256(device_id + EMBEDDED_SALT)
└── heartbeat_state.json # { last_heartbeat, lease_until }
4.7 编译期公钥嵌入
build.rs 读取 native/craft-core/embedded/pubkey.pem,生成 const EMBEDDED_PUBLIC_KEY: &str = "...",crypto.rs 在 initialize 时解析为 RsaPublicKey。
5. 平台后端变更
5.1 delivery-platform-api 新增模块
license/
├── LicenseController.java # REST /api/v1/licenses
├── LicenseService.java # 签发/吊销/查询
├── LicenseSigner.java # RSA+AES 签发
└── LicenseKeyManager.java # 密钥对管理
persistence/license/
├── PlatformLicense.java & Mapper
├── PlatformLicenseFeature.java & Mapper
├── PlatformLicenseActivation.java & Mapper
├── PlatformLicenseKey.java & Mapper
└── PlatformLicensePolicy.java & Mapper
新增角色:LICENSE_OPS,管理 /api/v1/licenses/** 的写权限。
5.2 license-webhook-ingress 新增模块
license/
├── LicenseController.java # /license/v1/*
├── LicenseActivateService.java # 激活 + 终端匹配
├── LicenseHeartbeatService.java # 心跳 + 吊销检测
├── LicenseCheckService.java # 在线校验
├── LicenseReleaseService.java # 设备释放
├── DeviceMatcher.java # 分层指纹匹配
└── NonceValidator.java # 防重放
5.3 事件回调
激活/心跳失败/到期/吊销等事件,沿现有 Webhook→API 异步投递链路通知 API 更新台账和审计。
6. 安全设计
6.1 许可证防篡改
| 措施 | 作用 |
|---|---|
| 载荷 AES-256-GCM 加密 | 阻止直接查看许可证内容(特性/期限/终端数),每个 license_id 独立密钥防批量破解 |
| 密文 RSA-SHA256 签名 | 验签不通过 = 篡改,先行快速拒绝 |
| HKDF 密钥派生 | 盐编译期嵌入 + license_id,增加逆向提取难度 |
| 公钥编译期嵌入 | 不在运行时从文件或网络加载,防替换攻击 |
6.2 SDK 在线交互安全
| 措施 | 说明 |
|---|---|
| TLS | 全链路 HTTPS,证书校验 |
| HMAC 签名 | 每个请求 X-Craft-Signature = HMAC-SHA256(nonce |
| Nonce 去重 | Redis SETNX + 时间窗口 5 分钟,防重放 |
| Authorization 头 | Bearer tenantKey 双向验证 |
6.3 运行时保护
Rust 侧复用现有 security/ 模块:反调试检测、完整性校验、字符串混淆,适配自研路径。
7. 实施阶段
Phase 1:离线核心
目标:管理员签发 → 文件交付 → SDK 本地验签解密
- Rust: crypto.rs、license.rs、cache.rs、device.rs、error.rs
- Java: SelfhostedConfigSection 扩展字段
- 平台: 数据库迁移、LicenseSigner、LicenseController(签发/查询/吊销)
- 验证: AES 往返测试、RSA 验签测试、过期拒绝测试
Phase 2:在线激活
目标:SDK 网络激活获取许可证,终端配额限制
- Rust: activate.rs、protocol.rs、trait_provider.rs、reqwest 集成
- Webhook: LicenseController、ActivateService、DeviceMatcher、NonceValidator
- 平台: 终端释放端点
- 验证: Mock HTTP 测试、终端满 409 测试
Phase 3:心跳 + 离线兜底
目标:心跳维持租约、离线宽限期降级、吊销远程生效
- Rust: heartbeat.rs、check_license 离线逻辑
- Webhook: HeartbeatService、CheckService、ReleaseService
- 验证: 心跳成功更新租约、断网 8 天 OfflineGraceExceeded、吊销后 410
Phase 4:完善与生产加固
目标:双 Provider 切换、CI/CD、文档
- Rust: build.rs 公钥嵌入、security 模块适配
- Java: MultiProviderSmokeTest、SelfHostedProviderTest
- CI: ci-native.yml 适配、ci-platform.yml 新增
- 文档: 集成指南、操作手册、CHANGELOG
工作量估算
| Phase | Rust | Java SDK | Platform API | Webhook | 合计 |
|---|---|---|---|---|---|
| P1 | M | S | M | — | M~L |
| P2 | M | — | S | M | M |
| P3 | S | — | — | M | M |
| P4 | S | S | — | — | S |
(S=小,M=中,L=大)