【问题标题】:How to produce valid signature at Binance API using Vanilla Javascript: {"code":-2014,"msg":"API-key format invalid."}如何使用 Vanilla Javascript 在 Binance API 生成有效签名:{"code":-2014,"msg":"API-key format invalid."}
【发布时间】:2022-01-21 07:39:32
【问题描述】:

我花了几个小时试图从端点获得正确的响应。为此,我需要发送一个由 SHA-256 函数生成的签名字符串,附加到我发送到服务器的查询字符串中。

我尝试了不同的方法来获取文档中描述的签名:https://developers.binance.com/docs/binance-api/spot/index/#signed-trade-user_data-and-margin-endpoint-security 但似乎没有任何效果。

我尝试了几种使用不同库和函数生成有效签名的方法,但它们都不起作用(我什至尝试使用 Web Crypto API 文档)

我在拨打电话时收到此错误:

{"code":-2014,"msg":"API-key format invalid."}

这是电话:

https://testnet.binance.vision/api/v3/account?timestamp=my_timestamp&signature=my_signature

我猜这是 Fetch 的问题,但在我的应用程序中的其他自定义函数中它不会导致问题。

这是我的代码:

export async function getAccountInfo() {
    const apiSecret = pub.TESTNET_SECRETKEY; // Your secret key

    const timestamp = await serverTimestamp()
        .then(timestamp => {
            return timestamp;
        });

    let signature = sha256(apiSecret, timestamp);

    const testnet = 'https://testnet.binance.vision/api';

    // {{url}}/api/v3/account?timestamp={{timestamp}}&signature={{signature}}

    const fullUrl = testnet + '/v3/account?timestamp=' + timestamp + '&signature=' + signature;

    retrieveInformation(fullUrl);
}

