tencent cloud

云市场

产品动态
产品简介
产品概述
应用看板介绍
购买指南
用户指南
新手指引
应用商店应用使用指南
应用看板使用指南
服务商指南
上架流程概述
云市场入驻流程
制作和管理安装包
商品管理
订单管理
用户应用实例
常见问题
用户相关问题
Marketplace 政策
隐私协议
数据处理和安全协议
Tencent Cloud Marketplace User Agreement
Tencent Cloud Marketplace Products Cooperation Agreement
Tencent Cloud Marketplace End User License Agreement(Standard Version)
Marketplace Product Cancellation and Refund Rules
联系我们
词汇表

对接 License

PDF
聚焦模式
字号
最后更新时间: 2026-01-30 17:02:53
对于通过云市场安装的软件,云市场会为软件颁发 License,软件进程内,可以读取和校验 License。

License 有什么作用?

License 是对软件终端用户的一个许可,授权内容包括:
使用期限:如果软件过期,软件进程可以拒绝服务或提供降级服务。
规格:软件可以基于授权的规格运行,如限制并发数、用户数等。

对接云市场 License 有什么优势?

顺畅:下单即自动颁发 License,无需等待。
灵活:支持续期、升降配等灵活需求。
可信:腾讯云官方颁发,基于角色绑定的实例关联 License,难以伪造和复制。

License 交易与发货




服务商需要为应用包对应的商品定义规格,定义的规格会在用户选购时确定下来,作为 License 的规格。
License 按照客户购买的软件规格颁发,颁发的 License 包含下列信息:
关联的软件包
关联的软件实例
授权的规格信息
授权模式(永久/订阅)
授权状态(有效/过期/失效)
颁发时间、激活时间、过期时间、失效时间。

License 校验

服务商的应用镜像,可以通过云 API 校验 License。流程如下。



为了校验软件的 License,核心步骤是:
获取安装参数 cloudapp_cam_role,使用 CAM 角色申请临时密钥,调用云 API。详见文档调用云 API
调用 cloudapp.VerifyLicense 接口,获取当前进程关联的 License 信息。
软件进程按需校验 License 的状态,或者根据规格做功能控制。
License 信息示例如下:
{
"Response": {
"RequestId": "9026a0fe-992e-4015-a5d5-95a322333853",
"License": {
"ActivationDate": "2024-12-10T09:46:58+08:00",
"AuthorizedCloudappId": "cloudapp-2gwvcfb1",
"AuthorizedCloudappRoleId": "0",
"AuthorizedSpecification": [
{
"ParamKey": "version",
"ParamKeyName": "版本",
"ParamValue": "basic",
"ParamValueName": "基础版"
},
{
"ParamKey": "scale",
"ParamKeyName": "规格",
"ParamValue": "single",
"ParamValueName": "单线程"
}
],
"AuthorizedUserUin": "100000888888",
"BillingMode": 1,
"ExpirationDate": "2025-01-10T09:46:58+08:00",
"IssueDate": "2024-12-10T09:46:46+08:00",
"LicenseId": "100000888888:pkg-la39nlb7:cloudapp-2gwvcfb1:6006",
"LicenseMode": "Subscription",
"LicenseStatus": "Active",
"LicenseType": "Standard",
"LifeSpan": 1,
"LifeSpanUnit": "M",
"ProviderId": 100000099,
"SoftwarePackageId": "pkg-la39nlb7"
},
"Signature": "T2mbjUr8Q8I0nekShP7WJs7MvUq8Ovv0lrWkn5PEIPVlFYWpaRJOWBviQstCUaotFE89QQdbDEDaP9XVXtPfFt47Zp/BN+qaajTNdUN/HL8IAsC15OsDjGtXgdezPcRwDNx0SfaWBmBeJuB2xgFxvF7/tKcWKEltAf+tE4/SPMcIFDQPOqQD1gpdC0HM3nSL92uP+yN6jCRGbTSZXN8L5RAC64f9XfSuFIIhDGQpKbXDalmrxxUHMRG6Qcs5TtQw7vdQvpwRY1NeRWC49g97ZZaboveK0skG1ym3wfk0Z8slkEzlVWbLmYe4VjJpdMpQv29XPrCUHc3tPJ4beb9FiJ1N4kzUflmr6bo75zRm4XSs8NIjN/jONPArt+6dTua9MITNtqP4nqKtwS6lw3GBZeTljb+wz5FlV1tKYoYMaBJSZyAu27mT7nwt1HJ34XLOw/yvgeGmqNDe6kAqxJ9tg/kEmw6TZP8UiKeWITsRCYXkdsyfeCnacVyiWFcsonOJoG4+wujnjroXEMgymw/CiddQD13PH4sLscpqgxlvXntVf2ljYP0ryFnO/bcq5I3L6FIVtOhpb6yjdWAl1A5FO9KG7jNT9aD11f38G7t1A4PIi2OdBQpcOLfYO3fqoimbAqiWVy8ww9N63ejnN+7ioJ/km2zBrAejFRv7Qes+p6c=",
"Timestamp": "2025-01-09T10:28:57+08:00"
}
}
这里需要关注的字段有:
LicenseStatus:授权状态,如果为 Active 则表示授权有效,为 Deactive 表示授权已失效。服务商应使用 LicenseStatus 字段来判断授权是否有效。
AuthorizedSpecification:授权的规格,软件进程可以根据该规格控制软件行为。
LicenseMode:授权模式,订阅还是永久买断。
Signature:对 Response.License 结构的签名,对 License 安全要求较高的,可进行签名验证(具体做法见License 签名验证方法),以确保返回的License 信息未被中间人拦截篡改过。
校验 License 的时机,可以由软件进程自主决定,常见的方式有:
定时校验,例如每天或者每小时校验一次。
按需校验,例如在需要校验 License 的产品功能调用时校验。

