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

rn0zuynd  于 2023-04-22  发布在  Flutter
关注(0)|答案(2)|浏览(135)

我在连接到需要身份验证的Binance API端点时收到签名无效错误。在下面的链接中有一个类似的查询,但它是特定于Binance的,我猜这个问题是特定于Binance US的。我试图使用下面链接中的方法,但它不起作用。
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)

这里是完整的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文档。有人能帮助我解决这个错误吗?

mo49yndu

mo49yndu1#

最后!!花了几个小时后,shammy 12的帖子帮助我找出了问题所在。这段时间我一直在尝试基于json格式的参数创建签名。尽管Binance US文档明确指出,
totalParams定义为query stringrequest 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;
  }
}
oewdyzsn

oewdyzsn2#

另一种方式在 dart (Flutter)我如何让它工作使用crypto:^3.0.2和http:^0.13.5

test() async {

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

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

二进制的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;
  }
}

相关问题