Skip to content

Commit c2c4643

Browse files
authored
feature: support signature key Rsa and ed25519 (#579)
1 parent 1ba84a6 commit c2c4643

File tree

5 files changed

+159
-24
lines changed

5 files changed

+159
-24
lines changed

v2/client.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package binance
33
import (
44
"bytes"
55
"context"
6-
"crypto/hmac"
7-
"crypto/sha256"
86
"crypto/tls"
97
"fmt"
108
"io/ioutil"
@@ -323,6 +321,7 @@ func NewClient(apiKey, secretKey string) *Client {
323321
return &Client{
324322
APIKey: apiKey,
325323
SecretKey: secretKey,
324+
KeyType: common.KeyTypeHmac,
326325
BaseURL: getAPIEndpoint(),
327326
UserAgent: "Binance/golang",
328327
HTTPClient: http.DefaultClient,
@@ -343,6 +342,7 @@ func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client {
343342
return &Client{
344343
APIKey: apiKey,
345344
SecretKey: secretKey,
345+
KeyType: common.KeyTypeHmac,
346346
BaseURL: getAPIEndpoint(),
347347
UserAgent: "Binance/golang",
348348
HTTPClient: &http.Client{
@@ -373,6 +373,7 @@ type doFunc func(req *http.Request) (*http.Response, error)
373373
type Client struct {
374374
APIKey string
375375
SecretKey string
376+
KeyType string
376377
BaseURL string
377378
UserAgent string
378379
HTTPClient *http.Client
@@ -419,16 +420,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) {
419420
if r.secType == secTypeAPIKey || r.secType == secTypeSigned {
420421
header.Set("X-MBX-APIKEY", c.APIKey)
421422
}
422-
423+
kt := c.KeyType
424+
if kt == "" {
425+
kt = common.KeyTypeHmac
426+
}
427+
sf, err := common.SignFunc(kt)
428+
if err != nil {
429+
return err
430+
}
423431
if r.secType == secTypeSigned {
424432
raw := fmt.Sprintf("%s%s", queryString, bodyString)
425-
mac := hmac.New(sha256.New, []byte(c.SecretKey))
426-
_, err = mac.Write([]byte(raw))
433+
sign, err := sf(c.SecretKey, raw)
427434
if err != nil {
428435
return err
429436
}
430437
v := url.Values{}
431-
v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil))))
438+
v.Set(signatureKey, *sign)
432439
if queryString == "" {
433440
queryString = v.Encode()
434441
} else {

v2/common/sign.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package common
2+
3+
import (
4+
"crypto"
5+
"crypto/ed25519"
6+
"crypto/hmac"
7+
"crypto/rand"
8+
"crypto/rsa"
9+
"crypto/sha256"
10+
"crypto/x509"
11+
"encoding/base64"
12+
"encoding/pem"
13+
"errors"
14+
"fmt"
15+
)
16+
17+
const (
18+
KeyTypeHmac = "HMAC"
19+
KeyTypeRsa = "RSA"
20+
KeyTypeEd25519 = "ED25519"
21+
)
22+
23+
func SignFunc(keyType string) (func(string, string) (*string, error), error) {
24+
switch {
25+
case keyType == KeyTypeHmac:
26+
return Hmac, nil
27+
case keyType == KeyTypeRsa:
28+
return Rsa, nil
29+
case keyType == KeyTypeEd25519:
30+
return Ed25519, nil
31+
default:
32+
return nil, fmt.Errorf("unsupported keyType=%s", keyType)
33+
}
34+
}
35+
36+
func Hmac(secretKey string, data string) (*string, error) {
37+
mac := hmac.New(sha256.New, []byte(secretKey))
38+
_, err := mac.Write([]byte(data))
39+
if err != nil {
40+
return nil, err
41+
}
42+
encodeData := fmt.Sprintf("%x", (mac.Sum(nil)))
43+
return &encodeData, nil
44+
}
45+
46+
func Rsa(secretKey string, data string) (*string, error) {
47+
block, _ := pem.Decode([]byte(secretKey))
48+
if block == nil {
49+
return nil, errors.New("Rsa pem.Decode failed, invalid pem format secretKey")
50+
}
51+
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
52+
if err != nil {
53+
return nil, fmt.Errorf("Rsa ParsePKCS8PrivateKey failed, error=%v", err.Error())
54+
}
55+
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
56+
if !ok {
57+
return nil, fmt.Errorf("Rsa convert PrivateKey failed")
58+
}
59+
hashed := sha256.Sum256([]byte(data))
60+
signature, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey, crypto.SHA256, hashed[:])
61+
if err != nil {
62+
return nil, err
63+
}
64+
encodedSignature := base64.StdEncoding.EncodeToString(signature)
65+
return &encodedSignature, nil
66+
}
67+
68+
func Ed25519(secretKey string, data string) (*string, error) {
69+
block, _ := pem.Decode([]byte(secretKey))
70+
if block == nil {
71+
return nil, fmt.Errorf("Ed25519 pem.Decode failed, invalid pem format secretKey")
72+
}
73+
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
74+
if err != nil {
75+
return nil, fmt.Errorf("Ed25519 call ParsePKCS8PrivateKey failed, error=%v", err.Error())
76+
}
77+
ed25519PrivateKey, ok := privateKey.(ed25519.PrivateKey)
78+
if !ok {
79+
return nil, fmt.Errorf("Ed25519 convert PrivateKey failed")
80+
}
81+
pk := ed25519.PrivateKey(ed25519PrivateKey)
82+
signature := ed25519.Sign(pk, []byte(data))
83+
encodedSignature := base64.StdEncoding.EncodeToString(signature)
84+
return &encodedSignature, nil
85+
}

v2/delivery/client.go

+35-6
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ package delivery
33
import (
44
"bytes"
55
"context"
6-
"crypto/hmac"
7-
"crypto/sha256"
6+
"crypto/tls"
87
"encoding/json"
98
"fmt"
109
"io/ioutil"
@@ -190,19 +189,43 @@ func NewClient(apiKey, secretKey string) *Client {
190189
return &Client{
191190
APIKey: apiKey,
192191
SecretKey: secretKey,
192+
KeyType: common.KeyTypeHmac,
193193
BaseURL: getApiEndpoint(),
194194
UserAgent: "Binance/golang",
195195
HTTPClient: http.DefaultClient,
196196
Logger: log.New(os.Stderr, "Binance-golang ", log.LstdFlags),
197197
}
198198
}
199199

200+
func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client {
201+
proxy, err := url.Parse(proxyUrl)
202+
if err != nil {
203+
log.Fatal(err)
204+
}
205+
tr := &http.Transport{
206+
Proxy: http.ProxyURL(proxy),
207+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
208+
}
209+
return &Client{
210+
APIKey: apiKey,
211+
SecretKey: secretKey,
212+
KeyType: common.KeyTypeHmac,
213+
BaseURL: getApiEndpoint(),
214+
UserAgent: "Binance/golang",
215+
HTTPClient: &http.Client{
216+
Transport: tr,
217+
},
218+
Logger: log.New(os.Stderr, "Binance-golang ", log.LstdFlags),
219+
}
220+
}
221+
200222
type doFunc func(req *http.Request) (*http.Response, error)
201223

202224
// Client define API client
203225
type Client struct {
204226
APIKey string
205227
SecretKey string
228+
KeyType string
206229
BaseURL string
207230
UserAgent string
208231
HTTPClient *http.Client
@@ -249,16 +272,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) {
249272
if r.secType == secTypeAPIKey || r.secType == secTypeSigned {
250273
header.Set("X-MBX-APIKEY", c.APIKey)
251274
}
252-
275+
kt := c.KeyType
276+
if kt == "" {
277+
kt = common.KeyTypeHmac
278+
}
279+
sf, err := common.SignFunc(kt)
280+
if err != nil {
281+
return err
282+
}
253283
if r.secType == secTypeSigned {
254284
raw := fmt.Sprintf("%s%s", queryString, bodyString)
255-
mac := hmac.New(sha256.New, []byte(c.SecretKey))
256-
_, err = mac.Write([]byte(raw))
285+
sign, err := sf(c.SecretKey, raw)
257286
if err != nil {
258287
return err
259288
}
260289
v := url.Values{}
261-
v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil))))
290+
v.Set(signatureKey, *sign)
262291
if queryString == "" {
263292
queryString = v.Encode()
264293
} else {

v2/futures/client.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package futures
33
import (
44
"bytes"
55
"context"
6-
"crypto/hmac"
7-
"crypto/sha256"
86
"crypto/tls"
97
"encoding/json"
108
"fmt"
@@ -205,6 +203,7 @@ func NewClient(apiKey, secretKey string) *Client {
205203
return &Client{
206204
APIKey: apiKey,
207205
SecretKey: secretKey,
206+
KeyType: common.KeyTypeHmac,
208207
BaseURL: getApiEndpoint(),
209208
UserAgent: "Binance/golang",
210209
HTTPClient: http.DefaultClient,
@@ -225,6 +224,7 @@ func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client {
225224
return &Client{
226225
APIKey: apiKey,
227226
SecretKey: secretKey,
227+
KeyType: common.KeyTypeHmac,
228228
BaseURL: getApiEndpoint(),
229229
UserAgent: "Binance/golang",
230230
HTTPClient: &http.Client{
@@ -240,6 +240,7 @@ type doFunc func(req *http.Request) (*http.Response, error)
240240
type Client struct {
241241
APIKey string
242242
SecretKey string
243+
KeyType string
243244
BaseURL string
244245
UserAgent string
245246
HTTPClient *http.Client
@@ -286,16 +287,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) {
286287
if r.secType == secTypeAPIKey || r.secType == secTypeSigned {
287288
header.Set("X-MBX-APIKEY", c.APIKey)
288289
}
289-
290+
kt := c.KeyType
291+
if kt == "" {
292+
kt = common.KeyTypeHmac
293+
}
294+
sf, err := common.SignFunc(kt)
295+
if err != nil {
296+
return err
297+
}
290298
if r.secType == secTypeSigned {
291299
raw := fmt.Sprintf("%s%s", queryString, bodyString)
292-
mac := hmac.New(sha256.New, []byte(c.SecretKey))
293-
_, err = mac.Write([]byte(raw))
300+
sign, err := sf(c.SecretKey, raw)
294301
if err != nil {
295302
return err
296303
}
297304
v := url.Values{}
298-
v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil))))
305+
v.Set(signatureKey, *sign)
299306
if queryString == "" {
300307
queryString = v.Encode()
301308
} else {

v2/options/client.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package options
33
import (
44
"bytes"
55
"context"
6-
"crypto/hmac"
7-
"crypto/sha256"
86
"crypto/tls"
97
"encoding/json"
108
"fmt"
@@ -208,6 +206,7 @@ func NewClient(apiKey, secretKey string) *Client {
208206
return &Client{
209207
APIKey: apiKey,
210208
SecretKey: secretKey,
209+
KeyType: common.KeyTypeHmac,
211210
BaseURL: getApiEndpoint(),
212211
UserAgent: "Binance/golang",
213212
HTTPClient: http.DefaultClient,
@@ -228,6 +227,7 @@ func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client {
228227
return &Client{
229228
APIKey: apiKey,
230229
SecretKey: secretKey,
230+
KeyType: common.KeyTypeHmac,
231231
BaseURL: getApiEndpoint(),
232232
UserAgent: "Binance/golang",
233233
HTTPClient: &http.Client{
@@ -243,6 +243,7 @@ type doFunc func(req *http.Request) (*http.Response, error)
243243
type Client struct {
244244
APIKey string
245245
SecretKey string
246+
KeyType string
246247
BaseURL string
247248
UserAgent string
248249
HTTPClient *http.Client
@@ -289,16 +290,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) {
289290
if r.secType == secTypeAPIKey || r.secType == secTypeSigned {
290291
header.Set("X-MBX-APIKEY", c.APIKey)
291292
}
292-
293+
kt := c.KeyType
294+
if kt == "" {
295+
kt = common.KeyTypeHmac
296+
}
297+
sf, err := common.SignFunc(kt)
298+
if err != nil {
299+
return err
300+
}
293301
if r.secType == secTypeSigned {
294302
raw := fmt.Sprintf("%s%s", queryString, bodyString)
295-
mac := hmac.New(sha256.New, []byte(c.SecretKey))
296-
_, err = mac.Write([]byte(raw))
303+
sign, err := sf(c.SecretKey, raw)
297304
if err != nil {
298305
return err
299306
}
300307
v := url.Values{}
301-
v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil))))
308+
v.Set(signatureKey, *sign)
302309
if queryString == "" {
303310
queryString = v.Encode()
304311
} else {

0 commit comments

Comments
 (0)