第 25 章:软件许可 — 离线签名授权与在线激活
示例工程:
examples-middleware/license_demo(不需要任何外部服务,纯内存/离线)
学习目标
- 理解商业软件为什么需要许可授权,以及两类方案的适用场景
- 实现离线签名授权:ed25519 签发 + 客户端验签 + 过期/机器绑定/功能位
- 实现在线激活:激活配额 + 心跳续租 + 远程吊销
- 说清离线与在线的取舍,以及为什么常两者结合
25.1 两类许可方案
商业软件要防盗用、按授权范围与期限使用。两条主流路线:
| 离线签名授权 | 在线激活 | |
|---|---|---|
| 原理 | 厂商私钥签发 license 文件,客户端内置公钥验证 | 软件定期向激活服务器校验 |
| 联网 | 断网可用 | 依赖网络与激活服务 |
| 撤销 | 不能远程撤销,只能等过期 | 可远程吊销/计量 |
| 适用 | 私有化部署、离线环境 | SaaS、需要远程管控 |
license_demo 两个都实现了,并演示它们各自的边界。
25.2 离线签名授权:公钥自证
核心思路:厂商用私钥签发,客户端用内置公钥验证,私钥永不离开厂商签发机。
rust
struct License {
licensee: String, product: String, edition: String,
features: Vec<String>, // 功能位:控制"专业版/企业版"解锁什么
machine_fingerprint: String, // 绑定机器,防一份 license 到处装
issued_at: i64, expires_at: i64,
}
struct SignedLicense { license: License, signature_b64: String } // 对 license 规范 JSON 的 ed25519 签名客户端 verify 严格按顺序三步,签名必须最先验:
text
1. 验签名(BadSignature)—— 必须第一步!否则下面的 expires/指纹都可能是篡改过的
2. 查是否过期 now > expires_at(Expired)
3. 核机器指纹是否匹配(MachineMismatch)为什么签名先行:如果先看 expires_at 再验签名,攻击者改一下过期时间你就信了——未经验签的任何字段都不可信。has_feature(license, name) 实现「同一个二进制按 license 解锁不同功能」,这就是专业版/企业版的技术底层。
机器指纹(machine_fingerprint)由主机名/CPU 等稳定信息哈希得到——示例把原料做成可注入的,保证测试确定;真实实现要综合多个硬件标识,还得容忍硬件小变动(换个内存条不该让 license 失效)。
25.3 在线激活:可远程吊销
离线方案签发后就管不了了。需要「卖出去还能远程关掉」(欠费、盗版)时,用在线激活:
text
POST /activate {license_key, machine_fp}
→ 校验 key 合法 + 未吊销 + 未超机器数配额 → 发带 lease_expires 的激活令牌
(同机重复激活不占配额,对客户端重试幂等友好)
POST /heartbeat {activation_token}
→ 续租;若 key 已被 revoke() → 返回失效,客户端应停止服务远程吊销是在线相对离线的核心价值:厂商 revoke(key) 后,客户端下次心跳就失效。「一个 key 限 N 台机器」也是靠激活时记录 machine_fp 数量实现的。代价是:激活服务器挂了或断网,客户端可能无法启动——所以要设计好宽限期(lease 别太短)。
25.4 两者结合
生产商业软件常两条腿走路:离线签名保底(保证断网也能用、不被激活服务器单点拖死)+ 在线激活增强(计量、远程吊销、防超用)。签名保证「这份授权是真的」,在线保证「这份授权现在还有效」。
bash
cd examples-middleware
cargo run -p license_demo # 签发→验证→篡改/过期/换机器各自被拒→在线激活→吊销后失效本章小结
- 离线签名:厂商私钥签发、客户端公钥验证,断网可用但不能远程撤销
- verify 三步且签名必须最先——未验签的字段一律不可信
- 功能位(features)让一个二进制按 license 解锁不同版本
- 机器指纹绑定防一份 license 到处装,原料要可注入(测试)、要容忍硬件小变动
- 在线激活:激活配额 + 心跳续租 + 远程吊销,代价是依赖激活服务可用性
- 生产常离线签名保底 + 在线激活增强
自测清单
- [ ] 我能说出离线与在线授权各自的适用场景和短板
- [ ] 我能解释为什么 verify 必须先验签名
- [ ] 我能说出功能位怎么实现"同一二进制多版本"
- [ ] 我能解释机器指纹的作用和"容忍硬件小变动"的必要
- [ ] 我能描述在线激活的配额与吊销机制
- [ ] 我能说出为什么生产常两者结合
练习
- 给离线 license 加「宽限期」:过期后 7 天内仍可用但打警告,超 7 天才彻底停——体会硬过期对用户体验的伤害。
- 把 license 文件加载做成「首次校验后缓存结果 + 定期重验」,减少每次操作都验签的开销。
- 给在线激活加「离线宽限」:客户端记住上次成功心跳时间,激活服务器短暂不可达时允许继续运行 N 小时,融合两种方案。
- 思考并写注释:示例的机器指纹只用主机名,太容易伪造。列出生产上更可靠的指纹来源及各自的隐私/稳定性权衡。
动手验证:
bash
cd examples-middleware
cargo test -p license_demo # 签名/过期/机器/吊销/配额的完整断言,默认全跑