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

发布于 2025-01-11 22:51:36 字数 2731 浏览 1 评论 0原文

我在连接到需要身份验证的 Binance API 端点时收到签名无效错误。以下链接中有一个类似的查询,但我猜它是特定于币安的,而这个问题是特定于币安美国的。我尝试使用下面链接中的方法,但没有成功。

Flutter binance api 签名

以下是 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)

这是完整的 dart 代码。

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 文档。有人可以帮助我解决该错误吗?

I am getting a signature not valid error while connecting to Binance API endpoints that require authentication. There is a similar query in following link however it is specific to Binance, I guess and this question is specific to Binance US. I tried to use the methods in below link though and it didn't work.

Flutter binance api signature

Following is the Python code

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)

Here is the full dart code.

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;
  }
}

If it helps, here is the API documentation. Could someone help me in resolving the error?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

请你别敷衍 2025-01-18 22:51:36

最后!!花了几个小时后,shammy12 的帖子帮助我弄清楚了什么出了问题。一直以来,我一直在尝试基于 json 格式的参数创建签名。尽管币安美国文档明确指出,

totalParams 定义为与以下内容连接的查询字符串
请求正文

在我引用的大多数响应中都忽略了这一点。即使 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;
  }
}

Finally!! After spending hours, shammy12's post helped me to figure out what went wrong. All this time I have been trying to create a signature based on a parameter in json format. Although Binance US documentation clearly stated,

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

this was overlooked in most of the responses I referenced. Even recvWindow parameter has nothing to do with the invalid signature error. All it took me to debug this issue was figure out a way to format the query as,

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

And, this is achieved by using following:

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

Here is the full debugged code. Please feel free to let me know if there is anything. I have renamed some variables for better readability and avoid some possible confusions.

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;
  }
}
腹黑女流氓 2025-01-18 22:51:36

在 Dart (Flutter) 中,我如何使用

test() async {

   String api = 'timestamp=${DateTime.now().millisecondsSinceEpoch}';

   var res = ApiBinance.get(context, ApiBinance.account, api);
}

binance 的 crypto: ^3.0.2 和 http: ^0.13.5 包 Api 类让它工作

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

class ApiBinance {

   static var base = 'https://testnet.binance.vision';
   static var account = '$base/api/v3/account?';

   static Map<String, String> headers() {

      var headers = {
        'X-MBX-APIKEY': 'API KEY',
        'Content-Type': 'application/json',
      };

      return headers;
   }

   static String signature(String queryParams) {

      List<int> messageBytes = utf8.encode(queryParams);
      List<int> key = utf8.encode('SECRET');
      Hmac hash = Hmac(sha256, key);
      Digest digest = hash.convert(messageBytes);
      // String signature = base64.encode(digest.bytes); Does not work
      // String signature = sha256.convert(digest.bytes).toString(); // does not work

      return '$digest'; // I return directly digest and it works

   }

   static Future<String?> get(BuildContext context, url, String queryParams) async {

      String sign = signature(queryParams);

      var uri = Uri.parse('$url$queryParams&signature=$sign');

      var response = await http.get(
        uri,
        headers: headers(),
      );

      if (response.statusCode == 200) {
        print(response.body);
      } else {
        print('Request failed with status: ${response.statusCode}. 
        ${response.body}');
      }

      return response.body;
  }
}

Another way in Dart (Flutter) how i got it to work using crypto: ^3.0.2 and http: ^0.13.5 package

test() async {

   String api = 'timestamp=${DateTime.now().millisecondsSinceEpoch}';

   var res = ApiBinance.get(context, ApiBinance.account, api);
}

Api class of binance

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

class ApiBinance {

   static var base = 'https://testnet.binance.vision';
   static var account = '$base/api/v3/account?';

   static Map<String, String> headers() {

      var headers = {
        'X-MBX-APIKEY': 'API KEY',
        'Content-Type': 'application/json',
      };

      return headers;
   }

   static String signature(String queryParams) {

      List<int> messageBytes = utf8.encode(queryParams);
      List<int> key = utf8.encode('SECRET');
      Hmac hash = Hmac(sha256, key);
      Digest digest = hash.convert(messageBytes);
      // String signature = base64.encode(digest.bytes); Does not work
      // String signature = sha256.convert(digest.bytes).toString(); // does not work

      return '$digest'; // I return directly digest and it works

   }

   static Future<String?> get(BuildContext context, url, String queryParams) async {

      String sign = signature(queryParams);

      var uri = Uri.parse('$url$queryParams&signature=$sign');

      var response = await http.get(
        uri,
        headers: headers(),
      );

      if (response.statusCode == 200) {
        print(response.body);
      } else {
        print('Request failed with status: ${response.statusCode}. 
        ${response.body}');
      }

      return response.body;
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文