签名和加解密
sidebar_position: 2
签名和加解密
> 本文档是对sdkVersion=47及以上的版本有效,服务端也同步做了低版本sdk兼容,设备端放心接入<br /> EncryptionTests.java<br /> EncryptionUtils.java<br />
一 总体规则 (签名和加解密)
2.1、所有请求带在请求头带如下参数 (签名和加解密)
> mac=A8:20:06:B7:C8:B9
cpu= 02c0008145f0462078a3840038350b53
api-version=47
host=api.yudao.iocoder.cn (设备端在请求时一般不用设置,web请求框架自动带入的)
2.2、加解密和验签整体情况说明 (签名和加解密)
> 对于设备端请求服务器的请求,除了心跳和获取时间戳的请求,其他请求都带签名,如果不带签名,服务端统一返回404错误码,包含http状态码也是404。
> 服务端返回时,如果返回头中的encryption=true表示返回体数据加密,设备端拿到密文之后,进行解密操作,记住只是data下的数据加密,msg和code正常。
二 盒子签名 (签名和加解密)
> 签名和验签,加密解密逻辑 1、设备发起业务请求前,先请求服务器拿到最新时钟。域名分发为/app-api/hnc2fng/gnbht53hdv2/dhgb35dg,主UOTA为心跳接口。<br /> 2、备心跳请求时返回时间戳逻辑。 请求/app-api/hnc2fng/gnbht53hdv2/dhgb35dg,而时间戳永远放在data.time字段中,为13位时间戳。<br /> 3、主UOTA心跳请求时返回时间戳逻辑。 盒子端的UOTA程序内部不管是否已经激活,都要先请求心跳接口,而时间戳永远放在data.time字段中,为13 位时间戳。<br /> 4、拿到时间戳之后,设备开始进行签名操作。签名工具类见【<一个文件链接>】,参数如下。<br />
/**
*
* MD5签名
* @param domain 发起请求时使用的域名。从设备端的host请求头获取,设备端在请求时一般不用设置,web请求框架自动带入的。 请求头示例:host=api.yudao.iocoder.cn
* @param mac 设备mac。请求头示例: mac=A8:20:06:B7:C8:B9
* @param cpu 设备cpu。请求头示例:cpu= 02c0008145f0462078a3840038350b53
* @param time 13位时间戳。 服务器有个获取时间戳的接口,通过那个接口获取后后续请求带上来。请求头示例:time=1750324247858
* @param salt 签名的盐值。建议每个uota版本都不一样,uota写入程序内部后进行混淆处理;服务端通过配置管理和版本对应关系。根据【sdkVersion】从服务端获配置获取
* @param sdkVersion sdk版本 ,机顶盒发起请求时手动设置,服务端从请求头获取。请求头示例: api-version=46
* @return 签名结果
*/
public static String sign(String domain,String mac,String cpu,Long time,String salt,int sdkVersion){
String sign = domain+salt+"8622Nbdf52"+mac+salt+"2d6fndw"+cpu+salt+"3hd73bx6g"+time+salt+"23jhY6DGVhbd";
return getMD5(sign);
}
5、签名结果放在请求头中。所以完整请求头示例如下:
mac=A8:20:06:B7:C8:B9
cpu= 02c0008145f0462078a3840038350b53
api-version=47
# 设 备端在请求时一般不用设置,web请求框架自动带入的
host=api.yudao.iocoder.cn
time=1750324247858
# 签名结果
sign=bc7318a3dce54a3e3aa30785eae53abf
三 服务端验签 (签名和加解密)
> 在服务端进行验签操作。
/**
* 验证签名
* @param domain 发起请求时使用的域名。从设备端的host请求头获取,设备端在请求时一般不用设置,web请求框架自动带入的。 请求头示例:host=api.yudao.iocoder.cn
* @param mac 设备mac。请求头示例: mac=A8:20:06:B7:C8:B9
* @param cpu 设备cpu。请求头示例:cpu= 02c0008145f0462078a3840038350b53
* @param time 13位时间戳。 服务器有个获取时间戳的接口,通过那个接口获取后后续请求带上来。请求头示例:time=1750324247858
* @param salt 签名的盐值。建议每个uota版本都不一样,uota写入程序内部后进行混淆处理;服务端通过配置管理和版本对应关系。根据【sdkVersion】从服务端获配置获取
* @param sdkVersion sdk版本 ,从请求头获取。请求头示例: api-version=46
* @param receivedSign 和字段带上来的签名结果
* @return 验证结果(true=有效,false=无效)
*/
public static boolean verify(String domain, String mac, String cpu, Long time, String salt,int sdkVersion, String receivedSign)
四 数据加解密(当前仅用于域名分发) (签名和加解密)
1、 服务端返回给设备的数据都在body里面,且数据的data经过加密处理。
/**
* 加密方法
* @param plainText 明文
* @param secretKey 密钥。 建议每个uota版本都不一样,uota写入程序内部后进行混淆处理;服务端通过配置管理和版本对应关系。根据【sdkVersion】从服务端获配置获取
* @return 密文
* @throws Exception
*/
public static String encrypt(String plainText, String secretKey)
2、 设备端拿到密文之后,进行解密操作,记住只是data下的数据加密,msg和code正常。
/**
* 解密方法
* @param encryptedText 密文
* @param secretKey 密钥。建议每个uota版本都不一样,uota写入程序内部后进行混淆处理;服务端通过配置管理和版本对应关系。根据【sdkVersion】从服务端获配置获取
* @return 明文
* @throws Exception
*/
// 解密方法
public static String decrypt(String encryptedText, String secretKey)
五 测试环境秘钥信息 (签名和加解密)
--- ###################### 机顶盒请求时验签、加密相关 ######################
app:
api:
security:
# 不需要验签或者加密的的域名
no-need-domain:
# - 127.0.0.1
- localhost
- akrdinfo.cn
- 47.251.5.25
# 需要验证签名在处理的路径
sign-urls:
- /app-api/hnc2fng/ghbn1hebdsh/**
# 需要加密data后返回的路径
encryption-urls:
- /app-api/hnc2fng/ghbn1hebdsh/**
# sdk信息 version-版本号;singKey-sdk签名和验签密钥,其实就是md5盐值;encryptionKey-sdk用于数据加密解密;singTimeDiff-签名时间差,单位毫秒,当前时间和签名带上来的时间超过该值则直接验签失败
sdks:
- version: 47
singKey: jdh2dh7hdbhu3hbfcyHvdhf87NFH67b47
encryptionKey: 1234567887654347
singTimeDiff: 600000
- version: 48
singKey: jdh2dh7hdbhu3hbfcyHvdhf87NFH67b48
encryptionKey: 1234567887654348
singTimeDiff: 600000
六、apifox模拟请求 (签名和加解密)
6.1 获取当前时间 (签名和加解密)
调用【获取当前时间】接口拿到服务器时间,结果如下,记住这个时间,在【生成签名接口,生产环境需要屏蔽】和【获取域名和APK】中都要使用,crul如下
curl --location --request GET 'http://imptor.top:8742/app-api/hnc2fng/gnbht53hdv2/dhgb35dg' \
--header 'api-version: 47' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Accept: */*' \
--header 'Host: imptor.top:8742' \
--header 'Connection: keep-alive'
请求结果如下:
{
"code": 200,
"data": {
"time": 1753181666064
},
"msg": ""
}
6.2 模拟机顶盒生成签名数据(机顶不要调用该接口) (签名和加解密)
调用【生成签名接口,生产环境需要屏蔽】接口用于模拟代码执行签名的逻辑,返回的数据为签名结果,请求curl请求如下,请求头无所谓,主要是json请求体data-raw<br /> time是【获取当前时间】结果返回的data.time
curl --location --request POST 'http://imptor.top:8742/app-api/hnc2fng/ghbn1hebdsh2/sign' \
--header 'api-version: 47' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: imptor.top:8742' \
--header 'Connection: keep-alive' \
--data-raw '{
"domain": "imptor.top",
"mac": "9C:00:D3:58:B3:DF",
"cpu": "82422823dde3caa6",
"time": 1753181666064,
"sdkVersion": 47
}'
返回结果如下,data是签名的结果
{
"code": 200,
"data": "2111360344febfc8a6179b19c9450871",
"msg": ""
}
6.3 获取域名分发信息 (签名和加解密)
【获取域名和APK】主要用于机顶盒正式请求的接口,这里用apifox模拟,请求的curl如下<br /> 请求头time:【获取当前时间】结果返回的data.time<br /> 请求头mac:保持【生成签名接口,生产环境需要屏蔽】一致<br /> 请求头cpu:保持【生成签名接口,生产环境需要屏蔽】一致<br /> 请求头sign:【生成签名接口,生产环境需要屏蔽】中返回的data签名结果<br />
curl --location --request POST 'http://imptor.top:8742/app-api/hnc2fng/ghbn1hebdsh/yh65hbfvg3jfdbs' \
--header 'time: 1753181666064' \
--header 'api-version: 47' \
--header 'mac: 9C:00:D3:58:B3:DF' \
--header 'cpu: 82422823dde3caa6' \
--header 'sign: 2111360344febfc8a6179b19c9450871' \
--header 'deviceToken;' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: imptor.top:8742' \
--header 'Connection: keep-alive' \
--data-raw '{
"platform": "Allwinner"
}'
主要是请求头,其中sign是上一步【生成签名接口,生产环境需要屏蔽】接口的data数据
mac=9C:00:D3:58:B3:DF
cpu= 82422823dde3caa6
api-version=47
# 设备端在请求时一般不用设置,web请求框架自动带入的
host=imptor.top:8742
time=1753181666064
# 签名结果
sign=2111360344febfc8a6179b19c9450871
请求返回结果如下,data是返回的域名和apk信息,但是是加密的数据,需要执行6.4解密
{
"code": 200,
"data": "DC15hFmkezrskGUxijhd47DMxzS8IXKtuXAwJ6YC0C6mrBBxHJi0Cw6VVJxkIDxSeHU/nqd6yftwUFfEdy505HyNMXrZfKio7c/GQzQGJlM9/i7B+n1vOgc8wMfUsttdOI6U2d4v06VKKuPQd2nLIC7Eq3MQCSf+cCJdT8kKPN2Jd0A86v1PiwCI4cCmYhV5QHdqmvAw2+Wjqn51qNytOFQA4CBnD3qprDDEbm/qNHcvW8TBSIfg5iK4PfZ9MFog32QyNjN9vVGjugYZpPkf/nncnYoM+ZAIBr3O9Qc8PV9C3XCMjkXRlBGDNUkQqp3lSpdwoQY2wHorYpY0krS04K0TmcbQM8YS7Xz5HiEdcZLAjBNZcvxhsPG1N2rSkQcShFHsYBfYwWVCSWVa3hwlIARwgul7PK1rM/IuU5eCpbPxo635mCyUZWHrk3wARt6MQUP2hyUy9ddfAnCVkU0P5Q==",
"msg": ""
}