当关注者与已授权小程序进行交互时,第三方平台将接收到相应的消息推送、事件推送。由于第三方平台一般帮助众多小程序进行业务运营,所以为了加强安全性,QQ服务器将对此过程进行 2 个措施:
接收已授权小程序消息和事件的 URL 中,增加 2 个参数(此前已有 2 个参数,为时间戳 timestamp,随机数 nonce),分别是 encrypt_type(加密类型,为 aes)和 msg_signature(消息体签名,用于验证消息体的正确性开放平台的消息加密解密技术方案基于 AES 加解密算法来实现,具体如下:
EncodingAESKey: 即消息加解密 Key,长度固定为 43 个字符,从 a-z,A-Z,0-9 共 62 个字符中选取。由开发者在创建小程序插件时填写,后也可申请修改。
AESKey: AESKey=Base64_Decode(EncodingAESKey + "="),EncodingAESKey 尾部填充一个字符的 "=", 用 Base64_Decode 生成 32 个字节的 AESKey;
AES 采用 CBC 模式,秘钥长度为 32 个字节(256 位),数据采用 PKCS#7 填充; PKCS#7:K 为秘钥字节数(采用 32),Buf 为待加密的内容,N 为其字节数。Buf 需要被填充为 K 的整数倍。在 Buf 的尾部填充(K - N%K)个字节,每个字节的内容 是(K - N%K)。
| 尾部填充 | 说明 | 示例 |
|---|---|---|
| 01 | if ( N%K==(K-1)) | |
| 0202 | if ( N%K==(K-2)) | |
| 030303 | if ( N%K==(K-3)) | |
| ... | ... | |
| KK....KK (K 个字节) | if ( N%K==0) |
具体详见: http://tools.ietf.org/html/rfc2315
BASE64 采用 MIME 格式,字符包括大小写字母各 26 个,加上 10 个数字,和加号 "+",斜杠 "/",一共 64 个字符,等号 "=" 用作后缀填充;
出于安全考虑,开放平台网站提供了修改 EncodingAESKey 的功能(在 EncodingAESKey 可能泄漏时进行修改,对应上第三方平台申请时填写的接收消息的加密 symmetric_key),所以建议开放平台账号保存当前的和上一次的 EncodingAESKey,若当前 EncodingAESKey 生成的 AESKey 解密失败,则尝试用上一次的 AESKey 的解密。回包时,用哪个 AESKey 解密成功,则用此 AESKey 加密对应的回包。
QQ团队提供了多种语言的示例代码(包括 PHP、Java、C++、Python、C#),请开发者尽量使用示例代码,仔细阅读技术文档、示例代码及其注释后,再进行编码调试。 示例下载
下面以普通文本消息为例,详细说明小程序平台对消息体加解密的方法和流程,其它普通消息和事件消息的加解密可以此类推。 # 消息体加密 ]≥;lp-现有消息为明文,格式如下:
<xml>
<ToUserName>
</ToUserName>
<FromUserName>
</FromUserName>
<CreateTime>
1348831860</CreateTime>
<MsgType>
</MsgType>
<Content>
</Content>
<MsgId>
1234567890123456</MsgId>
</xml>
加密后,消息格式如下:
<xml>
<ToUserName>
</ToUserName>
<Encrypt>
</Encrypt>
</xml>
其中,msg_encrypt = Base64_Encode( AES_Encrypt[ random(16B) + msg_len(4B) + msg + ] )。
加密的 buf 由 16 个字节的随机字符串、4 个字节的 msg_len(网络字节序)、msg 和 AESKey =Base64_Decode(EncodingAESKey + “=”),32 个字节。 # 消息体签名 为了验证消息体的合法性,开放平台新增消息体签名,开发者可用以验证消息体的真实性,并对验证通过的消息体进行解密。具体做法如下:在QQ服务器向小程序插件推送消息时,将会在其消息接收 URL(创建时填写)上增加参数:msg_signature msg_signature=sha1(sort(Token、timestamp、nonce, msg_encrypt))
| 参数 | 描述 |
|---|---|
| Token | QQ小程序第三方平台上,服务方设置的接收消息的校验 token |
| timestamp | URL 上原有参数,时间戳 |
| nonce | URL 上原有参数,随机数 |
| msg_encrypt | 前文描述密文消息体 |
# 消息体验证和解密 开发者先验证消息体签名的正确性,验证通过后,再对消息体进行解密。 # 验证方式:
1. 开发者计算签名,dev_msg_signature=sha1(sort(Token、timestamp、nonce, msg_encrypt))
2. 比较dev_msg_signature和URL上带的msg_signature是否相等,相等则表示验证通过。
# 解密方式如下:
1. aes_msg=Base64_Decode(msg_encrypt)
2. rand_msg=AES_Decrypt(aes_msg)
3. 验证尾部
4. 去掉rand_msg头部的16个随机字节,4个字节的msg_len,和尾部的
# 四、例子:服务方代替授权方向用户回复消息
# 回复消息体的签名与加密
现有消息格式:
<xml>
<ToUserName>
</ToUserName>
<FromUserName>
</FromUserName>
<CreateTime>
12345678</CreateTime>
<MsgType>
</MsgType>
<Content>
</Content>
</xml>
加密后消息格式:
<xml>
<Encrypt>
</Encrypt>
<MsgSignature>
</MsgSignature>
<TimeStamp>
</TimeStamp>
<Nonce>
</Nonce>
</xml>
其中,msg_encrypt=Base64_Encode(AES_Encrypt [random(16B)+ msg_len(4B) + msg + ])
random(16B)为 16 字节的随机字符串;msg_len 为 msg 长度,占 4 个字节(网络字节序);
AESKey =Base64_Decode(EncodingAESKey + “=”),32 个字节 msg_signature=sha1(sort(Token、timestamp、nonce, msg_encrypt))timestamp、nonce 回填请求中的值即可。
# 常见错误举例 对开发者在进行消息加解密过程中可能会遇到的常见错误问题,整理原因如下: