Home > Blockchain >  Signature for this request is not valid error for Binance US API
Signature for this request is not valid error for Binance US API

Time:03-07

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?

CodePudding user response:

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