License 开发调试

通过在应用包的 package.yaml中按如下格式添加specs规格配置,即可在安装应用开发版本时选择规格信息及授权模式创建 License。
# <package.yaml>
# 声明应用包规格
specs:
- key: version
name: 版本
enum:
- value: standard
name: 标准版
- value: advanced
name: 高级版
- value: enterprise
name: 企业版
- key: cluster_mode
name: 集群模式
enum:
- value: single
name: 单集群
- value: double
name: 双集群
- value: triple
name: 三集群

License 签名验证方法

签名采用 RSA-PSS-SHA256算法实现,请使用以下公钥进行签名验证:
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5EQJv0v0f1hrB7NIGwXi
hFmw+ugrJi7gnmOqaiYp7oFrzf4RSJ3DPr4K01F+CrjTdCPghDLq4fsKVxxHAjNj
nbstbVlHZEVOfzQ4umeocJpxWFuyKyGwHv+obnEZ/4689fxVpTbG3IbUTGn1TRJs
9s3xM8nFd6LLAoh1Hhrdf2D4mLRToLvtRVat1l8fH3gsM+RoG4L4h+3hghn4bpyA
na2MBFDzvmBeVGUVzqRjSvUaexd+Bo1wTsllAdqjP6MTlAAWGmIAMStBSRS+YpRQ
xjhE9Rdb9zTE54q3Ui7UJg5BMe+R3kVrBINbnT6Va8/Lzjg4+THdpMTLr6fY6ObF
7r+i/924XgxqQOFvGaFJSyjXTORnK42T5YRr5TSqxr9CzhybPcdRvws2GdAq9f55
8whj1DYcgg0X8kR06Iu+/9Mk/CqssdrZ8LYDwSkDI8S/RwpdNQfifUa8wyY0R2xN
nY+bnkrjvGPz7Rokr0Ki9/orT9i4yQWA1mMCDi2vcP+oXqrEs7XAyH85gDSzuTp+
dXbTYPZpIAK6Kejwssw1IE1lGNP4PNQZk9EXU7+vB1csz4GUao7Mr7F5VbrGKvTs
aGxbIc6b0MDWMEFA7L/CWC9UtReWCk1MYwJzy105bWU/VBpYJPmyZTFRQaY2MEH4
fnsK2+jtZ1IYIQw/YsHU6CcCAwEAAQ==
-----END PUBLIC KEY-----
签名验证涉及以下两个字段:
Response.Signature:即签名,是对 Response.License JSON 结构体先进行压缩去除换行后进行 RSA-PSS-SHA256签名。
Response.License:即被签名的 JSON 结构体,验签时,需要确保该结构体是压缩去除换行后的 JSON 数据。
签名验证的示例代码,当前提供 Golang 版本如下。其他语言的示例待后续提供。
Go Lang
// VerifySignWithSha256RSA 使用RSA公钥对签名进行验证
// @Param publicKey PEM格式的PKIX RSA公钥
// @Param signature base64编码过的签名
// @Param message 待签名验证的内容
// @Return error 签名验证通过则返回nil,否则返回错误
func VerifySignWithSha256RSA(publicKey string, signature string, message []byte) error {
// 解析公钥
publicKeyBlock, _ := pem.Decode([]byte(publicKey)) // 读取pem格式公钥
// 解码PKIX格式RSA公钥
rsaPublicKeyPKIX, err := x509.ParsePKIXPublicKey(publicKeyBlock.Bytes)
if err != nil {
return err
}
rsaPublicKey, ok := rsaPublicKeyPKIX.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("public key is not rsa public key")
}
// 计算消息的SHA-256哈希
hash := sha256.New()
hash.Write(message)
hashedMessage := hash.Sum(nil)
// 获取signature
signatureFromBase64, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return err
}
// 使用公钥验证签名
return rsa.VerifyPSS(rsaPublicKey, crypto.SHA256, hashedMessage, signatureFromBase64, nil)
}

