2023-04-26-微信安全模式下消息解析

在微信公众号的使用过程中,为了提高信息传输的安全性,可以在服务器配置中将消息加解密模式指定为安全模式

启用安全模式后,公众号主动调用API的情况并不会受影响,只有被动回复用户的消息时才需要对消息进行加解密

官方提供了5种开发语言的示例代码,参照官方给的C++示例代码,本文给出go语言的解密实现:

func handlerEncrypt(body []byte, timestamp, nonce, msg_sig string) (random, rawXMLMsg []byte, err error) {
request := &WeChatEncryptRequest{}
err = xml.Unmarshal(body, request)
if err != nil {
log.Errorf("unmarshal wechat encrypt request error: %s")
errors.Wrap(err, "unmarshal wechat encrypt request error")
return
}
// verify msg from wechat signature
if calcSignature(token, timestamp, nonce, request.Encrypt) != msg_sig {
log.Errorf("encrypt msg got from wechat verify signature failed")
errors.New("encrypt msg got from wechat verify signature failed")
return
}
// decode cipher text from base64
cipherText, err := base64.StdEncoding.DecodeString(request.Encrypt)
if err != nil {
log.Errorf("decode wechat encrypt request error: %s", err.Error())
errors.Wrap(err, "decode wechat encrypt request error")
return
}
// aes decrypt
plainText, err := aesDecrypt(cipherText, key)
if err != nil {
log.Errorf("decrypt wechat encrypt request error: %s", err.Error())
errors.Wrap(err, "decrypt wechat encrypt request error")
return
}
// get raw wechat encrypt request length
rawXMLMsgLen := int(ntohl(plainText[16:20]))
if rawXMLMsgLen < 0 {
log.Errorf("incorrect msg length: %d", rawXMLMsgLen)
errors.Wrapf(err, "incorrect msg length: %d", rawXMLMsgLen)
return
}
// verify appid
appIDOffset := 20 + rawXMLMsgLen
if len(plainText) <= appIDOffset {
log.Errorf("msg length too large: %d", rawXMLMsgLen)
errors.Wrapf(err, "msg length too large: %d", rawXMLMsgLen)
return
}
// verify appid
if appID != string(plainText[appIDOffset:]) {
log.Errorf("Received an attack disguised as a WeChat server.")
errors.New("Received an attack disguised as a WeChat server.")
return
}
// get random which from wechat
random = plainText[:16:20]
// raw wechat msg
rawXMLMsg = plainText[20:appIDOffset:appIDOffset]
return
}
func calcSignature(args ...string) string {
sort.Strings(args)
h := sha1.New()
for _, arg := range args {
io.WriteString(h, arg)
}
return hex.EncodeToString(h.Sum(nil))
}
func aesDecrypt(cipherText []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
plainText := make([]byte, len(cipherText))
blockMode.CryptBlocks(plainText, cipherText)
plainText = pkcs7UnPadding(plainText)
return plainText, nil
}
func pkcs7UnPadding(data []byte) []byte {
length := len(data)
unpadding := int(data[length-1])
return data[:(length - unpadding)]
}
func ntohl(orderBytes []byte) (n uint32) {
return uint32(orderBytes[0])<<24 |
uint32(orderBytes[1])<<16 |
uint32(orderBytes[2])<<8 |
uint32(orderBytes[3])
}

完整的代码示例在这里


声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意


posted @   落雷  阅读(77)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
点击右上角即可分享
微信分享提示