Skip to content
快速导航

概述

  • 用户填写了 消息接收url ,且有消息通知时会向 消息接收urlPOST 形式推送消息

重试机制

若由于网络不通等原因首次推送不成功,会自动重试1次

推送参数

参数类型长度必填说明
encryptstring加密字符串
timestampint10时间戳 1661326296
noncestring8随机字符串
msg_signaturestring40消息体签名

推送参数示例

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)}"

解密后参数

参数类型长度必填说明
appidstring18
info_typestring对应推送的消息类型
data对应推送的消息格式

解密后参数示例

json
{
  "appid": "123456",
  "info_type": "",
  "data": [
    {
      "id": 1,
      "name": 1
    },
    {
      "id": 1,
      "name": 1
    }
  ]
}