业务接收到请求后,需要做签名验证、解密等操作

1. 签名

用于验证回调请求数据无篡改

验证签名:
token = x-request-token (请求头里获取) 
app_id = x-request-app-id (请求头里获取) 
timestamp = x-request-timestamp (请求头里获取) 
body = request_body(请求的body参数)   // +:表示字符串拼接 检查 
sha256(app_id+body+timestamp) == token 是否成立 

 

2. 解密

当 x-request-need-encrypt(请求头)true
业务方需要基于AES-256-CBC 算法,使用 secret(后台获取) 解密 request_body 里的 encrypt (使用json 解析 request_body 后,获取encrypt的值)

2.1.1 需要解密时,request_body格式

 //x-request-need-encrypt 为true 需要解密
secretKey=secret(后台获取)   


//需要解密的示例值  
request_body=`{"encrypt":"-iGBkTiANHIebyc02rD67ih5TW48AnFi2HGAI-MXlC-K07nCsVU8k0DtdzgUEp7uUgg5D2VOGE2TV6x_p2w-g5r7OK5Qv4f5ZmY0tdWosD1Nzbu3wNyZctn9y9IlAD8ZZ1dLNxI039LyNAX1hkTAN99TDmso4f3w1eIQ3qsr0rCfpBfqAsp_ndl_rQXahZXofW2wGqOLuewmTjPBWj5jgJkMnJxURGiGcdDC7BjLW_tiHMGcWm6LcVPASMIAac-EyQPr7bvTbQ0NvYK0Ikzozgzia5qIQqaYcoet5dMn4TY"}` 

//获取密文
requestBody:=map[string]string{} json.unmarshal([]byte(request_body),&requestBody) encrypt:=requestBody["encrypt"] 

//解密 
Decrypt(encrypt,secretKey)   

//解密后的格式
{"schema":"1.0","header":{"event_id":"814f6a52239171a4a47387df2d41f11e","create_time":1739763187139,"event_type":"im.message.group_at.receive_v1","app_id":"robot_mibxy8f6mfstpmqp"},"event":{"open_id":"ou_8a61ec4e2c7232294c498fd70f5649d0","open_message_id":"190b20c5a501ed123826c61bc7e258bf-202502-0","create_time":0,"send_time":1739763186871,"send_id":"BxgMFUUoCQZUHh5LX0QADxpBCAsLBwUFGwo","content_type":114,"raw_content_type":140,"sender_nickname":"如易","sender_face_url":"https://s1-imfile.im30.net/uwnz1wdo6fgs9rmii5xfzwwjye","group_id":"100001141t0","content":"{\"text\":\"@测试机器人5-如易 111\",\"json\":{\"content\":[{\"content\":[{\"attrs\":{\"id\":\"HR89XSslFxMYAgNeAGkZXgcaRxUQHRobBg4AOQgOUwEdWlAFVAcYFQ\",\"label\":\"测试机器人5-如易\"},\"type\":\"mention\"},{\"text\":\" 111\",\"type\":\"text\"}],\"type\":\"paragraph\"}],\"type\":\"doc\"},\"atUserIDList\":[\"HR89XSslFxMYAgNeAGkZXgcaRxUQHRobBg4AOQgOUwEdWlAFVAcYFQ\"]}","quote_content_type":101,"quote_send_id":"BxgMFUUoCQZUHh5LX0QADxpBCAsLBwUFGwo","quote_sender_nickname":"如易","quote_sender_face_url":"https://s1-imfile.im30.net/uwnz1wdo6fgs9rmii5xfzwwjye","quote_content":"@奥托 引用的会话消息\n[图片]","quote_message_id":"cf7437c5c277b87c0b407cf9ad3898af-202502-0"}}

2.1.2 不需要解密时,request_body 格式

//x-request-need-encrypt 为 false 不需要解密   

request_body=`{"schema":"1.0","header":{"event_id":"eec4bbda12765a9f6946483403f32913","create_time":1737110488603,"event_type":"application.bot.verify_callback_url","app_id":"robot_peozr1m9cq3mox8p"},"event":null}` 

3. 加解密算法

加解密算法 AES-256-CBC

  1. 使用 SHA256 对 Encrypt Key进行哈希得到密钥key

  2. 使用 PKCS7Padding 方式将事件内容进行填充。

  3. 生成 16 字节的随机数作为初始向量 iv

  4. 使用 ivkey 对事件内容加密得到 encrypted_event

  5. 应用收到的密文为 base64(iv+encrypted_event)

 

package utils
import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "crypto/sha256"
    "encoding/base64"
    "errors"
    "fmt"
    "io"
    "strings"
)
func Encrypt(plaintext []byte, encryptKey string) (string, error) {
    // 使用SHA256对加密密钥进行哈希得到密钥key
    key := sha256.Sum256([]byte(encryptKey))
    // 使用PKCS7Padding方式对明文内容进行填充
    blockSize := aes.BlockSize
    padding := blockSize - len(plaintext)%blockSize
    padText := append(plaintext, bytes.Repeat([]byte{byte(padding)}, padding)...)
    // 生成16字节的随机数作为初始向量iv
    iv := make([]byte, blockSize)
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
       return "", fmt.Errorf("io.ReadFull Error[%v]", err)
    }
    // 创建AES加密算法实例
    block, err := aes.NewCipher(key[:])
    if err != nil {
       return "", fmt.Errorf("aes.NewCipher Error[%v]", err)
    }
    // 使用CBC模式进行加密
    mode := cipher.NewCBCEncrypter(block, iv)
    encrypted := make([]byte, len(padText))
    mode.CryptBlocks(encrypted, padText)
    // 将iv和加密后的内容拼接,并进行base64编码
    ciphertext := append(iv, encrypted...)
    ciphertextBase64 := base64.RawURLEncoding.EncodeToString(ciphertext)
    return ciphertextBase64, nil
}
// PKCS7Padding 填充
func PKCS7Padding(data []byte, blockSize int) []byte {
    padding := blockSize - len(data)%blockSize
    padText := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(data, padText...)
}
func Decrypt(encrypt string, key string) (string, error) {
    buf, err := base64.RawURLEncoding.DecodeString(encrypt)
    if err != nil {
       return "", fmt.Errorf("base64StdEncode Error[%v]", err)
    }
    if len(buf) < aes.BlockSize {
       return "", errors.New("cipher too short")
    }
    keyBs := sha256.Sum256([]byte(key))
    block, err := aes.NewCipher(keyBs[:sha256.Size])
    if err != nil {
       return "", fmt.Errorf("AESNewCipher Error[%v]", err)
    }
    iv := buf[:aes.BlockSize]
    buf = buf[aes.BlockSize:]
    if len(buf)%aes.BlockSize != 0 {
       return "", errors.New("ciphertext is not a multiple of the block size")
    }
    mode := cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(buf, buf)
    n := strings.Index(string(buf), "{")
    if n == -1 {
       n = 0
    }
    m := strings.LastIndex(string(buf), "}")
    if m == -1 {
       m = len(buf) - 1
    }
    return string(buf[n : m+1]), nil
}

 更新时间:2026-03-26
上一篇:
下一篇: