func writeAESResponse(w io.Writer, msg interface{}, timestamp int64, nonce string, AESKey [32]byte, random []byte, AppId, Token string) (err error) { rawXMLMsg, err := xml.Marshal(msg) if err != nil { return } EncryptedMsg := util.AESEncryptMsg(random, rawXMLMsg, AppId, AESKey) base64EncryptedMsg := base64.StdEncoding.EncodeToString(EncryptedMsg) var responseHttpBody response.ResponseHttpBody responseHttpBody.EncryptedMsg = base64EncryptedMsg responseHttpBody.TimeStamp = timestamp responseHttpBody.Nonce = nonce timestampStr := strconv.FormatInt(timestamp, 10) responseHttpBody.MsgSignature = util.MsgSignature(Token, timestampStr, nonce, base64EncryptedMsg) return xml.NewEncoder(w).Encode(&responseHttpBody) }
// ServeHTTP 处理 http 消息请求 // NOTE: 确保所有参数合法, r.Body 能正确读取数据 func ServeHTTP(w http.ResponseWriter, r *http.Request, urlValues url.Values, agent Agent, invalidRequestHandler InvalidRequestHandler) { switch r.Method { case "POST": // 消息处理 msgSignature1, timestampStr, nonce, err := parsePostURLQuery(urlValues) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } const signatureLen = sha1.Size * 2 if len(msgSignature1) != signatureLen { err = fmt.Errorf("the length of msg_signature mismatch, have: %d, want: %d", len(msgSignature1), signatureLen) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } timestamp, err := strconv.ParseInt(timestampStr, 10, 64) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, fmt.Errorf("can not parse timestamp(==%q) to int64, error: %s", timestampStr, err.Error())) return } var requestHttpBody request.RequestHttpBody if err := xml.NewDecoder(r.Body).Decode(&requestHttpBody); err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } agentCorpId := agent.GetCorpId() if len(requestHttpBody.CorpId) != len(agentCorpId) { err = fmt.Errorf("the message RequestHttpBody's ToUserName mismatch, have: %s, want: %s", requestHttpBody.CorpId, agentCorpId) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } if subtle.ConstantTimeCompare([]byte(requestHttpBody.CorpId), []byte(agentCorpId)) != 1 { err = fmt.Errorf("the message RequestHttpBody's ToUserName mismatch, have: %s, want: %s", requestHttpBody.CorpId, agentCorpId) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } agentAgentId := agent.GetAgentId() if requestHttpBody.AgentId != agentAgentId && requestHttpBody.AgentId != 0 { err = fmt.Errorf("the message RequestHttpBody's AgentId mismatch, have: %d, want: %d", requestHttpBody.AgentId, agentAgentId) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } // 此时 // 要么 requestHttpBody.AgentId == agent.GetAgentId(), // 要么 requestHttpBody.AgentId == 0 msgSignature2 := util.MsgSignature(agent.GetToken(), timestampStr, nonce, requestHttpBody.EncryptedMsg) if subtle.ConstantTimeCompare([]byte(msgSignature1), []byte(msgSignature2)) != 1 { err = fmt.Errorf("check signature failed, have: %s, want: %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 } random, rawXMLMsg, err := util.AESDecryptMsg(EncryptedMsgBytes, agentCorpId, agent.GetAESKey()) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } var msgReq request.Request if err := xml.Unmarshal(rawXMLMsg, &msgReq); err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } if requestHttpBody.CorpId != msgReq.ToUserName { err = fmt.Errorf("the RequestHttpBody's ToUserName(==%s) mismatch the Request's ToUserName(==%s)", requestHttpBody.CorpId, msgReq.ToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } if requestHttpBody.AgentId != msgReq.AgentId { err = fmt.Errorf("the RequestHttpBody's AgentId(==%d) mismatch the Request's AgengId(==%d)", requestHttpBody.AgentId, msgReq.AgentId) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } // 订阅/取消订阅 整个企业号, requestHttpBody.AgentId == 0 if requestHttpBody.AgentId != agentAgentId { if msgReq.MsgType == request.MSG_TYPE_EVENT && (msgReq.Event == request.EVENT_TYPE_SUBSCRIBE || msgReq.Event == request.EVENT_TYPE_UNSUBSCRIBE) { // do nothing } else { err = fmt.Errorf("the message Request's AgentId mismatch, have: %d, want: %d", msgReq.AgentId, agentAgentId) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } } msgDispatch(w, r, &msgReq, rawXMLMsg, timestamp, nonce, random, agent) case "GET": // 首次验证 msgSignature1, timestamp, nonce, encryptedMsg, err := parseGetURLQuery(urlValues) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } const signatureLen = sha1.Size * 2 if len(msgSignature1) != signatureLen { err = fmt.Errorf("the length of msg_signature mismatch, have: %d, want: %d", len(msgSignature1), signatureLen) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } msgSignature2 := util.MsgSignature(agent.GetToken(), timestamp, nonce, encryptedMsg) if subtle.ConstantTimeCompare([]byte(msgSignature1), []byte(msgSignature2)) != 1 { err = fmt.Errorf("check signature failed, have: %s, want: %s", msgSignature1, msgSignature2) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } EncryptedMsgBytes, err := base64.StdEncoding.DecodeString(encryptedMsg) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } _, echostr, err := util.AESDecryptMsg(EncryptedMsgBytes, agent.GetCorpId(), agent.GetAESKey()) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } w.Write(echostr) } }
// ServeHTTP 处理 http 消息请求 // NOTE: 确保所有参数合法, r.Body 能正确读取数据 func ServeHTTP(w http.ResponseWriter, r *http.Request, urlValues url.Values, agent Agent, 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 { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } switch encryptType { case "", "raw": // 明文模式 const signatureLen = sha1.Size * 2 if len(signature1) != signatureLen { err = fmt.Errorf("the length of signature mismatch, have: %d, want: %d", len(signature1), signatureLen) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } signature2 := util.Signature(agent.GetToken(), timestampStr, nonce) if subtle.ConstantTimeCompare([]byte(signature1), []byte(signature2)) != 1 { err = fmt.Errorf("check signature failed, have: %s, want: %s", signature1, signature2) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } rawXMLMsg, err := ioutil.ReadAll(r.Body) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } var msgReq request.Request if err = xml.Unmarshal(rawXMLMsg, &msgReq); err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } wantToUserName := agent.GetId() if len(msgReq.ToUserName) != len(wantToUserName) { err = fmt.Errorf("the message Request's ToUserName mismatch, have: %s, want: %s", msgReq.ToUserName, wantToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } if subtle.ConstantTimeCompare([]byte(msgReq.ToUserName), []byte(wantToUserName)) != 1 { err = fmt.Errorf("the message Request's ToUserName mismatch, have: %s, want: %s", msgReq.ToUserName, wantToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } rawMsgDispatch(w, r, &msgReq, rawXMLMsg, timestamp, agent) case "aes": // 兼容模式, 安全模式 const signatureLen = sha1.Size * 2 //if len(signature1) != signatureLen { // err = fmt.Errorf("the length of signature mismatch, have: %d, want: %d", len(signature1), signatureLen) // invalidRequestHandler.ServeInvalidRequest(w, r, err) // return //} if len(msgSignature1) != signatureLen { err = fmt.Errorf("the length of msg_signature mismatch, have: %d, want: %d", len(msgSignature1), signatureLen) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } //signature2 := signature(agent.GetToken(), timestampStr, nonce) //if subtle.ConstantTimeCompare([]byte(signature1), []byte(signature2)) != 1 { // err = fmt.Errorf("check signature failed, have: %s, want: %s", signature1, signature2) // invalidRequestHandler.ServeInvalidRequest(w, r, err) // return //} var requestHttpBody request.RequestHttpBody if err := xml.NewDecoder(r.Body).Decode(&requestHttpBody); err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } wantToUserName := agent.GetId() if len(requestHttpBody.ToUserName) != len(wantToUserName) { err = fmt.Errorf("the message RequestHttpBody's ToUserName mismatch, have: %s, want: %s", requestHttpBody.ToUserName, wantToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } if subtle.ConstantTimeCompare([]byte(requestHttpBody.ToUserName), []byte(wantToUserName)) != 1 { err = fmt.Errorf("the message RequestHttpBody's ToUserName mismatch, have: %s, want: %s", requestHttpBody.ToUserName, wantToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } msgSignature2 := util.MsgSignature(agent.GetToken(), timestampStr, nonce, requestHttpBody.EncryptedMsg) if subtle.ConstantTimeCompare([]byte(msgSignature1), []byte(msgSignature2)) != 1 { err = fmt.Errorf("check signature failed, have: %s, want: %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 } AESKey := agent.GetCurrentAESKey() random, rawXMLMsg, err := util.AESDecryptMsg(EncryptedMsgBytes, agent.GetAppId(), AESKey) if err != nil { // 尝试上一个 AESKey LastAESKey := agent.GetLastAESKey() if bytes.Equal(zeroAESKey[:], LastAESKey[:]) || bytes.Equal(AESKey[:], LastAESKey[:]) { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } AESKey = LastAESKey // !!! random, rawXMLMsg, err = util.AESDecryptMsg(EncryptedMsgBytes, agent.GetAppId(), AESKey) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } } var msgReq request.Request if err = xml.Unmarshal(rawXMLMsg, &msgReq); err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } if requestHttpBody.ToUserName != msgReq.ToUserName { err = fmt.Errorf("the RequestHttpBody's ToUserName(==%s) mismatch the Request's ToUserName(==%s)", requestHttpBody.ToUserName, msgReq.ToUserName) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } aesMsgDispatch(w, r, &msgReq, rawXMLMsg, timestamp, nonce, AESKey, random, agent) default: // 未知的加密类型 invalidRequestHandler.ServeInvalidRequest(w, r, errors.New("unknown encrypt_type")) return } case "GET": // 首次验证 signature1, timestamp, nonce, echostr, err := parseGetURLQuery(urlValues) if err != nil { invalidRequestHandler.ServeInvalidRequest(w, r, err) return } const signatureLen = sha1.Size * 2 if len(signature1) != signatureLen { err = fmt.Errorf("the length of signature mismatch, have: %d, want: %d", len(signature1), signatureLen) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } signature2 := util.Signature(agent.GetToken(), timestamp, nonce) if subtle.ConstantTimeCompare([]byte(signature1), []byte(signature2)) != 1 { err = fmt.Errorf("check signature failed, have: %s, want: %s", signature1, signature2) invalidRequestHandler.ServeInvalidRequest(w, r, err) return } io.WriteString(w, echostr) } }