From d7469afee9e7c2bc879173fc957fc660dee77555 Mon Sep 17 00:00:00 2001 From: huangping Date: Mon, 18 May 2026 21:00:56 +0800 Subject: [PATCH] docs: self-hosted licensing SDK design spec --- ...6-05-18-selfhosted-licensing-sdk-design.md | 477 ++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-18-selfhosted-licensing-sdk-design.md diff --git a/docs/superpowers/specs/2026-05-18-selfhosted-licensing-sdk-design.md b/docs/superpowers/specs/2026-05-18-selfhosted-licensing-sdk-design.md new file mode 100644 index 0000000..ee2efdd --- /dev/null +++ b/docs/superpowers/specs/2026-05-18-selfhosted-licensing-sdk-design.md @@ -0,0 +1,477 @@ +# 自研授权 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; + fn check_license(&self, ctx: &CraftContext) + -> Result; + fn heartbeat(&self, ctx: &CraftContext) + -> Result; + 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, +} + +pub struct LicenseStatus { + pub licensed: bool, + pub not_after: Option, + pub features: HashMap, + pub device_count: u32, + pub max_devices: u32, + pub heartbeat_due: Option, +} +``` + +### 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 { + // 1. 加载缓存许可证 + // 2. 检查距离上次在线心跳的天数 + // 3. 超过 offline_grace_days → OfflineGraceExceeded + // 4. 未超过 → 时间窗口校验(not_before/not_after) + // 5. 返回 LicenseStatus(device_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=大)