// 使用示例
const publicKeyPkix = "-----BEGIN PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvIPknZUDTxI8ep3wDFsN\\nC2vF1sTUfF8f6pnjSduwtIc5yUYV/1hONRe4DwWRiXQPrDRTjlDridNRfglmktoe\\ngUewNKfluGlxuTrUV35BBSGXdFTWJNg8/9j5zpsQS69mjwlh0wO8RxL0N9JatyHD\\nHZBg9psp4RGj57wxEdyANv5IUvPQ0MUwuZ64UATl/0VI5eRM1FCJI5rE9kC+eJyH\\n+c/63SNqBoSjG2kmXUb4nN8DPoDs90oA0wS2Yq1kr83kPAaFpcCIvnNKbXCK/hbY\\nJymt92Tcd8/viCxcEd88hacfzavWkyiLPl0W7Golnn2N9ZIyPwUb3a52yC4HiS5h\\n4XQSogiFluMQ+OIm4YwoaGgTILoU/Ip03LX7AILNI/Fcx9oGsLv2v4Lj01bStdJj\\n7EaCeitIw3SVyjlNAaoBTLzee0opBgVHGf8AnCzzf6qe7a0ics+pJbJi8+SGN6CF\\nOBZxeQxqu7ZE9c6y05ZYQEh0e5V/5KlIZMG0FtmyMY1Q1l2CHjVJz4xzG8t8asqZ\\njg7uVpsnQhOxrbz68cAnw9X/9297K62VnECa8z9/3kSfY0SWd+lmc5HpFRzRJPt8\\nDLjFR/r/turTJ+HnvUe1aJzD2oa8D8Y09T6gQWmAlmqLOnt7aSPm/zN3rVt/6CPY\\n6EKSQMgJ7oOgKg4FybkNELcCAwEAAQ==\\n-----END PUBLIC KEY-----"

