1,需求分析
在实际的软件部署流程中,为了防止一套售出的软件被随意在多个平台进行部署,那么就需要对软件系统进行授权,只有在获取到授权后才能使用,常用的方法无非两种,
其一是软件认证,
其二是硬件绑定。
软件认证,顾名思义就是在软件层面的一种认证手段,常用的方法就是注册账号设置密码。
只要账号密码正确,在任何设备上都可使用。
硬件绑定,就是将软件和硬件设备进行捆绑,也就是说一旦完成捆绑后,该软件就只能在该硬件设备上使用了。
两种授权方法各有优劣,因应用场景的不同而选择不同的方案,在此就不多做讨论了,
本文主要探讨的是硬件绑定的方法,以在PC机的软件授权为背景进行授权码的设计。
2,方案选型
方案一:硬件绑定。考虑到虚拟机部署,无法确定具体的硬件信息,但是通过挂在宿主机的/sys/class/net/eth0/address文件,则可以获取宿主机的mac地址。
方案二:结合方案一,同时通过限制部署节点的个数、cpu mem使用情况来防止大规模的部署,在可控范围内起到一定的限制作用。
授权机制的过程和原理:
- 生成密钥对,包含私钥和公钥,私钥签名,公钥验签。
- 授权者保留私钥,使用私钥对授权信息诸如使用截止日期、mac地址、限定的资源和appID等内容生成 license签名证书。
- 公钥给使用者,放在代码中使用,用于验证 license 签名证书是否符合使用条件,比如部署节点个数是否在限定范围内。
- 各个部署节点分别定时向鉴权服务注册自己的信息,包括设备id,cpu mem等资源情况。
- 如果设备下线或者不适用,则请求反注册接口,释放自己的资源。如果节点长时间(一天)没有上报信息则自动过期。
- 授权服务统计上报节点的各资源信息并统计,如果已经超过了license规定的资源限定值则返回授权失败。
3,技术实现
编译命令行工具
编译工具需要安装Go1.19和make
命令,设置Go模块代理
# 切换到本项目根目录,执行以下命令编译工具
make build_tool
make build_api # 编译生成 api 服务
# 默认命令行工具将会输出build目录,切换到build目录,执行以下命令
cd build
autool #显示全部命令
autool version #显示工具版本号
autool help version #显示version命令帮助信息
# 如果是在linux环境,可能需要执行以下命令
chmod +x autool
./autool #显示全部命令
创建授权证书&编译程序库
创建授权证书基本步骤:
- 创建密钥文件,或者使用已有密钥文件
- 修改命令行工具配置文件
tool.toml
,设置授权信息[license] User = '1'#授权用户 ExpiredAt = '20240101' # 授权到期日期,格式:YYYYMMDD EngineVersion = 'v1.0.0' # 引擎版本 FingerPrint = 'xxx' # 指纹 MacAddress = '00:ff:42:c3:58:9a' # 网卡Mac地址,多个逗号分隔 PhysicalID = '178BFBFF00A50F00' # cpu序列号 # 资源限定 Cpu = 1024 # cpu核数 Mem = 1024 # 内存k Node = 10 # 节点个数 CompanyName = "test" # 企业名称 ContractID = "fdfdj12345jjfjd" # 合同id LesseeID = 123 # 租户id AppID = [1,2,3,4] # 授权的app id,多个以逗号隔开
- 创建&加密授权证书
- 编译程序库,嵌入加密后证书
# 项目包含1份示例配置文件,见`conf/tool.toml`
# 以下假设autool已经存在于bulid目录下
# 打开命令行窗口
cd build
mkdir -o conf
cp ../conf/tool.toml ./conf/
# 创建密钥文件,文件默认输出到: conf/key.pem, conf/key_pub.pem
autool newkey
# 修改tool.toml,根据实际情况设置授权证书信息,见"[license]"
# 如果设置mac地址,则调用接口时会校验服务器mac地址是否已授权
# 可执行以下命令查看本机mac地址
autool mac
# 创建加密授权证书,文件默认输出到:conf/license
autool newlic
# 编译程序库,加密证书和对应的公钥文件会嵌入程序库里
# 如果需要使用已有证书和公钥,可修改tool.toml->PubKeyFile/LicenseFile
# windows环境,库文件输出到:parser.dll
# linux环境,库文件输出到:libparser.so
# 注:Go目前不支持通过设置GOOS来编译不同环境下的动态库文件
autool newlib
# 启动api服务
chmod +x api
./api
api 接口
aes加密方案
所有请求数据和返回数据均通过aes加密,32位秘钥是:Y8uCrLL8SavXyiUzpnU+Lmn4mODprYLo
所以,发送和接收到的body,都是经过加密后的数据,比如:
实际请求:
{
"device_id": "3",
"cpu": 4,
"mem": 1024,
"engine_version": "v1.0.0"
}
加密后的请求:
��2.�&%Y���g�|�Q��-V�ݧa��%yIO;��6��ֵYb1}���5���ޭ��Pޕ<Q8�ڍ���j��E�ܯ��缰BsbV`�0�F0w
同理,接收到的body也是加密的
实际返回:
{
"code": 0,
"err": "",
"data": ""
}
加密后的返回:
(w�`�'�gDk�
: 4$C������n
]�����[rr���fl�ff��sk`J
- 注册
POST localhost:8080/api/v1/register 请求body: { "device_id": "3", "cpu": 4, "mem": 1024, "engine_version": "v1.0.0" } 返回: { "code": 0, "err": "", "data": "" }
- 反注册
POST localhost:8080/api/v1/unregister 请求body: { "device_id": "2" } 返回: { "code": 0, "err": "", "data": "" }
- 鉴权
POST localhost:8080/api/v1/auth 请求: { "app_id": ["1","2","3"] } 返回成功: { "code": 0, "err": "", "data": { "user": "1", "engine_version": "v1.0.0", "expired_at": "20240101", "finger_print": "xxx", "mac_address": "", "physical_id": "", "cpu": 1024, "mem": 1024, "node": 10, "app_id": [ "1", "2", "3" ] } } 授权失败返回: { "code": 121, "err": "unauthorized failed", "data": null }
web前端接口
参考下图:
-
获取服务信息
POST localhost:8080/api/v1/service_info 请求: { "lease_id": "123456", "app_id": ["1","2", "oppmApp"], "cpu": 1024, "mem": 1024, "node": 100, "engine_version": "v1.0.0", "expired_at": "20240101" } 返回成功: { "code": 0, "err": "", "data": "HWqwL0TBDyULAiZPv0u5zRst6OHfnv8AC7gDoxeXA97ceSh2OeG80hX0ZaxoAHoR7I2M/WlsojCq2KK/ao4UyRm711YVM+jS+X+b67dBYE5eNXHrMpzWpPryd3fh09f0w7ghnp2ZJ++THIBTio/hJp3Nk2gwlLTnM4jLkb3ZD8Yb/+kOJrirDN4QKzGguejK+95aiCPbH7VzW9sVfJQsijBWFyzDto2Qp2Rr7o0uESFMC4HWE1yrNQzAoeQUy7Qr4yBsF04UGZWPrjGwGGChIms6zosFOKAgvzKMwUX94TfCDfP5RsdG4EVIe7cxkk86j175hlLn162kglZGRI+PsA==" } 授权失败返回: { "code": 121, "err": "", "data": null }
-
提交授权许可
POST localhost:8080/api/v1/submit 请求: { "license": "xxxxxxx", } 返回成功: { "code": 0, "err": "", "data": null } 授权失败返回: { "code": 121, "err": "submit err", "data": null }
计划
- 3月27-3月29
完成整个授权码功能的后端接口开发并自测 - 3月30-3月31
完成联调