服务器配置中将消息加解密模式指定为安全模式。
官方提供了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]
}
完整的代码示例在这里。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意