Skip to content

激活认证 Quick Start

本文档面向需要接入 Licence Server 的第三方软件客户端,说明如何通过激活服务完成软件认证。

当前支持两种接入方式:

  • 方式一:扫码激活
  • 方式二:手动下发激活码

基础信息

服务地址

下文统一使用 {BASE_URL} 表示注册器服务地址。

示例:

text
http://127.0.0.1:1280

所有认证接口统一使用以下路由前缀:

text
/sansi/register/api/v1

完整请求地址格式:

text
{BASE_URL}/sansi/register/api/v1/{resource}

数据格式

  • 请求体:application/json
  • 时间戳:Unix 秒级时间戳,除非特别说明
  • 激活码展示:服务端返回的激活码为不带横杠的字符串,客户端可按 4 位一组自行格式化展示

已支持 softName

softName 必须使用服务端已配置的软件名称,当前支持:

softName
cyber-hub
starriver
easyplay
v3pro
v3pro-arm
quasar
vizohub
plc
hulk

激活类型和默认有效期

首字母code说明扫码激活默认有效期手动下发有效期限制
TtrialActivation试用激活当前时间 + 1 个月validityPeriod 为准有效期不能超过 30 天
EemergencyActivation应急激活当前时间 + 7 天validityPeriod 为准有效期不能超过 7 天
FformallyActivation正式激活当前时间 + 99 年validityPeriod 为准需要合同号
HhomeActivation家庭激活(预留)当前时间 + 99 年validityPeriod 为准需要合同号
XcustomizedActivation定制激活(预留)当前时间 + 99 年validityPeriod 为准需要合同号

说明:

  • 扫码激活的默认有效期只在移动端确认激活时没有传 deadline 时生效。
  • 扫码激活如果传了 deadline,服务端使用传入的 Unix 秒级时间戳作为最终有效期。
  • 手动下发创建中间码时,服务端不会按激活类型自动补默认有效期,必须显式传 validityPeriod
  • 如果 activeType 为空或不是上述枚举,服务端会按 trialActivation 处理;建议客户端不要依赖这个兜底逻辑。

设备标识

第三方客户端需要提供稳定的设备标识。服务端当前参与激活码计算的字段为:

字段说明是否必填
softName软件名称,需要和服务端配置枚举一致
contractNumber合同号。试用/应急激活可为空,其他类型必填按激活类型
sysUUID系统 UUID扫码激活必填;手动下发时和 hardNumber 不能同时为空
hardNumber硬盘/硬件序列号(注意虚拟机)扫码激活必填;手动下发时和 sysUUID 不能同时为空
computerName设备名称建议提供

客户端应保证 sysUUIDhardNumber 的获取逻辑稳定。这里别整花活,设备指纹一变,激活码自然就对不上。

方式一:扫码激活(推荐使用)

扫码激活适用于客户端可以展示二维码,并由企业微信/移动端扫码确认的场景。

流程

https://ccs-pro.sansi.io/cyberhub/deploy.html#软件激活

  1. 第三方客户端采集设备信息。
  2. 第三方客户端按服务端约定生成 RSA 加密二维码。
  3. 移动端扫码后调用服务端,将二维码状态置为 activing
  4. 移动端选择激活类型、使用场景并确认激活。
  5. 服务端生成最终激活码,并将二维码状态置为 actived
  6. 第三方客户端轮询二维码状态,拿到最终激活码。
  7. 第三方客户端保存激活码,并在本地或通过服务端校验激活状态。

1. 生成二维码内容

二维码中的核心字段是 code,它是 RSA 加密后的 Base64 字符串。

明文格式必须按以下顺序拼接,字段之间使用英文逗号:

text
{softName},{contractNumber},{sysUUID},{timestamp},{computerName},{hardNumber},{deadline},{deviceCount}

示例明文:

text
cyber-hub,HT202606150001,550E8400-E29B-41D4-A716-446655440000,1781518800,DESKTOP-001,DISK-SN-001,1765288356,25556

加密要求:

  • 算法:RSA
  • Padding:PKCS#1 v1.5
  • 公钥:使用与服务端 server/key/private.txt 配套的公钥
  • 输出:Base64 字符串