【问题讨论】:

    标签: javascript api cryptography binance binance-api-client


    【解决方案1】:

    我只是在这一行中向哈希函数发送了错误的时间戳字符串:

    let signature = sha256(apiSecret, timestamp); // Keep reading and you'll understand why.
    

    原版JS解决方案

    虽然我使用了依赖项,但我认为这仍然是一个完整的有效解决方案。

    https://www.npmjs.com/package/jhash.js 功能比较简单直接上手。

    问题

    问题出在我发送到哈希函数的查询字符串中。

    正如 Binance API 文档解释的那样,虽然晦涩难懂:

    • 端点使用HMAC SHA256 签名。 HMAC SHA256 签名是 键入HMAC SHA256 操作。使用您的 secretKey 作为键,totalParams 作为 HMAC 操作的值。
    • totalParams 定义为与请求正文连接的查询字符串。

    最后一点让我很困惑。

    现在,解决方案是将正确的字符串 (queryString) 发送到 sha256 函数。 API 的要求是:

    https://testnet.binance.vision/api/v3/account?timestamp=my_timestamp&signature=my_signature
    

    timestamp= 子字符串是我的问题的解决方案。我不得不将那一小段代码发送到 hex_hmac_sha256 函数中,这是 Binance API 所需的格式。

    完整的解决方案。

    async function serverTimestamp() {
        const url = 'https://testnet.binance.vision/api/v3/time';
        const timeServer = await getJson(url);
        return timeServer.serverTime;
    }
    

    不是本地时间,而是时间服务器必须在签名里面发送。这就是问题的解决方案。

    export async function getAccountInfo() {
        const apiSecret = pub.TESTNET_SECRETKEY; // Your secret key
    
        const timestamp = await serverTimestamp()
            .then(timestamp => {
                return timestamp;
            });
    
        const queried_timestamp = 'timestamp=' + timestamp;
    
        // https://www.npmjs.com/package/jhash.js
        let signature = JHash.hex_hmac_sha256(apiSecret, queried_timestamp);
        // let signature = await sha256(apiSecret, queried_timestamp); // This one is not library dependant.
    
        const testnet = 'https://testnet.binance.vision/api';
    
        // {{url}}/api/v3/account?timestamp={{timestamp}}&signature={{signature}}
        const fullUrl = testnet + '/v3/account?timestamp=' + timestamp + '&signature=' + signature; // + '&recvWindow=60000';
        retrieveInformation(fullUrl);
    }
    

    请注意,在以下代码行中,我将 URL 中包含的字符串作为 queryString 发送。

    let signature = JHash.hex_hmac_sha256(apiSecret, queried_timestamp);
    // This is the same line than the one I wrote above,
    // but using another version of the function.
    

    这是引导我走向正确方向的示例:https://developers.binance.com/docs/binance-api/spot/index/#example-1-as-a-request-body

    正如您在官方文档示例中看到的那样,他们回应了用于制作签名的完整 queryString(s)。

    现在,您可能需要更好地理解问题的其他功能:

    async function retrieveInformation(url = null) {
        const apiKey = pub.TESTNET_APIKEY; // Your ApiKey
        let httpHeaders = {
            'Content-Type': 'application/x-www-form-urlencoded',
            'X-MBX-APIKEY': apiKey
        }
    
        let myHeaders = new Headers(httpHeaders);
        var requestOptions = {
            headers: myHeaders
        };
        console.log(url);
        console.log(requestOptions);
        const data = await getJson(url, requestOptions);
        console.log(data);
        return data;
    }
    

    data 显示为以下 JSON 对象:

    {
      "makerCommission": 15,
      "takerCommission": 15,
      "buyerCommission": 0,
      "sellerCommission": 0,
      "canTrade": true,
      "canWithdraw": true,
      "canDeposit": true,
      "updateTime": 123456789,
      "accountType": "SPOT",
      "balances": [
        {
          "asset": "BTC",
          "free": "4723846.89208129",
          "locked": "0.00000000"
        },
        {
          "asset": "LTC",
          "free": "4763368.68006011",
          "locked": "0.00000000"
        }
      ],
      "permissions": [
        "SPOT"
      ]
    }
    

    您可以在 API Binance 文档中看到此处显示的相同信息:https://developers.binance.com/docs/binance-api/spot/index/#account-information-user_data

    我使用的其他功能(这只是对这个答案的补充,您可能会发现它们很有用)

    这是我使用的fetch 函数:

    async function getJson(url = null, requestOptions = null) {
        return fetch(url, requestOptions)
            .then((response) => {
                if (!response.ok) {
                    throw Error(response.statusText);
                } else {
                    const jsoned = response.json();
                    return jsoned;
                    // NOTE:
                    //  response.json().then(data => {
                    //      → do something with your data
                    //  });
                    //
                }
            })
            .catch(function (error) {
                console.log(error);
            });
    }
    

    这是我使用SubtleCrypto Object (Crypto Web API) 上的一些 Mozilla 文档制作的 sha256 函数。它返回的结果与依赖项中的结果相同。

    async function sha256(key, message) {
        
        // Step 1
        // encode as (utf-8) Uint8Array
        const msgUint8_key = new TextEncoder().encode(key);
        // encode as (utf-8) Uint8Array
        const msgUint8_message = new TextEncoder().encode(message);
        
        // Step 2
        const importedKey = await crypto.subtle.importKey('raw', msgUint8_key, {
            name: 'HMAC',
            hash: 'SHA-256'
        }, true, ['sign']);
        
        // Step 3
        const signedKey = await crypto.subtle.sign('HMAC', importedKey, msgUint8_message);
        // convert buffer to byte array
        const hashArray = Array.from(new Uint8Array(signedKey));
        // convert bytes to hex string    
        const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
        return hashHex;
    }
    

    对于那些正在寻找更 100 % Vanilla 解决方案来解决最后一个功能的人:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-24
      • 1970-01-01
      • 1970-01-01
      • 2020-07-09
      相关资源
      最近更新 更多