license issuance and verification

下一代 License
签发与验证框架

license-next 把 License 的签发(issuer)与客户端的校验(checker)彻底分离——不需要常驻的 license server 进程,极其轻量的客户端集成体验。

License
product: myapp
customer: acme-001
expireAt: 2030-12-31
machineID: target-machine-id
CKD · 内部编码 c_data: 8f3a1c…e02d
ECC · 外部签名 timestamp: 1750000000 · c_data: 8f3a1c…e02d
ECDSA P-256 · signature: MEUCIQDx7…3kZ2A==
revoked: false · 新鲜度窗口内
core concepts

License 是合同,CData 是它的密文

四个概念构成整个框架:合同本身、合同的可逆编码、签名后的传输结构,以及校验结果的三种状态。

License

授权合同的原始明文:customer、product、expireAt、machineID、features。是签发和校验共同理解的业务对象。

CData

License 经 CKD 协议用 MasterKey 派生编码后的字符串——可逆、无状态、不直接暴露明文,客户端配置同一 MasterKey 即可还原。

LicenseSign

{ c_data, timestamp, signature, revoked }。签名覆盖 CData + "|" + timestamp,防止 CData 被单独重放。

校验结果三态

ResultOK 直接放行;ResultNeedRemote 联网确认,失败即拒绝启动;ResultInvalid 明确不匹配,直接拒绝。

api overview

API 速览

方法说明
Check()完整校验,本地优先 + 自动远端刷新
SimpleCheck()纯本地校验,不联网
Verify(licJSON)验签并持久化 .lic 文件内容
architecture

两种业务形态,一套框架

两种形态的区别只在于客户端校验时要不要联网确认新鲜度。

签发端(html或wasm集成)issuer

打开issuer.html,用 WASM 编码的 encodeCData 生成 License,下载 .lic 文件。

手动复制 .lic 给客户

客户端(Go 应用)checker

licensenext.New(cfg) → SimpleCheck(),只做本地缓存校验:签名有效性、机器码、过期时间、产品名,不校验签名新鲜度

优点:不依赖服务端 · 代价:无法主动吊销

签发端(html或wasm集成)issuer

同样通过 issuer.html 生成 CData 字符串,手动复制。

写入 KV

请求端Cloudflare Worker

提供开箱即用的 Cloudflare Worker 代码。KV 以 machine_id 为 key 存储 CData;GET /v1/query?machine_id=xxx 读取后用 Web Crypto ECDSA P-256 签名,返回 LicenseSign JSON。

超出新鲜度窗口时自动请求

客户端(Go 应用)checker

licensenext.New(cfg) → Check():本地校验优先,新鲜度过期才联网刷新签名;异步预刷新按 3 天节流,不阻塞启动。

优点:可通过 revoked 字段主动吊销 · 代价:需要部署一个 Worker
check vs simplecheck

联网校验,还是纯离线校验

两者共享同一套本地校验逻辑,区别只在要不要联网、要不要新鲜度窗口。

能力CheckSimpleCheck
签名 / 机器码 / 产品一致性 / 过期判断
本地缓存读取与验签
需要本地手动配置 .lic 文件不需要,自动远端 fetch需要
新鲜度窗口超窗触发远端不参与
远端请求自动不触发
调用方式Check()Verify() + SimpleCheck()
comparison

与老牌方案相比

核心校验能力(签名、机器码绑定、过期、吊销)与 FlexLM、Sentinel、keygen.sh 对齐,但运维负担更轻。

FlexLMSentinelkeygen.shlicense-next
部署形态独立 license server 进程独立守护进程 / 硬件狗托管 SaaS API一个 HTTP 查询接口 + 本地缓存
运维负担需要维护常驻服务需要维护常驻服务依赖第三方可用性Worker 无状态,KV 存储
离线可用受限支持(硬件狗)受限纯离线签名模式原生支持
主动吊销✓(在线刷新模式)
调用方接入心智需理解服务端协议细节需接入 SDK 与硬件层需处理 API 限流 / 重试只区分成功 / 失败两种结果
代码归属闭源商业授权闭源商业授权部分开源MIT 开源
quickstart

五分钟接入

01

安装

go get github.com/doc-war/license-next
02

生成密钥对

openssl ecparam -genkey -name prime256v1 -out key-private.pem
openssl ec -in key-private.pem -pubout -out key-public.pem
03

在浏览器里生成 License

打开 official/issuer.html,填入 License 参数与 MasterKey,直接生成 .lic 文件交给客户;也可以用 Go 侧的 issuer 子包生成。

import "github.com/doc-war/license-next/issuer"

iss, _ := issuer.New(issuer.Config{
    PrivateKey: privPEM,
    MasterKey:  masterKey,
})
ls, _ := iss.Sign(&issuer.License{
    Customer:  "acme-001",
    Product:   "myapp",
    ExpireAt:  time.Date(2030, 12, 31, 23, 59, 59, 0, time.UTC),
    MachineID: "target-machine-id",
})
04

客户端集成

checker, err := licensenext.New(licensenext.Config{
    Product:   "myapp",
    PublicKey: pubPEM,
    MasterKey: masterKey,
    RemoteURL: "https://your-worker.workers.dev/v1/query",
})

lic, err := checker.Check()
if err != nil {
    log.Fatalf("license校验失败: %v", err)
}
log.Printf("欢迎 %s,有效期至 %s", lic.CustomerNickname, lic.ExpireAt.Format("2006-01-02"))
config

配置项

字段默认值说明
Product string必填产品名,用于隔离本地存储目录
MasterKey string必填CKD 主密钥
PublicKey string必填ECC 公钥 PEM
RemoteURL string""license 查询 API 地址
StorageDir string~/.license-next/{product}/本地存储根目录
FreshWindow time.Duration7 天新鲜度窗口
RefreshInterval time.Duration3 天异步预刷新节流间隔
HTTPTimeout time.Duration5s远端请求超时(失败后自动重试一次)
sub-projects

仓库结构

目录说明
official/官网在线工具,issuer.html 生成 CData,index.html 文档页
wasm/浏览器 WASM 入口(GOOS=js 编译),暴露 encodeCData
worker/Cloudflare Worker 模板,Web Crypto 签名 + KV 查询
issuer/Go 服务端签发包(ECDSA 签名 + CKD 派生)
examples/集成示例(checker / issuer / 全流程)