二维码内容建议为 JSON:

json
{
  "code": "BASE64_RSA_CIPHERTEXT"
}

公钥

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCu6eFNmpyEYZfpAZMqI7FEnt6h
N1BqwPZAHaHh+P6u4GBkxZGES1clAo3YjqEgL+68JLrAc9rZRaq95LPHIk/SJ688
hzXD6RTtsW3EKo6HTTKWhzAXQAtaCAsigYq40ewZzkk6bTDpy4ourJVnlLqLFMTk
6RJsrK5E7sxiGuxWqQIDAQAB
-----END PUBLIC KEY-----

当前移动端扫码页面会解析二维码 JSON,并读取其中的 code 字段。

2. 客户端轮询激活状态

第三方客户端展示二维码后,应轮询状态接口。

http
POST /sansi/register/api/v1/active/code/status

请求示例:

bash
curl -X POST "{BASE_URL}/sansi/register/api/v1/active/code/status" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "BASE64_RSA_CIPHERTEXT"
  }'

响应示例:

json
{
  "status": "unknown",
  "activeCode": ""
}

status 说明:

status说明客户端处理
unknown二维码尚未被扫描继续轮询
activing已扫码,用户尚未确认激活提示用户在移动端确认
actived已激活成功读取 activeCode 并保存

建议轮询间隔 2-5 秒,并设置总超时时间。二维码明文中的 timestamp 太旧会导致激活失败,服务端当前会按时间判断二维码是否过期。

3. 移动端扫码通知服务端

移动端扫码后调用该接口,把二维码状态置为 activing

http
POST /sansi/register/api/v1/active/code/scan

请求示例:

bash
curl -X POST "{BASE_URL}/sansi/register/api/v1/active/code/scan" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "BASE64_RSA_CIPHERTEXT",
    "user": ""
  }'

成功响应:

json
{
  "status": "activing",
  "softName": "cyber-hub",
  "contractNumber": "HT202606150001"
}

4. 移动端确认激活

http
POST /sansi/register/api/v1/message?activeType={activeType}

activeType 决定许可证首字母和默认有效期。请求体可选传 deadline,传入后会覆盖该激活类型的默认有效期。

请求示例:

bash
curl -X POST "{BASE_URL}/sansi/register/api/v1/message?activeType=formallyActivation" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "BASE64_RSA_CIPHERTEXT",
    "user": "张三",
    "usageScenarios": ["traffic"],
    "remark": "项目现场激活"
  }'

成功响应:

json
{
  "status": "F123456789ABCDEF"
}

注意:这里响应字段名叫 status,但值实际是最终激活码。这个命名挺别扭,对接时别被它骗了。

默认有效期: | --- | --- | --- | | trialActivation | 当前时间 + 1 个月 | 使用传入的 Unix 秒级时间戳 | | emergencyActivation | 当前时间 + 7 天 | 使用传入的 Unix 秒级时间戳 | | formallyActivation | 当前时间 + 99 年 | 使用传入的 Unix 秒级时间戳 | | homeActivation | 当前时间 + 99 年 | 使用传入的 Unix 秒级时间戳 | | customizedActivation | 当前时间 + 99 年 | 使用传入的 Unix 秒级时间戳 |

5. 无网离线校验(客户端本地校验)

无网离线校验属于扫码激活链路的本地校验部分,用于客户端拿到最终 activeCode 后,在后续无法访问注册器服务时自行判断许可证是否匹配本机。

客户端本地校验的核心是:使用本机设备标识按同一套 SHA-256 规则重新生成激活码,并与扫码激活得到的 activeCode 对比。

5.1 离线激活码生成规则

最终 activeCode 使用以下字段按固定顺序直接拼接,不使用分隔符:

text
{softName}{contractNumber}{hardNumber}{sysUUID}

字段说明:

字段说明
softName软件名称,需要和注册器配置一致
contractNumber合同号。试用/应急激活可为空;为空时按空字符串参与拼接
hardNumber硬盘/硬件序列号。为空时按空字符串参与拼接
sysUUID系统 UUID。为空时按空字符串参与拼接

