Skip to content

Commit d26d41c

Browse files
committed
feat(client): add RSA/ECDSA support
1 parent 396357b commit d26d41c

3 files changed

Lines changed: 57 additions & 8 deletions

File tree

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,20 @@ const client = Binance({
5959
This library works in both browsers and Node.js environments:
6060

6161

62+
### RSA/ECDSA support
6263

64+
This library supports RSA and ED25519 keys out of the box. The usage is straightforward, just provide `privateKey` instead of `apiSecret`.
6365

66+
```js
67+
68+
const apiKey = "ZymCbCxu1LiYIW8IcYSbXQOaAtHaeW3ioCU5qFf5QvUyTfP1runCaY8AwzCaoOaq"
69+
const privateKey = "-----BEGIN PRIVATE KEY-----\ndMC4CAfAwafYDK2cwaCIEIa+Ax8dMK50wcIcD0Zdf2jaCDoRdaoc7KaadRUh+aLdt\n-----END PRIVATE KEY-----"
70+
const client = Binance({
71+
privateKey,
72+
apiKey,
73+
})
74+
75+
```
6476

6577
### Proxy Support (Node.js only)
6678

@@ -261,6 +273,7 @@ Following examples will use the `await` form, which requires some configuration
261273
| ----------- | -------- | -------- | -------------------------------------------- |
262274
| apiKey | String | false | Required when making private calls |
263275
| apiSecret | String | false | Required when making private calls |
276+
| privateKey | String | false | Required when using RSA/Ed25519 calls |
264277
| getTime | Function | false | Time generator, defaults to () => Date.now() |
265278
| httpBase | String | false | Changes the default endpoint |
266279
| httpFutures | String | false | Changes the default endpoint |

src/http-client.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import zip from 'lodash.zipobject'
22
import JSONbig from 'json-bigint'
3-
import { createHmacSignature } from './signature'
3+
import { createHmacSignature, createAsymmetricSignature } from './signature'
44

55
// Robust environment detection for Node.js vs Browser
66
const isNode = (() => {
@@ -230,10 +230,10 @@ const keyCall =
230230
* @returns {object} The api response
231231
*/
232232
const privateCall =
233-
({ apiKey, apiSecret, proxy, endpoints, getTime = defaultGetTime, pubCall, testnet }) =>
233+
({ apiKey, apiSecret, privateKey, proxy, endpoints, getTime = defaultGetTime, pubCall, testnet }) =>
234234
(path, data = {}, method = 'GET', noData, noExtra) => {
235-
if (!apiKey || !apiSecret) {
236-
throw new Error('You need to pass an API key and secret to make authenticated calls.')
235+
if (!apiKey || (!apiSecret && !privateKey)) {
236+
throw new Error('You need to pass an API key and secret/privateKey to make authenticated calls.')
237237
}
238238

239239
return (
@@ -250,10 +250,23 @@ const privateCall =
250250
const dataToSign = queryString.substr(1)
251251

252252
// Create signature (async in browser, sync in Node.js)
253-
return createHmacSignature(dataToSign, apiSecret).then(signature => ({
254-
timestamp,
255-
signature,
256-
}))
253+
if (apiSecret) {
254+
return createHmacSignature(dataToSign, apiSecret).then(signature => ({
255+
timestamp,
256+
signature,
257+
}))
258+
} else if (privateKey) {
259+
const sig = createAsymmetricSignature(dataToSign, privateKey)
260+
// .then(signature => ({
261+
// timestamp,
262+
// signature,
263+
// }))
264+
return {
265+
timestamp,
266+
signature: sig,
267+
}
268+
}
269+
257270
})
258271
.then(({ timestamp, signature }) => {
259272
const newData = noExtra ? data : { ...data, timestamp, signature }

src/signature.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { sign } from 'crypto'
2+
13
// Robust environment detection for Node.js vs Browser
24
const isNode = (() => {
35
// Check for Node.js specific features
@@ -59,3 +61,24 @@ export const createHmacSignature = async (data, secret) => {
5961
.join('')
6062
/* eslint-enable no-undef */
6163
}
64+
65+
export const createAsymmetricSignature = (data, privateKey) => {
66+
// Handles RSA and ECDASA (Ed25519) private keys
67+
const privateKeyObj = { key: privateKey };
68+
const keyObject = nodeCrypto.createPrivateKey(privateKeyObj)
69+
70+
let signature = '';
71+
72+
if (privateKey.length > 120) {
73+
// RSA key
74+
signature = nodeCrypto
75+
.sign('RSA-SHA256', Buffer.from(data), keyObject)
76+
.toString('base64');
77+
// if (encode) signature = encodeURIComponent(signature);
78+
} else {
79+
80+
// Ed25519 key
81+
signature = nodeCrypto.sign(null, Buffer.from(data), keyObject).toString('base64');
82+
}
83+
return signature;
84+
}

0 commit comments

Comments
 (0)