Websocket API signatures broken?

I am struggling with authenticating my websocket connection using the session.logon call. All my attempts are getting the {code: -1022, msg: 'Signature for this request is not valid.'} error.

I have successfully implemented the signature signing code in javascript running in my browser, according to this example:

With the same inputs I get the correct signature.

But when I try the call to session.logon with a properly generated timestamp (and request string) and calculate the signature using my own secretKey, it fails with the error as described.

Diving into all the details, I notice that with the correct signature example, the signing uses HMAC and SHA-256.

However. looking into the websocket api docs here:

I notice the following comment: “Session Authentication. Note: Only Ed25519 keys are supported for this feature.”. If this is correct, the binance signature example is useless for this.

Regardless, I have gone down the rabbit hole and generated the proper private and public Ed25519 key pair, and created a separate API token using Ed25519 (PEM encoded, as Binance insist on). To get this to work in Chrome, I needed to enable “experimental platform features”. In addition to this, there are ways of getting lost in how the secret key is encoded and imported in the browser, and how to tell the browser to use this key for signing.

I’m pasting in some of the code that generates the correct signature for the simpler non-Ed25519 example, where I’ve also commented out some tests I’ve done to get it to use the Ed25519 stuff, but I am still not successful.

Has anybody been able to succeed with this (the websocket session.logon using Ed25519) call at all? Care to share the details on signing etc? Has anybody succeeded using the websocket APIs without the session.logon call (signing every request I guess), and did it then work with a “normal” key (non-Ed25519)?

And finally, it seems the URL for the Futures API endpoint is moving around a bit as well, so in theory I guess I might be calling some wrong endpoint as well (although getting the signature error indicates I may not be that wrong). The URL I’m calling is:

wss://ws-fapi.binance.com/ws-fapi/v1

Thanks.

Code below:

const encoder = new TextEncoder();
const decoder = new TextDecoder('utf-8');

async function sha256(key, message) {

  // Step 1
  // encode as (utf-8) Uint8Array
  const msgUint8_key = encoder.encode(key);
  //const msgUint8_key = base64toUint8(key);
  //console.log({msgUint8_key});
  // encode as (utf-8) Uint8Array
  const msgUint8_message = encoder.encode(message);

    // Step 2
  const importedKey = await crypto.subtle.importKey('raw', msgUint8_key, {
    name: 'HMAC',
    //name: 'Ed25519',
    hash: 'SHA-256'
  }, true, ['sign']);

  // Step 3
  const signedKey = await crypto.subtle.sign('HMAC', importedKey, msgUint8_message);
  
  //const signedKey = await crypto.subtle.sign('Ed25519', 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;
}

FWIW, I tested account.status as well as it has no note about Ed25519 and uses the same parameter (timestamp). Still can not get past the signature error.

I’ve also tested session.status which doesn’t require a signature. Works fine.

I’ve also verified that my own code generates the same signatures for my actual requests using my live key (HMAC) as in Binance’s own Node.js example here:

I’m seriously struggling to believe the websocket signature stuff on Binance’s side is even working.

I also verified my Ed25519 signing attempts using openssl for the timestamp=1578963600000 example (inside my text.txt file, without any newline) by making sure my own code using the same private key and input data generates the identical signature as:

openssl pkeyutl -in test.txt -rawin -sign -inkey .ssh/ed25519-priv.pem -hexdump

Needless to say by now, the Binance API server isn’t accepting these kinds of signatures either.

If anyone is having success with these signatures, just knowing you do would be helpful.

Another datapoint. I managed to successfully do HTTPS “rest style” signed API calls successfully using Ed25519 keys and signatures. For testing I found two API calls that only require the timestamp parameter. Using the same API call and signature building code, my test now outputs the following lines:

