如何使用 Vanilla Javascript 在 Binance API 生成有效签名:{"code":-2014,"msg":"API-key format invalid."}
How to produce valid signature at Binance API using Vanilla Javascript: {"code":-2014,"msg":"API-key format invalid."}
我花了几个小时试图从端点获得正确的响应。为此,我需要发送一个由 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);
}
我只是将不正确的时间戳字符串发送到散列函数,在这一行中:
let signature = sha256(apiSecret, timestamp); // Keep reading and you'll understand why.
香草 JS 解决方案
虽然我使用了依赖项,但我认为这仍然是一个完整有效的解决方案。
https://www.npmjs.com/package/jhash.js功能简单易用
问题
问题出在我发送到哈希函数的查询字符串中。
正如 Binance API 文档解释的那样,虽然含糊不清:
- Endpoints use
HMAC SHA256
signatures. The HMAC SHA256
signature is a
keyed HMAC SHA256
operation. Use your secretKey
as the key and totalParams
as the value for the HMAC operation.
totalParams
is defined as the query string concatenated with the request body.
最后一点让我很疑惑
现在,解决方案是将正确的字符串 (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 中包含的字符串作为查询字符串发送。
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
正如您在官方文档示例中所见,他们回显了用于制作签名的完整查询字符串。
现在,您可能需要更好地理解问题的其他函数:
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 函数。它 returns 与来自依赖项的结果相同。
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% 香草解决方案的人:
- Are there any SHA-256 javascript implementations that are generally considered trustworthy?
- JavaScript SHA-256 demo
我花了几个小时试图从端点获得正确的响应。为此,我需要发送一个由 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);
}
我只是将不正确的时间戳字符串发送到散列函数,在这一行中:
let signature = sha256(apiSecret, timestamp); // Keep reading and you'll understand why.
香草 JS 解决方案
虽然我使用了依赖项,但我认为这仍然是一个完整有效的解决方案。
https://www.npmjs.com/package/jhash.js功能简单易用
问题
问题出在我发送到哈希函数的查询字符串中。
正如 Binance API 文档解释的那样,虽然含糊不清:
- Endpoints use
HMAC SHA256
signatures. TheHMAC SHA256
signature is a keyedHMAC SHA256
operation. Use yoursecretKey
as the key andtotalParams
as the value for the HMAC operation.totalParams
is defined as the query string concatenated with the request body.
最后一点让我很疑惑
现在,解决方案是将正确的字符串 (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 中包含的字符串作为查询字符串发送。
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
正如您在官方文档示例中所见,他们回显了用于制作签名的完整查询字符串。
现在,您可能需要更好地理解问题的其他函数:
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 函数。它 returns 与来自依赖项的结果相同。
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% 香草解决方案的人:
- Are there any SHA-256 javascript implementations that are generally considered trustworthy?
- JavaScript SHA-256 demo