微信消息加解密(GoLang)-- 接收和回复加密消息
程序员文章站
2022-03-07 16:53:31
`接收微信消息的接口内容:` 附:微信消息加解密工具包(GoLang) go package wechat //微信消息加解密工具包 const ( //以下均为公众号管理后台设置项 token = "XXXXXXXX" appID = "XXXXXXXXXX" encodingAESKey = " ......
接收微信消息的接口内容:
//"r"为*http.Request r.ParseForm() timestamp := strings.Join(r.Form["timestamp"], "") nonce := strings.Join(r.Form["nonce"], "") signature := strings.Join(r.Form["signature"], "") encryptType := strings.Join(r.Form["encrypt_type"], "") msgSignature := strings.Join(r.Form["msg_signature"], "") if !wechat.ValidateUrl(timestamp, nonce, signature) { wmm.log.AppendObj(nil, "Wechat Message Service: this http request is not from Wechat platform!") return } //微信安全模式更改/首次添加url,需要验证,将参数原样写会即可 var es string if e = req.ParseGet("echostr", &es); e != nil { } if es != "" { //todo 将参数值es原先写回即可 return } if r.Method == "POST" { if encryptType == "aes" { encryptRequestBody := wechat.ParseEncryptRequestBody(r) // Validate mstBody signature if !wechat.ValidateMsg(timestamp, nonce, encryptRequestBody.Encrypt, msgSignature) { return } // Decode base64 cipherData, err := base64.StdEncoding.DecodeString(encryptRequestBody.Encrypt) if err != nil { return } // AES Decrypt plainData, err := wechat.AesDecrypt(cipherData, wechat.AesKey) if err != nil { return } //封装struct textRequestBody, err := wechat.ParseEncryptTextRequestBody(plainData) if err != nil { return } var url string tp := textRequestBody.MsgType if tp == "text" && textRequestBody.Content == "【收到不支持的消息类型,暂无法显示】" { //安全模式下向用户回复消息也需要加密 respBody, e := wechat.MakeEncryptResponseBody(textRequestBody.ToUserName, textRequestBody.FromUserName, "一些回复给用户的消息", nonce, timestamp) if e != nil { return e } //此处return NewSimpleError是一个对返回值处理的封装,返回xml格式消息,并不是返回错误 return service.NewSimpleError(service.SERVER_WRITE_XML, string(respBody)) } if tp == "event" { //某个类型的消息暂时后台不作处理,也需要向微信服务器做出响应 return service.NewSimpleError(service.SERVER_WRITE_TEXT, "success") } } return service.NewSimpleError(service.SERVER_WRITE_TEXT, "success")
附:微信消息加解密工具包(GoLang)
package wechat //微信消息加解密工具包 const ( //以下均为公众号管理后台设置项 token = "XXXXXXXX" appID = "XXXXXXXXXX" encodingAESKey = "XXXXXXXXXXXXXXX" ) var AesKey []byte func EncodingAESKey2AESKey(encodingKey string) []byte { data, _ := base64.StdEncoding.DecodeString(encodingKey + "=") return data } func init() { AesKey = EncodingAESKey2AESKey(encodingAESKey) } type TextRequestBody struct { XMLName xml.Name `xml:"xml"` ToUserName string FromUserName string CreateTime time.Duration MsgType string Url string PicUrl string MediaId string ThumbMediaId string Content string MsgId int Location_X string Location_Y string Label string } type TextResponseBody struct { XMLName xml.Name `xml:"xml"` ToUserName CDATAText FromUserName CDATAText CreateTime string MsgType CDATAText Content CDATAText } type EncryptRequestBody struct { XMLName xml.Name `xml:"xml"` ToUserName string Encrypt string } type EncryptResponseBody struct { XMLName xml.Name `xml:"xml"` Encrypt CDATAText MsgSignature CDATAText TimeStamp string Nonce CDATAText } type EncryptResponseBody1 struct { XMLName xml.Name `xml:"xml"` Encrypt string MsgSignature string TimeStamp string Nonce string } type CDATAText struct { Text string `xml:",innerxml"` } func MakeSignature(timestamp, nonce string) string { sl := []string{token, timestamp, nonce} sort.Strings(sl) s := sha1.New() io.WriteString(s, strings.Join(sl, "")) return fmt.Sprintf("%x", s.Sum(nil)) } func MakeMsgSignature(timestamp, nonce, msg_encrypt string) string { sl := []string{token, timestamp, nonce, msg_encrypt} sort.Strings(sl) s := sha1.New() io.WriteString(s, strings.Join(sl, "")) return fmt.Sprintf("%x", s.Sum(nil)) } func ValidateUrl(timestamp, nonce, signatureIn string) bool { signatureGen := MakeSignature(timestamp, nonce) if signatureGen != signatureIn { return false } return true } func ValidateMsg(timestamp, nonce, msgEncrypt, msgSignatureIn string) bool { msgSignatureGen := MakeMsgSignature(timestamp, nonce, msgEncrypt) if msgSignatureGen != msgSignatureIn { return false } return true } func ParseEncryptRequestBody(r *http.Request) *EncryptRequestBody { body, err := ioutil.ReadAll(r.Body) if err != nil { return nil } // mlog.AppendObj(nil, "Wechat Message Service: RequestBody--", body) requestBody := &EncryptRequestBody{} xml.Unmarshal(body, requestBody) return requestBody } func ParseTextRequestBody(r *http.Request) *TextRequestBody { body, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { log.Fatal(err) return nil } requestBody := &TextRequestBody{} xml.Unmarshal(body, requestBody) return requestBody } func Value2CDATA(v string) CDATAText { //return CDATAText{[]byte("<![CDATA[" + v + "]]>")} return CDATAText{"<![CDATA[" + v + "]]>"} } func MakeTextResponseBody(fromUserName, toUserName, content string) ([]byte, error) { textResponseBody := &TextResponseBody{} textResponseBody.FromUserName = Value2CDATA(fromUserName) textResponseBody.ToUserName = Value2CDATA(toUserName) textResponseBody.MsgType = Value2CDATA("text") textResponseBody.Content = Value2CDATA(content) textResponseBody.CreateTime = strconv.Itoa(int(time.Duration(time.Now().Unix()))) return xml.MarshalIndent(textResponseBody, " ", " ") } func MakeEncryptResponseBody(fromUserName, toUserName, content, nonce, timestamp string) ([]byte, error) { encryptBody := &EncryptResponseBody{} encryptXmlData, _ := MakeEncryptXmlData(fromUserName, toUserName, timestamp, content) encryptBody.Encrypt = Value2CDATA(encryptXmlData) encryptBody.MsgSignature = Value2CDATA(MakeMsgSignature(timestamp, nonce, encryptXmlData)) encryptBody.TimeStamp = timestamp encryptBody.Nonce = Value2CDATA(nonce) return xml.MarshalIndent(encryptBody, " ", " ") } func MakeEncryptXmlData(fromUserName, toUserName, timestamp, content string) (string, error) { textResponseBody := &TextResponseBody{} textResponseBody.FromUserName = Value2CDATA(fromUserName) textResponseBody.ToUserName = Value2CDATA(toUserName) textResponseBody.MsgType = Value2CDATA("text") textResponseBody.Content = Value2CDATA(content) textResponseBody.CreateTime = timestamp body, err := xml.MarshalIndent(textResponseBody, " ", " ") if err != nil { return "", errors.New("xml marshal error") } buf := new(bytes.Buffer) err = binary.Write(buf, binary.BigEndian, int32(len(body))) if err != nil { mlog.AppendObj(err, "Binary write err:", err) } bodyLength := buf.Bytes() randomBytes := []byte("abcdefghijklmnop") plainData := bytes.Join([][]byte{randomBytes, bodyLength, body, []byte(appID)}, nil) cipherData, err := AesEncrypt(plainData, AesKey) if err != nil { return "", errors.New("AesEncrypt error") } return base64.StdEncoding.EncodeToString(cipherData), nil } // PadLength calculates padding length, from github.com/vgorin/cryptogo func PadLength(slice_length, blocksize int) (padlen int) { padlen = blocksize - slice_length%blocksize if padlen == 0 { padlen = blocksize } return padlen } //from github.com/vgorin/cryptogo func PKCS7Pad(message []byte, blocksize int) (padded []byte) { // block size must be bigger or equal 2 if blocksize < 1<<1 { panic("block size is too small (minimum is 2 bytes)") } // block size up to 255 requires 1 byte padding if blocksize < 1<<8 { // calculate padding length padlen := PadLength(len(message), blocksize) // define PKCS7 padding block padding := bytes.Repeat([]byte{byte(padlen)}, padlen) // apply padding padded = append(message, padding...) return padded } // block size bigger or equal 256 is not currently supported panic("unsupported block size") } func AesEncrypt(plainData []byte, aesKey []byte) ([]byte, error) { k := len(aesKey) if len(plainData)%k != 0 { plainData = PKCS7Pad(plainData, k) } block, err := aes.NewCipher(aesKey) if err != nil { return nil, err } iv := make([]byte, aes.BlockSize) if _, err := io.ReadFull(rand.Reader, iv); err != nil { return nil, err } cipherData := make([]byte, len(plainData)) blockMode := cipher.NewCBCEncrypter(block, iv) blockMode.CryptBlocks(cipherData, plainData) return cipherData, nil } func AesDecrypt(cipherData []byte, aesKey []byte) ([]byte, error) { k := len(aesKey) //PKCS#7 if len(cipherData)%k != 0 { return nil, errors.New("crypto/cipher: ciphertext size is not multiple of aes key length") } block, err := aes.NewCipher(aesKey) if err != nil { return nil, err } iv := make([]byte, aes.BlockSize) if _, err := io.ReadFull(rand.Reader, iv); err != nil { return nil, err } blockMode := cipher.NewCBCDecrypter(block, iv) plainData := make([]byte, len(cipherData)) blockMode.CryptBlocks(plainData, cipherData) return plainData, nil } func ValidateAppId(id []byte) bool { if string(id) == appID { return true } return false } func ParseEncryptTextRequestBody(plainText []byte) (*TextRequestBody, error) { // Read length buf := bytes.NewBuffer(plainText[16:20]) var length int32 binary.Read(buf, binary.BigEndian, &length) // appID validation appIDstart := 20 + length id := plainText[appIDstart : int(appIDstart)+len(appID)] if !ValidateAppId(id) { mlog.AppendObj(nil, "Wechat Message Service: appid is invalid!") return nil, errors.New("Appid is invalid") } mlog.AppendObj(nil, "Wechat Message Service: appid validation is ok!") textRequestBody := &TextRequestBody{} xml.Unmarshal(plainText[20:20+length], textRequestBody) return textRequestBody, nil } func ParseEncryptResponse(responseEncryptTextBody []byte) { textResponseBody := &EncryptResponseBody1{} xml.Unmarshal(responseEncryptTextBody, textResponseBody) if !ValidateMsg(textResponseBody.TimeStamp, textResponseBody.Nonce, textResponseBody.Encrypt, textResponseBody.MsgSignature) { mlog.AppendInfo("msg signature is invalid") return } cipherData, err := base64.StdEncoding.DecodeString(textResponseBody.Encrypt) if err != nil { mlog.AppendObj(err, "Wechat Message Service: Decode base64 error") return } plainText, err := AesDecrypt(cipherData, AesKey) if err != nil { mlog.AppendInfo(err) return } mlog.AppendInfo(string(plainText)) } func DecryptWechatAppletUser(encryptedData string, session_key string, iv string) ([]byte, error) { ciphertext, _ := base64.StdEncoding.DecodeString(encryptedData) key, _ := base64.StdEncoding.DecodeString(session_key) keyBytes := []byte(key) block, err := aes.NewCipher(keyBytes) //选择加密算法 if err != nil { return nil, err } iv_b, _ := base64.StdEncoding.DecodeString(iv) blockModel := cipher.NewCBCDecrypter(block, iv_b) plantText := make([]byte, len(ciphertext)) blockModel.CryptBlocks(plantText, ciphertext) plantText = PKCS7UnPadding(plantText, block.BlockSize()) return plantText, nil } func PKCS7UnPadding(plantText []byte, blockSize int) []byte { length := len(plantText) unpadding := int(plantText[length-1]) return plantText[:(length - unpadding)] }