https://fapi.binance.com/fapi/v2/account
400 {"code":-1022,"msg":"Signature for this request is not valid...
https://api.binance.com/api/v3/account
200 {"makerCommission":10,"takerCommission":10,"buyerCommission"...

In short, it first tries to call the fapi end point with a signed request that fails. The second similar call to the regular api end point demonstrates it working. This is just regular HTTPS rest style API calls. But it is another indication that fapi isn’t quite behaving as expected (according to it’s own documentation).

Hi marius!

I’am also have problems with wss://ws-fapi.binance.com/ws-fapi/v1 with ed25519 keys. Same error as you have.

But it works fine with wss://ws-api.binance.com/ws-api/v3 and also REST Spot endpoints (note that you should sort params alphabetticaly before signing)

According docs rest endpoint https://fapi.binance.com does not work with ed25519 (no mentions in futures spot docs) so you better test session.logon at ws-fapi.binance.com/ws-fapi/v1 websocket connection. (and with Spot websocket API - this should work).

I suppose we need bring attention of Binance API developers to this problem. How to do it right? I am new here.

edit reason: switch wss://ws-api.binance.com/ws-api/v3 and wss://ws-fapi.binance.com/ws-fapi/v1

Thank you for confirming you were having success. I took another stab at it and finally succeeded.

While doing this work I’ve been carefully managing examples of both regular REST style API calls and Websocket calls. As written, I’ve made sure that for the same inputs, the same signatures are being generated. However this is useless for testing websockets, since the parameter apiKey needs to be part of the message being signed for websockets, but should not be part of the message being signed for regular REST API calls.

For REST API calls the apiKey is sent as one of the request headers. So for all those examples showing a simple timestamp=123456790000 before signing, they will never work for websockets. For websockets the “minimum” example for a minimal API call should be apiKey=YOURAPIKEY&timestamp=123457890000. Signatures generated should be base64 encoded.

I also tried and succeeded with the same for the websocket URL you are struggling with (wss://ws-api.binance.com/ws-api/v3). So I suspect something else is wrong at your end related to this.

Generally I can comment that keeping track of all the keys and encodings is quite hard, and at least if you are using javascript, the exact code needed for server side code (Node.js) and client side is quite different.

I believe the browser based base64 encoding routines (atob/btoa) are also doing “bad” stuff if you are not careful with what you are feeding it (they expect a string, and take some liberties if you just feed it a typed buffer or similar).

Sorry I made a huge mistake in my original message (and I fixed it).

I have issues with wss://ws-fapi.binance.com/ws-fapi/v1 (futures) and no issues with wss://ws-api.binance.com/ws-api/v3 (spot).

I have made exact session.logon requests to both endpoints with the same key/timestamp/signature. It works for spot but not for futures. (everything is the same! futures is allowed in api key preferences)

I wonder if you can make a successful signed request to wss://ws-fapi.binance.com/ws-fapi/v1

My code now works fine with both wss://ws-fapi and wss://ws-api. For instance the session.logon succeeds with both endpoints, and I have verified this by ONLY changing the websocket url. You have verified that the only difference in your case is the wss url’s? If so, and assuming the api key have the proper permissions, it should work in either both or none (assuming you use a method that is supported by both) just by changing the wss url.

You could also manually inspect the generated signature for both cases with a hard coded (non changing) timestamp. Then the signatures for both cases (calls to fapi and api) should be identical.

Yes, I have just changed ws-api.binance.com/ws-api/v3 to ws-fapi.binance.com/ws-fapi/v1 and got error “Signature for this request is not valid.”

I’ve made parallel requests with same timestamp (and same signature!) to both urls: fist is working and second is not.

If you have no errors on wss://ws-fapi.binance.com/ws-fapi/v1 with same approach then I dont’t know what else to do. We can’t reach the devs, support can only point to the documentation.

Will try to change key pair.

Thanks!

UPDATE: with new API Key result is the same.

Did you verify that you are generating identical signatures in your two test cases (assuming you use an API call which only requires timestamp, meaning you generate a signature with apiKey + timestamp)? If the signatures are not identical you should be able to figure it out. If they ARE identical I doubt you will get a signature fail.