生成步骤:

  1. 使用 UTF-8 将拼接后的明文转为字节。
  2. 计算 SHA-256。
  3. 取 SHA-256 结果的前 8 个字节,并转为大写十六进制字符串。
  4. 用激活类型首字母替换第 1 个字符。

伪代码:

text
plain = softName + contractNumber + hardNumber + sysUUID
sha256First8Hex = UPPERCASE(HEX(SHA256(UTF8(plain))[0:8]))
activeCode = activeTypePrefix + SUBSTRING(sha256First8Hex, 1, 15)

其中 SHA256(...)[0:8] 表示取 SHA-256 结果的前 8 个字节;SUBSTRING(sha256First8Hex, 1, 15) 表示从下标 1 开始取 15 个字符。最终 activeCode 长度为 16 位。

激活类型首字母沿用前文表格:

activeTypeprefix
trialActivationT
emergencyActivationE
formallyActivationF
homeActivationH
customizedActivationX

示例:

text
softName       = cyber-hub
contractNumber = HT202606150001
hardNumber     = DISK-SN-001
sysUUID        = 550E8400-E29B-41D4-A716-446655440000
activeType     = formallyActivation

拼接明文:

text
cyber-hubHT202606150001DISK-SN-001550E8400-E29B-41D4-A716-446655440000

SHA-256 结果:

text
77D7C571E51E28566884D210713C7BBA1AF0408F94388136E6C29CE49ACE5F97

取前 8 个字节转大写 HEX,即 77D7C571E51E2856,并将首字母替换为 F

text
F7D7C571E51E2856

展示时可格式化为:

text
F7D7-C571-E51E-2856

5.2 客户端离线校验步骤

客户端启动或用户输入激活码后,按以下方式本地校验:

  1. 读取本机 softNamecontractNumberhardNumbersysUUID
  2. 将输入的 activeCode 去掉横杠和空格,并转成大写。
  3. activeCode 第 1 位作为激活类型首字母。
  4. 按上面的 SHA-256 规则重新生成本机 expectedActiveCode
  5. 比较 expectedActiveCode == activeCode

注意:

  • activeCode 本身只绑定 softNamecontractNumberhardNumbersysUUID 和激活类型首字母,不包含有效期。
  • 有效期、设备数量、使用场景、备注等授权信息需要和 activeCode 一起保存,例如保存在本地授权文件中。
  • 设备标识必须和生成激活码时完全一致。大小写、空格、序列号来源变化都可能导致 SHA-256 结果不同。
  • 纯离线校验不依赖 GET /sansi/register/api/v1/active/code/validate

6. 常见错误

HTTP 状态codemessage说明
400400具体错误信息参数错误、解密失败或业务校验失败
418418Unix 时间戳二维码过期,需要重新生成
420420合同号不存在,请输入完整合同号合同号校验失败

方式二:手动下发激活码

手动下发适用于客户端无法方便扫码,或由管理端先创建授权码,再交给客户端完成绑定的场景。

这条链路分两段:

  1. 管理端创建 intermediateCode
  2. 第三方客户端提交 intermediateCode 和本机设备信息,换取最终 activeCode

1. 管理端创建中间码

http
POST /sansi/register/api/v1/active/code/create

权限要求:

  • 请求头 X-User-Roles 必须包含 licence:admin

请求示例:

bash
curl -X POST "{BASE_URL}/sansi/register/api/v1/active/code/create" \
  -H "Content-Type: application/json" \
  -H "X-User-Roles: licence:admin" \
  -d '{
    "softName": "cyber-hub",
    "contractNumber": "HT202606150001",
    "user": "张三",
    "usageScenarios": ["traffic"],
    "remark": "手动下发",
    "activeType": "formallyActivation",
    "validityPeriod": 4917254399
  }'

请求字段:

字段类型说明是否必填
softNamestring软件名称
contractNumberstring合同号。试用/应急可为空按激活类型
userstring申请/操作用户建议填写
usageScenariosstring[]使用场景建议填写
remarkstring备注建议填写
activeTypestring激活类型
validityPeriodnumber激活有效期,Unix 秒级时间戳

