此请求的签名对于 Binance US 无效 API

Signature for this request is not valid error for Binance US API

我在连接到需要身份验证的 Binance API 端点时收到签名无效错误。在 link 之后有一个类似的查询,但我猜它是 Binance 特有的,而且这个问题是 Binance US 特有的。我尝试使用下面 link 中的方法,但它没有用。

Flutter binance api signature

以下是Python代码

import urllib.parse
import hashlib
import hmac
import base64
import requests

api_url = "https://api.binance.us"

# get binanceus signature
def get_binanceus_signature(data, secret):
    postdata = urllib.parse.urlencode(data)
    message = postdata.encode()
    byte_key = bytes(secret, 'UTF-8')
    mac = hmac.new(byte_key, message, hashlib.sha256).hexdigest()
    return mac

# Attaches auth headers and returns results of a POST request
def binanceus_request(uri_path, data, api_key, api_sec):
    headers = {}
    headers['X-MBX-APIKEY'] = api_key
    signature = get_binanceus_signature(data, api_sec) 
    params={**data, "signature": signature}           
    req = requests.get((api_url + uri_path), params=params, headers=headers)
    return req.text

api_key = "vmPUZE6mv9SD5VNHk4HlWFsOr6aKE2zvsw0MuIgwCIPy6utIco14y7Ju91duEh8A"
secret_key = "NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j"

uri_path = "/api/v3/openOrders"
data = {
    "symbol": "BTCUSDT", 
    "timestamp": 1499827319559
}

get_open_order_result = binanceus_request(uri_path, data, api_key, secret_key)

这是完整的飞镖代码。

class BinanceUSRestClient {
  final String timestamp = DateTime.now().millisecondsSinceEpoch.toString();

  Future<http.Response> getResponse({
    required String secret,
    required String apiKey,
    required String path,
    Map<String, dynamic>? queryParams,
  }) async {
    //Header
    Map<String, String> headers = {};
    headers['X-MBX-APIKEY'] = apiKey;

    //Params
    Map<String, dynamic> params = {};
    if (queryParams != null) {
      params.addAll(queryParams);
    }
    params['signature'] = createSignature(secret, queryParams);
    params['timestamp'] = timestamp;

    final Uri uri = Uri.https('api.binance.us', path, params);
    http.Response response = await http.get(
      uri,
      headers: headers,
    );
    return response;
  }

  String createSignature(String secret, Map<String, dynamic>? data) {
    final String jsonString = jsonEncode(data);
    final List<int> message = utf8.encode(jsonString);
    final List<int> key = utf8.encode(secret);
    final List<int> mac = Hmac(sha256, key).convert(message).bytes;
    final String signature = hex.encode(mac);
    return signature;
  }
}

如果有帮助,请点击此处 API documentation。有人可以帮我解决这个错误吗?

终于!!花了几个小时后,shammy12's post 帮我弄清楚出了什么问题。一直以来,我一直在尝试根据 json 格式的参数创建签名。尽管 Binance US 文档中明确指出,

totalParams is defined as the query string concatenated with the request body

我引用的大多数回复都忽略了这一点。即使 recvWindow 参数也与无效签名错误无关。我调试这个问题只需要找出一种将查询格式化为

的方法
String _paramsString = 'timestamp=' + timeStamp.toString();

并且,这是通过使用以下内容实现的:

String _paramsString = Uri(queryParameters: baseParams).query;

这是完整的调试代码。如果有任何问题,请随时告诉我。我重命名了一些变量以提高可读性并避免一些可能的混淆。

import 'dart:convert';

import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;

class BinanceRestClient {
  final int timeStamp = DateTime.now().millisecondsSinceEpoch;

  Future<http.Response> getResponse({
    required String secret,
    required String apiKey,
    required String path,
    Map<String, dynamic>? queryParams,
  }) async {
    //Header
    Map<String, String> headers = {};
    headers['X-MBX-APIKEY'] = apiKey;

    //Base params include the timestamp in milliseconds since epoch
    //and query parameters required for API end point
    Map<String, dynamic> baseParams = {};
    baseParams['timestamp'] = timeStamp.toString();
    if (queryParams != null) {
      baseParams.addAll(queryParams);
    }

    //Total params is the combination of base params and signature.
    Map<String, dynamic> totalParams = baseParams;
    totalParams['signature'] = _createSignature(
      secret: secret,
      baseParams: baseParams,
    );

    final Uri uri = Uri.https('api.binance.us', path, totalParams);
    final http.Response response = await http.get(
      uri,
      headers: headers,
    );
    return response;
  }

  String _createSignature({
    required String secret,
    required Map<String, dynamic> baseParams,
  }) {
    //Important Note: baseParams must be changed to a concatenated string before hashed.
    //Failure to do so will result in signature invalid error
    //
    //i.e., String _paramsString = 'timestamp=' + timeStamp.toString();
    //
    //Following converts a json query to a concatenated string
    final String _paramsString = Uri(queryParameters: baseParams).query;
    final List<int> _message = utf8.encode(_paramsString);
    final List<int> _key = utf8.encode(secret);
    final List<int> _mac = Hmac(sha256, _key).convert(_message).bytes;
    final String _signature = hex.encode(_mac);
    return _signature;
  }
}