Files
craftlabs-authorization-sdk/docs/superpowers/specs/2026-05-18-selfhosted-licensing-sdk-design.md
T

478 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 自研授权 SDK 设计方案
> **日期**2026-05-18
> **背景**:比特安索授权云费用过高,决定优先推进自研授权方案。
> **原则**:与比特安索双线共存,Provider 可扩展架构,后续可接入更多第三方授权方式。
---
## 目录
1. [决策摘要](#1-决策摘要)
2. [总体架构](#2-总体架构)
3. [许可证协议与数据模型](#3-许可证协议与数据模型)
4. [Rust 层核心逻辑](#4-rust-层核心逻辑)
5. [平台后端变更](#5-平台后端变更)
6. [安全设计](#6-安全设计)
7. [实施阶段](#7-实施阶段)
---
## 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 契约
```rust
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 结构
```jsonc
{
"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(解密后明文)
```jsonc
{
"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 新增依赖
```toml
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 关键数据结构
```rust
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 离线宽限期
```rust
fn check_license离线(&self) -> Result<LicenseStatus, LicenseError> {
// 1. 加载缓存许可证
// 2. 检查距离上次在线心跳的天数
// 3. 超过 offline_grace_days → OfflineGraceExceeded
// 4. 未超过 → 时间窗口校验(not_before/not_after
// 5. 返回 LicenseStatusdevice_count=0 标记离线)
}
```
### 4.5 错误码体系
```rust
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|ts|method|path|body, tenantKey) |
| **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=大)