成功响应:

json
{
  "intermediateCode": "F1A2B3C4",
  "expirationTime": 1781520600
}

intermediateCode 不是最终激活码,它是 30 分钟有效的一次性中间码。客户端兑换成功后,服务端会删除该中间码并写入最终激活记录。

2. 第三方客户端兑换最终激活码

第三方客户端收到 intermediateCode 后,提交本机设备信息进行绑定。

http
POST /sansi/register/api/v1/active/code/validate

请求示例:

bash
curl -X POST "{BASE_URL}/sansi/register/api/v1/active/code/validate" \
  -H "Content-Type: application/json" \
  -d '{
    "intermediateCode": "F1A2B3C4",
    "sysUUID": "550E8400-E29B-41D4-A716-446655440000",
    "computerName": "DESKTOP-001",
    "hardNumber": "DISK-SN-001"
  }'

请求字段:

字段类型说明是否必填
intermediateCodestring管理端创建的中间码
sysUUIDstring系统 UUIDhardNumber 不能同时为空
computerNamestring设备名称建议填写
hardNumberstring硬盘/硬件序列号sysUUID 不能同时为空

成功响应:

json
{
  "activeCode": "F123456789ABCDEF",
  "validityPeriod": 4917254399
}

客户端应保存:

  • activeCode
  • validityPeriod
  • 当前设备标识快照,例如 sysUUIDhardNumber

3. 校验最终激活码

如果客户端需要在线校验最终激活码,可调用:

http
GET /sansi/register/api/v1/active/code/validate?activeCode={activeCode}

请求示例:

bash
curl "{BASE_URL}/sansi/register/api/v1/active/code/validate?activeCode=F123456789ABCDEF"

成功响应:

json
{
  "activeCode": "F123456789ABCDEF",
  "validityPeriod": 4917254399
}

失败响应示例:

json
{
  "code": "400",
  "message": "激活码不存在",
  "status": false
}

服务端会自动去掉传入激活码中的横杠并转成大写,因此 F123-4567-89AB-CDEFF123456789ABCDEF 都可以传。

4. 手动下发常见错误

场景message处理方式
无管理员权限创建中间码用户权限不足,请联系管理员检查 X-User-Roles
合同号为空合同号不能为空正式/家庭/定制激活必须传合同号
合同号无效合同号不存在或无效检查合同号或合同服务
试用期超过 30 天试用期不能大于30天缩短 validityPeriod
应急期超过 7 天应急期不能大于7天缩短 validityPeriod
中间码重复创建同一个合同号上一个二维码还未使用等 30 分钟过期或删除旧中间码
中间码为空二维码不能为空检查客户端输入
中间码已用/不存在二维码不存在或已被使用重新创建中间码
设备标识缺失硬件序列号和系统UUID不能同时为空至少上传一个稳定设备标识
中间码过期二维码已过期重新创建中间码

推荐客户端处理策略

扫码激活

  • 生成二维码时使用当前时间戳,避免旧二维码被重复使用。
  • 二维码展示后立即开始轮询 /sansi/register/api/v1/active/code/status
  • 轮询到 actived 后保存 activeCode,停止轮询。
  • 超时、过期或解密失败时重新生成二维码。

手动下发

  • 客户端输入或扫描 intermediateCode
  • 调用 POST /sansi/register/api/v1/active/code/validate 兑换最终 activeCode
  • 兑换成功后不要继续使用 intermediateCode,它已经失效。
  • 本地保存最终 activeCode 和有效期,启动时先做本地有效期判断,需要强校验时再请求服务端。

接入检查清单

  • 已拿到注册器服务地址 {BASE_URL}
  • 扫码激活需拿到服务端私钥配套公钥,并确认 RSA PKCS#1 v1.5 加密结果可被服务端解密。
  • softName 与服务端枚举一致。
  • 正式/家庭/定制激活传入有效合同号。
  • 客户端设备标识稳定,至少 sysUUIDhardNumber 不为空。
  • 客户端正确处理 unknownactivingactived 三种状态。
  • 客户端区分 intermediateCode 和最终 activeCode,别把中间码当最终授权凭据。