示例#1
0
文件: context.go 项目: btbxbob/wechat
// AESResponse 回复aes加密的消息给微信服务器.
//  msg:       经过 encoding/xml.Marshal 得到的结果符合微信消息格式的任何数据结构
//  timestamp: 时间戳, 如果为 0 则默认使用 Context.Timestamp
//  nonce:     随机数, 如果为 "" 则默认使用 Context.Nonce
//  random:    16字节的随机字符串, 如果为 nil 则默认使用 Context.Random
func (ctx *Context) AESResponse(msg interface{}, timestamp int64, nonce string, random []byte) (err error) {
	if timestamp == 0 {
		timestamp = ctx.Timestamp
	}
	if nonce == "" {
		nonce = ctx.Nonce
	}
	if len(random) == 0 {
		random = ctx.Random
	}

	msgPlaintext, err := callback.XmlMarshalResponseMessage(msg)
	if err != nil {
		return
	}

	encryptedMsg := util.AESEncryptMsg(random, msgPlaintext, ctx.AppId, ctx.AESKey)
	base64EncryptedMsg := base64.StdEncoding.EncodeToString(encryptedMsg)
	timestampString := strconv.FormatInt(timestamp, 10)
	msgSignature := util.MsgSign(ctx.Token, timestampString, nonce, base64EncryptedMsg)

	if sw, ok := ctx.ResponseWriter.(stringWriter); ok {
		if _, err = sw.WriteString("<xml><Encrypt>"); err != nil {
			return
		}
		if _, err = sw.WriteString(base64EncryptedMsg); err != nil {
			return
		}
		if _, err = sw.WriteString("</Encrypt><MsgSignature>"); err != nil {
			return
		}
		if _, err = sw.WriteString(msgSignature); err != nil {
			return
		}
		if _, err = sw.WriteString("</MsgSignature><TimeStamp>"); err != nil {
			return
		}
		if _, err = sw.WriteString(timestampString); err != nil {
			return
		}
		if _, err = sw.WriteString("</TimeStamp><Nonce>"); err != nil {
			return
		}
		if err = xml.EscapeText(ctx.ResponseWriter, []byte(nonce)); err != nil {
			return
		}
		_, err = sw.WriteString("</Nonce></xml>")
		return
	} else {
		bufw := bufio.NewWriterSize(ctx.ResponseWriter, 256)
		if _, err = bufw.WriteString("<xml><Encrypt>"); err != nil {
			return
		}
		if _, err = bufw.WriteString(base64EncryptedMsg); err != nil {
			return
		}
		if _, err = bufw.WriteString("</Encrypt><MsgSignature>"); err != nil {
			return
		}
		if _, err = bufw.WriteString(msgSignature); err != nil {
			return
		}
		if _, err = bufw.WriteString("</MsgSignature><TimeStamp>"); err != nil {
			return
		}
		if _, err = bufw.WriteString(timestampString); err != nil {
			return
		}
		if _, err = bufw.WriteString("</TimeStamp><Nonce>"); err != nil {
			return
		}
		if err = xml.EscapeText(bufw, []byte(nonce)); err != nil {
			return
		}
		if _, err = bufw.WriteString("</Nonce></xml>"); err != nil {
			return
		}
		return bufw.Flush()
	}
}
示例#2
0
文件: server.go 项目: btbxbob/wechat
// ServeHTTP 处理微信服务器的回调请求, queryParams 参数可以为 nil.
func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request, queryParams url.Values) {
	callback.DebugPrintRequest(r)
	if queryParams == nil {
		queryParams = r.URL.Query()
	}
	errorHandler := srv.errorHandler

	switch r.Method {
	case "POST": // 推送消息(事件)
		switch encryptType := queryParams.Get("encrypt_type"); encryptType {
		case "aes":
			haveSignature := queryParams.Get("signature")
			if haveSignature == "" {
				errorHandler.ServeError(w, r, errors.New("not found signature query parameter"))
				return
			}
			haveMsgSignature := queryParams.Get("msg_signature")
			if haveMsgSignature == "" {
				errorHandler.ServeError(w, r, errors.New("not found msg_signature query parameter"))
				return
			}
			timestampString := queryParams.Get("timestamp")
			if timestampString == "" {
				errorHandler.ServeError(w, r, errors.New("not found timestamp query parameter"))
				return
			}
			timestamp, err := strconv.ParseInt(timestampString, 10, 64)
			if err != nil {
				err = fmt.Errorf("can not parse timestamp query parameter %q to int64", timestampString)
				errorHandler.ServeError(w, r, err)
				return
			}
			nonce := queryParams.Get("nonce")
			if nonce == "" {
				errorHandler.ServeError(w, r, errors.New("not found nonce query parameter"))
				return
			}

			var token string
			currentToken, lastToken := srv.getToken()
			if currentToken == "" {
				err = errors.New("token was not set for Server, see NewServer function or Server.SetToken method")
				errorHandler.ServeError(w, r, err)
				return
			}
			token = currentToken
			wantSignature := util.Sign(token, timestampString, nonce)
			if !security.SecureCompareString(haveSignature, wantSignature) {
				if lastToken == "" {
					err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature)
					errorHandler.ServeError(w, r, err)
					return
				}
				token = lastToken
				wantSignature = util.Sign(token, timestampString, nonce)
				if !security.SecureCompareString(haveSignature, wantSignature) {
					err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature)
					errorHandler.ServeError(w, r, err)
					return
				}
			} else {
				if lastToken != "" {
					srv.removeLastToken(lastToken)
				}
			}

			requestBodyBuf := textBufferPool.Get().(*bytes.Buffer)
			requestBodyBuf.Reset()
			defer textBufferPool.Put(requestBodyBuf)

			if _, err = requestBodyBuf.ReadFrom(r.Body); err != nil {
				errorHandler.ServeError(w, r, err)
				return
			}
			requestBodyBytes := requestBodyBuf.Bytes()

			var requestHttpBody cipherRequestHttpBody
			if err = xmlUnmarshal(requestBodyBytes, &requestHttpBody); err != nil {
				errorHandler.ServeError(w, r, err)
				return
			}

			haveToUserName := requestHttpBody.ToUserName
			wantToUserName := srv.oriId
			if wantToUserName != "" && !security.SecureCompareString(haveToUserName, wantToUserName) {
				err = fmt.Errorf("the message ToUserName mismatch, have: %s, want: %s",
					haveToUserName, wantToUserName)
				errorHandler.ServeError(w, r, err)
				return
			}

			wantMsgSignature := util.MsgSign(token, timestampString, nonce, string(requestHttpBody.Base64EncryptedMsg))
			if !security.SecureCompareString(haveMsgSignature, wantMsgSignature) {
				err = fmt.Errorf("check msg_signature failed, have: %s, want: %s", haveMsgSignature, wantMsgSignature)
				errorHandler.ServeError(w, r, err)
				return
			}

			encryptedMsg := make([]byte, base64.StdEncoding.DecodedLen(len(requestHttpBody.Base64EncryptedMsg)))
			encryptedMsgLen, err := base64.StdEncoding.Decode(encryptedMsg, requestHttpBody.Base64EncryptedMsg)
			if err != nil {
				errorHandler.ServeError(w, r, err)
				return
			}
			encryptedMsg = encryptedMsg[:encryptedMsgLen]

			var aesKey []byte
			currentAESKey, lastAESKey := srv.getAESKey()
			if currentAESKey == nil {
				err = errors.New("aes key was not set for Server, see NewServer function or Server.SetAESKey method")
				errorHandler.ServeError(w, r, err)
				return
			}
			aesKey = currentAESKey
			random, msgPlaintext, haveAppIdBytes, err := util.AESDecryptMsg(encryptedMsg, aesKey)
			if err != nil {
				if lastAESKey == nil {
					errorHandler.ServeError(w, r, err)
					return
				}
				aesKey = lastAESKey
				random, msgPlaintext, haveAppIdBytes, err = util.AESDecryptMsg(encryptedMsg, aesKey)
				if err != nil {
					errorHandler.ServeError(w, r, err)
					return
				}
			} else {
				if lastAESKey != nil {
					srv.removeLastAESKey(lastAESKey)
				}
			}
			callback.DebugPrintPlainRequestMessage(msgPlaintext)

			haveAppId := string(haveAppIdBytes)
			wantAppId := srv.appId
			if wantAppId != "" && !security.SecureCompareString(haveAppId, wantAppId) {
				err = fmt.Errorf("the message AppId mismatch, have: %s, want: %s", haveAppId, wantAppId)
				errorHandler.ServeError(w, r, err)
				return
			}

			var mixedMsg MixedMsg
			if err = xml.Unmarshal(msgPlaintext, &mixedMsg); err != nil {
				errorHandler.ServeError(w, r, err)
				return
			}
			if haveToUserName != mixedMsg.ToUserName {
				err = fmt.Errorf("the message ToUserName mismatch between ciphertext and plaintext, %q != %q",
					haveToUserName, mixedMsg.ToUserName)
				errorHandler.ServeError(w, r, err)
				return
			}

			ctx := &Context{
				ResponseWriter: w,
				Request:        r,

				QueryParams:  queryParams,
				EncryptType:  encryptType,
				MsgSignature: haveMsgSignature,
				Signature:    haveSignature,
				Timestamp:    timestamp,
				Nonce:        nonce,

				MsgCiphertext: requestHttpBody.Base64EncryptedMsg,
				MsgPlaintext:  msgPlaintext,
				MixedMsg:      &mixedMsg,

				Token:  token,
				AESKey: aesKey,
				Random: random,
				AppId:  haveAppId,

				handlerIndex: initHandlerIndex,
			}
			srv.handler.ServeMsg(ctx)

		case "", "raw":
			haveSignature := queryParams.Get("signature")
			if haveSignature == "" {
				errorHandler.ServeError(w, r, errors.New("not found signature query parameter"))
				return
			}
			timestampString := queryParams.Get("timestamp")
			if timestampString == "" {
				errorHandler.ServeError(w, r, errors.New("not found timestamp query parameter"))
				return
			}
			timestamp, err := strconv.ParseInt(timestampString, 10, 64)
			if err != nil {
				err = fmt.Errorf("can not parse timestamp query parameter %q to int64", timestampString)
				errorHandler.ServeError(w, r, err)
				return
			}
			nonce := queryParams.Get("nonce")
			if nonce == "" {
				errorHandler.ServeError(w, r, errors.New("not found nonce query parameter"))
				return
			}

			var token string
			currentToken, lastToken := srv.getToken()
			if currentToken == "" {
				err = errors.New("token was not set for Server, see NewServer function or Server.SetToken method")
				errorHandler.ServeError(w, r, err)
				return
			}
			token = currentToken
			wantSignature := util.Sign(token, timestampString, nonce)
			if !security.SecureCompareString(haveSignature, wantSignature) {
				if lastToken == "" {
					err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature)
					errorHandler.ServeError(w, r, err)
					return
				}
				token = lastToken
				wantSignature = util.Sign(token, timestampString, nonce)
				if !security.SecureCompareString(haveSignature, wantSignature) {
					err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature)
					errorHandler.ServeError(w, r, err)
					return
				}
			} else {
				if lastToken != "" {
					srv.removeLastToken(lastToken)
				}
			}

			msgPlaintext, err := ioutil.ReadAll(r.Body)
			if err != nil {
				errorHandler.ServeError(w, r, err)
				return
			}
			callback.DebugPrintPlainRequestMessage(msgPlaintext)

			var mixedMsg MixedMsg
			if err = xml.Unmarshal(msgPlaintext, &mixedMsg); err != nil {
				errorHandler.ServeError(w, r, err)
				return
			}

			haveToUserName := mixedMsg.ToUserName
			wantToUserName := srv.oriId
			if wantToUserName != "" && !security.SecureCompareString(haveToUserName, wantToUserName) {
				err = fmt.Errorf("the message ToUserName mismatch, have: %s, want: %s",
					haveToUserName, wantToUserName)
				errorHandler.ServeError(w, r, err)
				return
			}

			ctx := &Context{
				ResponseWriter: w,
				Request:        r,

				QueryParams: queryParams,
				EncryptType: encryptType,
				Signature:   haveSignature,
				Timestamp:   timestamp,
				Nonce:       nonce,

				MsgPlaintext: msgPlaintext,
				MixedMsg:     &mixedMsg,

				Token: token,

				handlerIndex: initHandlerIndex,
			}
			srv.handler.ServeMsg(ctx)

		default:
			errorHandler.ServeError(w, r, errors.New("unknown encrypt_type: "+encryptType))
		}

	case "GET": // 验证回调URL是否有效
		haveSignature := queryParams.Get("signature")
		if haveSignature == "" {
			errorHandler.ServeError(w, r, errors.New("not found signature query parameter"))
			return
		}
		timestamp := queryParams.Get("timestamp")
		if timestamp == "" {
			errorHandler.ServeError(w, r, errors.New("not found timestamp query parameter"))
			return
		}
		nonce := queryParams.Get("nonce")
		if nonce == "" {
			errorHandler.ServeError(w, r, errors.New("not found nonce query parameter"))
			return
		}
		echostr := queryParams.Get("echostr")
		if echostr == "" {
			errorHandler.ServeError(w, r, errors.New("not found echostr query parameter"))
			return
		}

		var token string
		currentToken, lastToken := srv.getToken()
		if currentToken == "" {
			err := errors.New("token was not set for Server, see NewServer function or Server.SetToken method")
			errorHandler.ServeError(w, r, err)
			return
		}
		token = currentToken
		wantSignature := util.Sign(token, timestamp, nonce)
		if !security.SecureCompareString(haveSignature, wantSignature) {
			if lastToken == "" {
				err := fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature)
				errorHandler.ServeError(w, r, err)
				return
			}
			token = lastToken
			wantSignature = util.Sign(token, timestamp, nonce)
			if !security.SecureCompareString(haveSignature, wantSignature) {
				err := fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature)
				errorHandler.ServeError(w, r, err)
				return
			}
		} else {
			if lastToken != "" {
				srv.removeLastToken(lastToken)
			}
		}

		io.WriteString(w, echostr)
	}
}