From 67c0502660aa5f667833a39a0f74e4efcf6f65ee Mon Sep 17 00:00:00 2001 From: xieyongqi <657023321@qq.com> Date: Fri, 21 Jun 2024 14:57:54 +0800 Subject: [PATCH] feature: support signature key Rsa and ed25519 --- v2/client.go | 19 +++++++--- v2/common/sign.go | 85 +++++++++++++++++++++++++++++++++++++++++++ v2/delivery/client.go | 41 ++++++++++++++++++--- v2/futures/client.go | 19 +++++++--- v2/options/client.go | 19 +++++++--- 5 files changed, 159 insertions(+), 24 deletions(-) create mode 100644 v2/common/sign.go diff --git a/v2/client.go b/v2/client.go index 57848d34..99f30018 100644 --- a/v2/client.go +++ b/v2/client.go @@ -3,8 +3,6 @@ package binance import ( "bytes" "context" - "crypto/hmac" - "crypto/sha256" "crypto/tls" "fmt" "io/ioutil" @@ -323,6 +321,7 @@ func NewClient(apiKey, secretKey string) *Client { return &Client{ APIKey: apiKey, SecretKey: secretKey, + KeyType: common.KeyTypeHmac, BaseURL: getAPIEndpoint(), UserAgent: "Binance/golang", HTTPClient: http.DefaultClient, @@ -343,6 +342,7 @@ func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client { return &Client{ APIKey: apiKey, SecretKey: secretKey, + KeyType: common.KeyTypeHmac, BaseURL: getAPIEndpoint(), UserAgent: "Binance/golang", HTTPClient: &http.Client{ @@ -373,6 +373,7 @@ type doFunc func(req *http.Request) (*http.Response, error) type Client struct { APIKey string SecretKey string + KeyType string BaseURL string UserAgent string HTTPClient *http.Client @@ -419,16 +420,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) { if r.secType == secTypeAPIKey || r.secType == secTypeSigned { header.Set("X-MBX-APIKEY", c.APIKey) } - + kt := c.KeyType + if kt == "" { + kt = common.KeyTypeHmac + } + sf, err := common.SignFunc(kt) + if err != nil { + return err + } if r.secType == secTypeSigned { raw := fmt.Sprintf("%s%s", queryString, bodyString) - mac := hmac.New(sha256.New, []byte(c.SecretKey)) - _, err = mac.Write([]byte(raw)) + sign, err := sf(c.SecretKey, raw) if err != nil { return err } v := url.Values{} - v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil)))) + v.Set(signatureKey, *sign) if queryString == "" { queryString = v.Encode() } else { diff --git a/v2/common/sign.go b/v2/common/sign.go new file mode 100644 index 00000000..47bc5bdb --- /dev/null +++ b/v2/common/sign.go @@ -0,0 +1,85 @@ +package common + +import ( + "crypto" + "crypto/ed25519" + "crypto/hmac" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" + "fmt" +) + +const ( + KeyTypeHmac = "HMAC" + KeyTypeRsa = "RSA" + KeyTypeEd25519 = "ED25519" +) + +func SignFunc(keyType string) (func(string, string) (*string, error), error) { + switch { + case keyType == KeyTypeHmac: + return Hmac, nil + case keyType == KeyTypeRsa: + return Rsa, nil + case keyType == KeyTypeEd25519: + return Ed25519, nil + default: + return nil, fmt.Errorf("unsupported keyType=%s", keyType) + } +} + +func Hmac(secretKey string, data string) (*string, error) { + mac := hmac.New(sha256.New, []byte(secretKey)) + _, err := mac.Write([]byte(data)) + if err != nil { + return nil, err + } + encodeData := fmt.Sprintf("%x", (mac.Sum(nil))) + return &encodeData, nil +} + +func Rsa(secretKey string, data string) (*string, error) { + block, _ := pem.Decode([]byte(secretKey)) + if block == nil { + return nil, errors.New("Rsa pem.Decode failed, invalid pem format secretKey") + } + privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("Rsa ParsePKCS8PrivateKey failed, error=%v", err.Error()) + } + rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("Rsa convert PrivateKey failed") + } + hashed := sha256.Sum256([]byte(data)) + signature, err := rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey, crypto.SHA256, hashed[:]) + if err != nil { + return nil, err + } + encodedSignature := base64.StdEncoding.EncodeToString(signature) + return &encodedSignature, nil +} + +func Ed25519(secretKey string, data string) (*string, error) { + block, _ := pem.Decode([]byte(secretKey)) + if block == nil { + return nil, fmt.Errorf("Ed25519 pem.Decode failed, invalid pem format secretKey") + } + privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("Ed25519 call ParsePKCS8PrivateKey failed, error=%v", err.Error()) + } + ed25519PrivateKey, ok := privateKey.(ed25519.PrivateKey) + if !ok { + return nil, fmt.Errorf("Ed25519 convert PrivateKey failed") + } + pk := ed25519.PrivateKey(ed25519PrivateKey) + signature := ed25519.Sign(pk, []byte(data)) + encodedSignature := base64.StdEncoding.EncodeToString(signature) + return &encodedSignature, nil +} diff --git a/v2/delivery/client.go b/v2/delivery/client.go index fecab1c9..bdec1d54 100644 --- a/v2/delivery/client.go +++ b/v2/delivery/client.go @@ -3,8 +3,7 @@ package delivery import ( "bytes" "context" - "crypto/hmac" - "crypto/sha256" + "crypto/tls" "encoding/json" "fmt" "io/ioutil" @@ -190,6 +189,7 @@ func NewClient(apiKey, secretKey string) *Client { return &Client{ APIKey: apiKey, SecretKey: secretKey, + KeyType: common.KeyTypeHmac, BaseURL: getApiEndpoint(), UserAgent: "Binance/golang", HTTPClient: http.DefaultClient, @@ -197,12 +197,35 @@ func NewClient(apiKey, secretKey string) *Client { } } +func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client { + proxy, err := url.Parse(proxyUrl) + if err != nil { + log.Fatal(err) + } + tr := &http.Transport{ + Proxy: http.ProxyURL(proxy), + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + return &Client{ + APIKey: apiKey, + SecretKey: secretKey, + KeyType: common.KeyTypeHmac, + BaseURL: getApiEndpoint(), + UserAgent: "Binance/golang", + HTTPClient: &http.Client{ + Transport: tr, + }, + Logger: log.New(os.Stderr, "Binance-golang ", log.LstdFlags), + } +} + type doFunc func(req *http.Request) (*http.Response, error) // Client define API client type Client struct { APIKey string SecretKey string + KeyType string BaseURL string UserAgent string HTTPClient *http.Client @@ -249,16 +272,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) { if r.secType == secTypeAPIKey || r.secType == secTypeSigned { header.Set("X-MBX-APIKEY", c.APIKey) } - + kt := c.KeyType + if kt == "" { + kt = common.KeyTypeHmac + } + sf, err := common.SignFunc(kt) + if err != nil { + return err + } if r.secType == secTypeSigned { raw := fmt.Sprintf("%s%s", queryString, bodyString) - mac := hmac.New(sha256.New, []byte(c.SecretKey)) - _, err = mac.Write([]byte(raw)) + sign, err := sf(c.SecretKey, raw) if err != nil { return err } v := url.Values{} - v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil)))) + v.Set(signatureKey, *sign) if queryString == "" { queryString = v.Encode() } else { diff --git a/v2/futures/client.go b/v2/futures/client.go index ae0b5878..afd5160e 100644 --- a/v2/futures/client.go +++ b/v2/futures/client.go @@ -3,8 +3,6 @@ package futures import ( "bytes" "context" - "crypto/hmac" - "crypto/sha256" "crypto/tls" "encoding/json" "fmt" @@ -205,6 +203,7 @@ func NewClient(apiKey, secretKey string) *Client { return &Client{ APIKey: apiKey, SecretKey: secretKey, + KeyType: common.KeyTypeHmac, BaseURL: getApiEndpoint(), UserAgent: "Binance/golang", HTTPClient: http.DefaultClient, @@ -225,6 +224,7 @@ func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client { return &Client{ APIKey: apiKey, SecretKey: secretKey, + KeyType: common.KeyTypeHmac, BaseURL: getApiEndpoint(), UserAgent: "Binance/golang", HTTPClient: &http.Client{ @@ -240,6 +240,7 @@ type doFunc func(req *http.Request) (*http.Response, error) type Client struct { APIKey string SecretKey string + KeyType string BaseURL string UserAgent string HTTPClient *http.Client @@ -286,16 +287,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) { if r.secType == secTypeAPIKey || r.secType == secTypeSigned { header.Set("X-MBX-APIKEY", c.APIKey) } - + kt := c.KeyType + if kt == "" { + kt = common.KeyTypeHmac + } + sf, err := common.SignFunc(kt) + if err != nil { + return err + } if r.secType == secTypeSigned { raw := fmt.Sprintf("%s%s", queryString, bodyString) - mac := hmac.New(sha256.New, []byte(c.SecretKey)) - _, err = mac.Write([]byte(raw)) + sign, err := sf(c.SecretKey, raw) if err != nil { return err } v := url.Values{} - v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil)))) + v.Set(signatureKey, *sign) if queryString == "" { queryString = v.Encode() } else { diff --git a/v2/options/client.go b/v2/options/client.go index f7e1dc77..8acb01f8 100644 --- a/v2/options/client.go +++ b/v2/options/client.go @@ -3,8 +3,6 @@ package options import ( "bytes" "context" - "crypto/hmac" - "crypto/sha256" "crypto/tls" "encoding/json" "fmt" @@ -208,6 +206,7 @@ func NewClient(apiKey, secretKey string) *Client { return &Client{ APIKey: apiKey, SecretKey: secretKey, + KeyType: common.KeyTypeHmac, BaseURL: getApiEndpoint(), UserAgent: "Binance/golang", HTTPClient: http.DefaultClient, @@ -228,6 +227,7 @@ func NewProxiedClient(apiKey, secretKey, proxyUrl string) *Client { return &Client{ APIKey: apiKey, SecretKey: secretKey, + KeyType: common.KeyTypeHmac, BaseURL: getApiEndpoint(), UserAgent: "Binance/golang", HTTPClient: &http.Client{ @@ -243,6 +243,7 @@ type doFunc func(req *http.Request) (*http.Response, error) type Client struct { APIKey string SecretKey string + KeyType string BaseURL string UserAgent string HTTPClient *http.Client @@ -289,16 +290,22 @@ func (c *Client) parseRequest(r *request, opts ...RequestOption) (err error) { if r.secType == secTypeAPIKey || r.secType == secTypeSigned { header.Set("X-MBX-APIKEY", c.APIKey) } - + kt := c.KeyType + if kt == "" { + kt = common.KeyTypeHmac + } + sf, err := common.SignFunc(kt) + if err != nil { + return err + } if r.secType == secTypeSigned { raw := fmt.Sprintf("%s%s", queryString, bodyString) - mac := hmac.New(sha256.New, []byte(c.SecretKey)) - _, err = mac.Write([]byte(raw)) + sign, err := sf(c.SecretKey, raw) if err != nil { return err } v := url.Values{} - v.Set(signatureKey, fmt.Sprintf("%x", (mac.Sum(nil)))) + v.Set(signatureKey, *sign) if queryString == "" { queryString = v.Encode() } else {