func TestVerifySignWithSha256RSA(t *testing.T) {
type args struct {
publicKey string
signature string
message []byte
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "验证签名",
wantErr: false,
args: args{
publicKey: publicKeyPkix,
signature: "T2mbjUr8Q8I0nekShP7WJs7MvUq8Ovv0lrWkn5PEIPVlFYWpaRJOWBviQstCUaotFE89QQdbDEDaP9XVXtPfFt47Zp/BN+qaajTNdUN/HL8IAsC15OsDjGtXgdezPcRwDNx0SfaWBmBeJuB2xgFxvF7/tKcWKEltAf+tE4/SPMcIFDQPOqQD1gpdC0HM3nSL92uP+yN6jCRGbTSZXN8L5RAC64f9XfSuFIIhDGQpKbXDalmrxxUHMRG6Qcs5TtQw7vdQvpwRY1NeRWC49g97ZZaboveK0skG1ym3wfk0Z8slkEzlVWbLmYe4VjJpdMpQv29XPrCUHc3tPJ4beb9FiJ1N4kzUflmr6bo75zRm4XSs8NIjN/jONPArt+6dTua9MITNtqP4nqKtwS6lw3GBZeTljb+wz5FlV1tKYoYMaBJSZyAu27mT7nwt1HJ34XLOw/yvgeGmqNDe6kAqxJ9tg/kEmw6TZP8UiKeWITsRCYXkdsyfeCnacVyiWFcsonOJoG4+wujnjroXEMgymw/CiddQD13PH4sLscpqgxlvXntVf2ljYP0ryFnO/bcq5I3L6FIVtOhpb6yjdWAl1A5FO9KG7jNT9aD11f38G7t1A4PIi2OdBQpcOLfYO3fqoimbAqiWVy8ww9N63ejnN+7ioJ/km2zBrAejFRv7Qes+p6c=",
message: []byte("{\\"ActivationDate\\":\\"2024-12-10T09:46:58+08:00\\",\\"AuthorizedCloudappId\\":\\"cloudapp-2gwvcfb1\\",\\"AuthorizedCloudappRoleId\\":\\"0\\",\\"AuthorizedSpecification\\":[{\\"ParamKey\\":\\"version\\",\\"ParamKeyName\\":\\"版本\\",\\"ParamValue\\":\\"basic\\",\\"ParamValueName\\":\\"基础版\\"},{\\"ParamKey\\":\\"scale\\",\\"ParamKeyName\\":\\"规格\\",\\"ParamValue\\":\\"single\\",\\"ParamValueName\\":\\"单线程\\"}],\\"AuthorizedUserUin\\":\\"100000888888\\",\\"BillingMode\\":1,\\"ExpirationDate\\":\\"2025-01-10T09:46:58+08:00\\",\\"IssueDate\\":\\"2024-12-10T09:46:46+08:00\\",\\"LicenseId\\":\\"100000888888:pkg-la39nlb7:cloudapp-2gwvcfb1:6006\\",\\"LicenseMode\\":\\"Subscription\\",\\"LicenseStatus\\":\\"Active\\",\\"LicenseType\\":\\"Standard\\",\\"LifeSpan\\":1,\\"LifeSpanUnit\\":\\"M\\",\\"ProviderId\\":100000099,\\"SoftwarePackageId\\":\\"pkg-la39nlb7\\"}"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := VerifySignWithSha256RSA(tt.args.publicKey, tt.args.signature, tt.args.message); (err != nil) != tt.wantErr {
t.Errorf("VerifySignWithSha256RSA() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

关于 License 的常见问题

接入 License 是必须的吗?是否可以自己生成和校验 License?

是必须的,对接之后用户的软件计费可以跟授权做好线上联动,例如:
免费试用转付费,只是 License 更新,用户无需更新实例,可以保留数据。
续费场景,会自动刷新 License,无需再找服务商线下更新。
升降配场景,也会自动刷新 License,实例无需更换。
如果软件本身是免费软件,无需校验软件授权,则可以无需关注 License 的接入。

服务商可以感知 License 的颁发吗?

暂时无法感知。

用于验证签名的公钥如何保存?

公钥用于验证 License 信息的签名,确保 VerifyLicense 中的 License 信息是由腾讯云发出且未经中间人篡改。供应商在校验 License 签名时,应该确保加载的公钥为 License 签名验证方法 中提供的公钥,否则可能导致 License 信息被中间人攻击和篡改。因此,不应该将公钥暴露在容易被修改的配置文件中或其他类似的独立配置中。如果需离线保存在软件客户端,至少应保障公钥配置在经过混淆/加固的客户端可执行文件中。

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