Appearance
概述
- 用户填写了 消息接收url ,且有消息通知时会向 消息接收url 以 POST 形式推送消息
重试机制
若由于网络不通等原因首次推送不成功,会自动重试1次
推送参数
参数 | 类型 | 长度 | 必填 | 说明 |
---|---|---|---|---|
encrypt | string | 是 | 加密字符串 | |
timestamp | int | 10 | 是 | 时间戳 1661326296 |
nonce | string | 8 | 是 | 随机字符串 |
msg_signature | string | 40 | 是 | 消息体签名 |
推送参数示例
json
{
"encrypt": "Ise0+sMZbZ+NQsCEGO0RVrSrk2EEZ6seBGk6AQpP37nQ5AUGJ0JPWkUwFnFU5T7fDn1a1w0oBvaJ7nVXe4W5OaaS+cVpJjML6YONVmFDn9jlVmYJ0Qi9WuRiYSeRg/M04yPnb9om7cxxVDL2B8QmgvE2MEnTF8t91nJuR6QnXJk=",
"msg_signature": "d1a37bd28a95fe01d55a7f857d18c5c65d491049",
"timestamp": 1661326296,
"nonce": "lDtDxRqa"
}
解密
解密技术方案简介
- token:32位消息校验Token,可在后台获取
- encoding_aes_key:43位消息加解密Key,可在后台获取
- 采用 AES 加解密算法,AES 采用 CBC 模式,密钥 256位(32字节)
解密流程
- 对参数字典排序后,sha1加密,校验签名是否一致
- 加密字符串进行 BASE64 解码后,使用 openssl 对其解密
- 去掉密文头部的随机16字节,去掉中间消息长度的4字节,最后去掉尾部填充的字节
消息解密代码示例
PHP
php
public function decryptMsg(string $encrypt, int $timestamp, string $nonce, string $msg_signature)
{
try {
// 消息校验Token,勿泄漏
$array = array($encrypt, $this->msg_token, $timestamp, $nonce);
sort($array, SORT_STRING);
$signature = sha1(implode($array));
if ($signature !== $msg_signature) {
throw new \Exception(OpenApiErrorCode::FAIL_DECRYPT_SIGNATURE, '签名校验不一致');
}
$ciphertext_dec = base64_decode($encrypt);
// 消息加解密Key,勿泄漏
$key = base64_decode($this->encoding_aes_key . "=");
$iv = substr($key, 0, 16);
$decrypted = openssl_decrypt($ciphertext_dec, 'aes-256-cbc', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
$text = $this->decode($decrypted);
if (strlen($text) < 16) {
throw new \Exception(OpenApiErrorCode::FAIL_DECRYPT_AES, 'AES解密串非法,小于16位');
}
$content = substr($text, 16, strlen($text));
$len_list = unpack("N", substr($content, 0, 4));
$msg_len = $len_list[1];
$msg = json_decode(substr($content, 4, $msg_len), true);
return [null, $msg];
} catch (\Throwable $e) {
return [sprintf('%s:%s', $e->getCode(), $e->getMessage()), null];
}
}
/**
* 对解密后的明文进行补位删除
* @param $text string
* @return bool|string
*/
private function decode(string $text)
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > 32) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
Python
python
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import base64
import hashlib
import json
import chardet
from Crypto.Cipher import AES
def pkcs7_unpad(data):
padding_len = data[-1]
return data[:-padding_len]
def openssl_decrypt(ciphertext, key, iv):
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(ciphertext)
decrypted = pkcs7_unpad(decrypted)
return decrypted
class decrypt:
# 消息校验Token
msg_token = "test"
# 消息加解密Key
encoding_aes_key = "test"
def decrypt_msg(self, encrypt, timestamp, nonce, msg_signature):
try:
# 消息校验Token,勿泄漏
array = [encrypt, self.msg_token, str(timestamp), nonce]
array.sort()
# 将数组元素连接为字符串
array_string = ''.join(array)
# 计算SHA1哈希
signature = hashlib.sha1(array_string.encode()).hexdigest()
if signature != msg_signature:
raise Exception("签名校验不一致")
# 解码密文
ciphertext_dec = base64.b64decode(encrypt)
# 解码密钥
key = base64.b64decode(self.encoding_aes_key + '=')
# 解密
iv = key[:16]
# 解密数据
decrypted = openssl_decrypt(ciphertext_dec, key, iv)
# 检测编码
detected_encoding = chardet.detect(key)['encoding']
if detected_encoding == 'utf-8':
text = decrypted.decode('utf-8')
else:
# 其他ibm866/Windows-1251编码
text = decrypted.decode(detected_encoding)
if len(text) < 16:
raise Exception('AES解密串非法,小于16位')
# 提取消息
result = json.loads(text[20:-18])
print(f"解密数据: {result}")
return result
except Exception as e:
print(f"解密错误:{e}")
return f"{e.__class__.__name__}:{str(e)}"
解密后参数
参数 | 类型 | 长度 | 必填 | 说明 |
---|---|---|---|---|
appid | string | 18 | 是 | |
info_type | string | 是 | 对应推送的消息类型 | |
data | 是 | 对应推送的消息格式 |
解密后参数示例
json
{
"appid": "123456",
"info_type": "",
"data": [
{
"id": 1,
"name": 1
},
{
"id": 1,
"name": 1
}
]
}