// 回复消息给微信服务器(安全模式). // 要求 msg 是有效的消息数据结构(经过 encoding/xml marshal 后符合消息的格式); // 如果有必要可以修改 Request 里面的某些值, 比如 TimeStamp. func WriteAESResponse(w http.ResponseWriter, r *Request, msg interface{}) (err error) { if w == nil { return errors.New("nil http.ResponseWriter") } if r == nil { return errors.New("nil Request") } if msg == nil { return errors.New("nil message") } MsgRawXML, err := xml.Marshal(msg) if err != nil { return } EncryptedMsg := util.AESEncryptMsg(r.Random, MsgRawXML, r.WechatAppId, r.AESKey) base64EncryptedMsg := base64.StdEncoding.EncodeToString(EncryptedMsg) responseHttpBody := ResponseHttpBody{ EncryptedMsg: base64EncryptedMsg, TimeStamp: r.TimeStamp, Nonce: r.Nonce, } TimestampStr := strconv.FormatInt(responseHttpBody.TimeStamp, 10) responseHttpBody.MsgSignature = util.MsgSign(r.WechatToken, TimestampStr, responseHttpBody.Nonce, responseHttpBody.EncryptedMsg) return xml.NewEncoder(w).Encode(&responseHttpBody) }
// ServeHTTP 处理 http 消息请求 // NOTE: 调用者保证所有参数有效 func ServeHTTP(w http.ResponseWriter, r *http.Request, urlValues url.Values, wechatServer WechatServer, invalidRequestHandler InvalidRequestHandler) { switch r.Method { case "POST": // 消息处理 signature1, timestampStr, nonce, encryptType, msgSignature1, err := parsePostURLQuery(urlValues) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } timestamp, err := strconv.ParseInt(timestampStr, 10, 64) if err != nil { err = errors.New("can not parse timestamp to int64: " + timestampStr) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } switch encryptType { case "aes": // 兼容模式, 安全模式 //if len(signature1) != 40 { // err = fmt.Errorf("the length of signature mismatch, have: %d, want: 40", len(signature1)) // invalidRequestHandler.ServeInvalidRequest(w, r, err) // return //} //WechatToken := wechatServer.Token() //signature2 := util.Sign(WechatToken, timestampStr, nonce) //if subtle.ConstantTimeCompare([]byte(signature1), []byte(signature2)) != 1 { // err = fmt.Errorf("check signature failed, input: %s, local: %s", signature1, signature2) // invalidRequestHandler.ServeInvalidRequest(w, r, err) // return //} // 首先验证密文签名 if len(msgSignature1) != 40 { err = fmt.Errorf("the length of msg_signature mismatch, have: %d, want: 40", len(msgSignature1)) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } var requestHttpBody RequestHttpBody if err := xml.NewDecoder(r.Body).Decode(&requestHttpBody); err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } // 安全考虑验证下 ToUserName haveToUserName := requestHttpBody.ToUserName wantToUserName := wechatServer.Id() if len(haveToUserName) != len(wantToUserName) { err = fmt.Errorf("the RequestHttpBody's ToUserName mismatch, have: %s, want: %s", haveToUserName, wantToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } if subtle.ConstantTimeCompare([]byte(haveToUserName), []byte(wantToUserName)) != 1 { err = fmt.Errorf("the RequestHttpBody's ToUserName mismatch, have: %s, want: %s", haveToUserName, wantToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } WechatToken := wechatServer.Token() msgSignature2 := util.MsgSign(WechatToken, timestampStr, nonce, requestHttpBody.EncryptedMsg) if subtle.ConstantTimeCompare([]byte(msgSignature1), []byte(msgSignature2)) != 1 { err = fmt.Errorf("check signature failed, input: %s, local: %s", msgSignature1, msgSignature2) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } // 验证签名成功, 解密 EncryptedMsgBytes, err := base64.StdEncoding.DecodeString(requestHttpBody.EncryptedMsg) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } WechatAppId := wechatServer.AppId() AESKey := wechatServer.CurrentAESKey() Random, RawMsgXML, err := util.AESDecryptMsg(EncryptedMsgBytes, WechatAppId, AESKey) if err != nil { // 尝试用上一次的 AESKey 来解密 LastAESKey := wechatServer.LastAESKey() if bytes.Equal(zeroAESKey[:], LastAESKey[:]) || bytes.Equal(AESKey[:], LastAESKey[:]) { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } AESKey = LastAESKey // NOTE Random, RawMsgXML, err = util.AESDecryptMsg(EncryptedMsgBytes, WechatAppId, AESKey) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } } // 解密成功, 解析 MixedMessage var MixedMsg MixedMessage if err = xml.Unmarshal(RawMsgXML, &MixedMsg); err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } // 安全考虑再次验证 ToUserName if haveToUserName != MixedMsg.ToUserName { err = fmt.Errorf("the RequestHttpBody's ToUserName(==%s) mismatch the MixedMessage's ToUserName(==%s)", haveToUserName, MixedMsg.ToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } // 成功, 交给 MessageHandler r := &Request{ HttpRequest: r, Signature: signature1, TimeStamp: timestamp, Nonce: nonce, RawMsgXML: RawMsgXML, MixedMsg: &MixedMsg, MsgSignature: msgSignature1, EncryptType: encryptType, AESKey: AESKey, Random: Random, WechatId: wantToUserName, WechatToken: WechatToken, WechatAppId: WechatAppId, } wechatServer.MessageHandler().ServeMessage(w, r) case "", "raw": // 明文模式 // 首先验证签名 if len(signature1) != 40 { err = fmt.Errorf("the length of signature mismatch, have: %d, want: 40", len(signature1)) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } WechatToken := wechatServer.Token() signature2 := util.Sign(WechatToken, timestampStr, nonce) if subtle.ConstantTimeCompare([]byte(signature1), []byte(signature2)) != 1 { err = fmt.Errorf("check signature failed, input: %s, local: %s", signature1, signature2) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } // 验证签名成功, 解析 MixedMessage RawMsgXML, err := ioutil.ReadAll(r.Body) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } var MixedMsg MixedMessage if err := xml.Unmarshal(RawMsgXML, &MixedMsg); err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } // 安全考虑验证 ToUserName haveToUserName := MixedMsg.ToUserName wantToUserName := wechatServer.Id() if len(haveToUserName) != len(wantToUserName) { err = fmt.Errorf("the message's ToUserName mismatch, have: %s, want: %s", haveToUserName, wantToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } if subtle.ConstantTimeCompare([]byte(haveToUserName), []byte(wantToUserName)) != 1 { err = fmt.Errorf("the message's ToUserName mismatch, have: %s, want: %s", haveToUserName, wantToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } // 成功, 交给 MessageHandler r := &Request{ HttpRequest: r, Signature: signature1, TimeStamp: timestamp, Nonce: nonce, RawMsgXML: RawMsgXML, MixedMsg: &MixedMsg, WechatId: wantToUserName, WechatToken: WechatToken, WechatAppId: wechatServer.AppId(), } wechatServer.MessageHandler().ServeMessage(w, r) default: // 未知的加密类型 err := errors.New("unknown encrypt_type: " + encryptType) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } case "GET": // 首次验证 signature1, timestamp, nonce, echostr, err := parseGetURLQuery(urlValues) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } if len(signature1) != 40 { err = fmt.Errorf("the length of signature mismatch, have: %d, want: 40", len(signature1)) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } signature2 := util.Sign(wechatServer.Token(), timestamp, nonce) if subtle.ConstantTimeCompare([]byte(signature1), []byte(signature2)) != 1 { err = fmt.Errorf("check signature failed, input: %s, local: %s", signature1, signature2) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } io.WriteString(w, echostr) } }