Authentication
caution
The authentication has already been encapsulated by Webull SDK, and no additional processing is required. Here is just a description of the signature generation process.
Catalog
1> Signature generation instructions
2> The content of the signature
3> Signature construction example
4> URL encoding considerations
5> Other instructions
Signature Generation Instructions
The signature value sig is the signature value generated by the request source param and secret according to a certain signature method, which is used to improve the tamper-proof modification of the parameters of the transmission process.
There are three steps in the generation of the signature value: construct the source param, construct the secret, and generate the signature value.
The Content of the Signature
The signature source param mainly consists of 4 parts:
1> Http Request Uri
2> QueryParams in Http URLs
3> Http Request Body
4> Http Request Headers
// Note: The http post method is currently not supported to pass k=v format parameters in the body, currently only application/json is supported
The signature-related header conventions are as follows:
Name | Description |
---|---|
x-app-key | Access secret id |
x-signature | Signature value |
x-signature-algorithm | Signature algorithm, default HMAC-SHA1 |
x-signature-version | Signature algorithm version, default 1.0 |
x-signature-nonce | Signature Unique Random Number |
x-timestamp | Requested timestamp, ISO8601 time format, UTC time zone |
host | http request default: host:port, example: api.webull.com:8080 |
The signature rules are as follows:
1> Content involved in signature calculation
1.1> http request uri
1.2> http request query params (Note: param is the original data without urlencode)
1.3> http request body
1.4> http request headers, including: x-app-key、x-signature-algorithm、x-signature-version、x-signature-nonce、x-timestamp、host
2> How signature content is organized:
2.1> The query params and headers are organized into a map structure according to k, v, and then sorted according to the value of the k string from small to large, and then connected in the way of k1=v1&k2=v2, and finally get s1
2.2> Perform md5 processing on the http request body, method: upcase(md5(body)), get: s2 (Note: body is empty and does not participate in the signature)
2.3> Concatenate: uri + "&" + s1 + "&" + s2, resulting in: s3
Note: when body is empty, s3 = uri + '&' + s1
3> Generate signature: signature = base64(HMAC-SHA1(app_secret + "&", encoded_sign_string))
Signature Construction Example
Request Content
uri: /trade/place_order
QueryParams:
Name | Example Value | Remark |
---|---|---|
a1 | webull | Value is not urlencoded |
a2 | 123 | Value is not urlencoded |
a3 | xxx | Value is not urlencoded |
q1 | yyy | Value is not urlencoded |
Headers:
Name | Example Value | Remark |
---|---|---|
x-app-key | 776da210ab4a452795d74e726ebd74b6 | |
x-timestamp | 2022-01-04T03:55:31Z | |
x-signature-version | 1.0 | |
x-signature-algorithm | HMAC-SHA1 | |
x-signature-nonce | 48ef5afed43d4d91ae514aaeafbc29ba | |
host | api.webull.com |
Body: {"k1":123,"k2":"this is the api request body","k3":true,"k4":{"foo":[1,2]}}
Secret: 0f50a2e853334a9aae1a783bee120c1f
Step1: Construct the Source Param
Sort the dictionary from small to large according to the key, and get the following order data:
Key: a1 , Value: webull
Key: a2 , Value: 123
Key: a3 , Value: xxx
Key: host , Value: api.webull.com
Key: q1 , Value: yyy
Key: x-app-key , Value: 776da210ab4a452795d74e726ebd74b6
Key: x-signature-algorithm , Value: HMAC-SHA1
Key: x-signature-nonce , Value: 48ef5afed43d4d91ae514aaeafbc29ba
Key: x-signature-version , Value: 1.0
Key: x-timestamp , Value: 2022-01-04T03:55:31Z
OriginBody: {"k1":123,"k2":"this is the api request body","k3":true,"k4":{"foo":[1,2]}}
Body MD5: ToUpper(MD5(body)) = E296C96787E1A309691CEF3692F5EEDD
Note: The json string generated by some json tools contains spaces between the key and value, which will cause the validation to fail
The concatenated source param is as follows:
/trade/place_order&a1=webull&a2=123&a3=xxx&host=api.webull.com&q1=yyy&x-app-key=776da210ab4a452795d74e726ebd74b6&x-signature-algorithm=HMAC-SHA1&x-signature-nonce=48ef5afed43d4d91ae514aaeafbc29ba&x-signature-version=1.0&x-timestamp=2022-01-04T03:55:31Z&E296C96787E1A309691CEF3692F5EEDD
Get encoded_sign_string after encodeURIComponent:
%2Ftrade%2Fplace_order%26a1%3Dwebull%26a2%3D123%26a3%3Dxxx%26host%3Dapi.webull.com%26q1%3Dyyy%26x-app-key%3D776da210ab4a452795d74e726ebd74b6%26x-signature-algorithm%3DHMAC-SHA1%26x-signature-nonce%3D48ef5afed43d4d91ae514aaeafbc29ba%26x-signature-version%3D1.0%26x-timestamp%3D2022-01-04T03%3A55%3A31Z%26E296C96787E1A309691CEF3692F5EEDD
Step2: Add Key Secret
The way to get the secret: Add a byte of "&" at the end of the app_secret of the application, that is, app_secret&, for example:
0f50a2e853334a9aae1a783bee120c1f&
Step3: Generate Signed Value
How to generate::
signature = base64(HMAC-SHA1(app_secret + "&", encoded_sign_string))
1> Use HMAC-SHA1 encryption algorithm, use the key obtained in Step2 to encrypt the source param obtained in Step1
2> Then base64 encode the encrypted string
3> The result of the obtained signature value is as follows: kvlS6opdZDhEBo5jq40nHYXaLvM=
URL Encoding Considerations
During signature verification, all non-alphanumeric characters in the string except "-", "_", "." are required to be replaced with percent sign (%) followed by two hexadecimal digits, hexadecimal Letters in system numbers must be uppercase.
Other Instructions
1> In some special cases, such as when there are multiple keys in the Query parameter, the values need to be sorted in ascending order by value, and then connected with '&'. For example:
url?k1=v1&k1=v2&k1=v3
After the connection is processed: k1 = v1&v2&v3 , otherwise, the signature will not pass
2> Golang json.Marshal defaults escapeHtml being true, which will escape <, >, &. In this case, the escaped content needs to be processed and replaced with the original characters. The general processing is as follows:
func trans(data []byte) []byte {
data = bytes.Replace(data, []byte("\\u0026"), []byte("&"), -1)
data = bytes.Replace(data, []byte("\\u003c"), []byte("<"), -1)
data = bytes.Replace(data, []byte("\\u003e"), []byte(">"), -1)
return data
}