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: 恋水无意