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

启用安全模式后,公众号主动调用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: 恋水无意