Search code examples
python-3.xapisigning

Huobi working python 3.6 example create url (including signature)


Working example to generate a valid url (including signature) for the Huobi API.

In the Huobi API documenation there is no explicit example that allows you to verify your signature creation method step by step.

My intention is to create that here, but I need help, because I haven't managed yet.

The following is supposed to be the recipe.

Note that once you have this working, substitute valid values for your API key + secret and timestamp:

import hmac
import hashlib
import base64

from urllib.parse import urlencode

API_KEY = 'dummy-key'
API_SECRET = 'dummy-secret'
timestamp = '2021-03-04T11:36:39'

params_dict = {
    'AccessKeyId': API_KEY,
    'SignatureMethod': 'HmacSHA256',
    'SignatureVersion': '2',
    'Timestamp': timestamp
}
params_url_enc = urlencode(sorted(params_dict.items()))

pre_signed = 'GET\n'
pre_signed += 'api.huobi.pro\n'
pre_signed += '/v1/account/accounts\n'
pre_signed += params_url_enc

sig_bytes = hmac.new(
    API_SECRET.encode(),
    pre_signed.encode(),
    hashlib.sha256).hexdigest().encode()
sig_b64_bytes = base64.b64encode(sig_bytes)
sig_b64_str = sig_b64_bytes.decode()
sig_url = urlencode({'Signature': sig_b64_str})

url = 'https://api.huobi.pro/v1/account/accounts?'
url += params_url_enc + '&'
url += sig_url

print('API_KEY={}'.format(API_KEY))
print('API_SECRET={}'.format(API_SECRET))
print('timestamp={}'.format(timestamp))
print('params_dict={}'.format(params_dict))
print('params_url_enc={}'.format(params_url_enc))
print('pre_signed:\n{}'.format(pre_signed))
print('sig_bytes={}'.format(sig_bytes))
print('sig_b64_bytes={}'.format(sig_b64_bytes))
print('sig_b64_str={}'.format(sig_b64_str))
print('sig_url={}'.format(sig_url))
print('url={}'.format(url))

Gives:

API_KEY=dummy-key
API_SECRET=dummy-secret
timestamp=2021-03-04T11:36:39
params_dict={'AccessKeyId': 'dummy-key', 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'Timestamp': '2021-03-04T11:36:39'}
params_url_enc=AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T11%3A36%3A39
pre_signed:
GET
api.huobi.pro
/v1/account/accounts
AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T11%3A36%3A39
sig_bytes=b'1921de9f42284bc0449c5580f52a9f7e7e3a54a6e8befc0d320992e757517a6b'
sig_b64_bytes=b'MTkyMWRlOWY0MjI4NGJjMDQ0OWM1NTgwZjUyYTlmN2U3ZTNhNTRhNmU4YmVmYzBkMzIwOTkyZTc1NzUxN2E2Yg=='
sig_b64_str=MTkyMWRlOWY0MjI4NGJjMDQ0OWM1NTgwZjUyYTlmN2U3ZTNhNTRhNmU4YmVmYzBkMzIwOTkyZTc1NzUxN2E2Yg==
sig_url=Signature=MTkyMWRlOWY0MjI4NGJjMDQ0OWM1NTgwZjUyYTlmN2U3ZTNhNTRhNmU4YmVmYzBkMzIwOTkyZTc1NzUxN2E2Yg%3D%3D
url=https://api.huobi.pro/v1/account/accounts?AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T11%3A36%3A39&Signature=MTkyMWRlOWY0MjI4NGJjMDQ0OWM1NTgwZjUyYTlmN2U3ZTNhNTRhNmU4YmVmYzBkMzIwOTkyZTc1NzUxN2E2Yg%3D%3D

Also add header in sending:

{"Content-Type": "application/x-www-form-urlencoded"}

Unfortunately, when I substitute my own valid API key + secret and a proper UTC time stamp, I invariably receive:

{"status":"error","err-code":"api-signature-not-valid","err-msg":"Signature not valid: Verification failure [校验失败]","data":null}

So what is going wrong here?


Solution

  • The mistake was that I took the hexidigest of the hash, whereas the digest was needed.

    Working recipe here that you can check numerically to validate your code:

    import hmac
    import hashlib
    import base64
    
    from urllib.parse import urlencode
    
    API_KEY = 'dummy-key'
    API_SECRET = 'dummy-secret'
    timestamp = '2021-03-04T12:54:56'
    
    params_dict = {
        'AccessKeyId': API_KEY,
        'SignatureMethod': 'HmacSHA256',
        'SignatureVersion': '2',
        'Timestamp': timestamp
    }
    params_url_enc = urlencode(
        sorted(params_dict.items(), key=lambda tup: tup[0]))
    
    pre_signed = 'GET\n'
    pre_signed += 'api.huobi.pro\n'
    pre_signed += '/v1/account/accounts\n'
    pre_signed += params_url_enc
    
    sig_bin = hmac.new(
        API_SECRET.encode(),
        pre_signed.encode(),
        hashlib.sha256).digest()
    sig_b64_bytes = base64.b64encode(sig_bin)
    sig_b64_str = sig_b64_bytes.decode()
    sig_url = urlencode({'Signature': sig_b64_str})
    
    url = 'https://api.huobi.pro/v1/account/accounts?'
    url += params_url_enc + '&'
    url += sig_url
    
    print('API_KEY={}'.format(API_KEY))
    print('API_SECRET={}'.format(API_SECRET))
    print('timestamp={}'.format(timestamp))
    print('params_dict={}'.format(params_dict))
    print('params_url_enc={}'.format(params_url_enc))
    print('pre_signed:\n{}'.format(pre_signed))
    print('sig_bin={}'.format(sig_bin))
    print('sig_b64_bytes={}'.format(sig_b64_bytes))
    print('sig_b64_str={}'.format(sig_b64_str))
    print('sig_url={}'.format(sig_url))
    print('url={}'.format(url))
    

    Result:

    $ python test_huobi_so.py
    API_KEY=dummy-key
    API_SECRET=dummy-secret
    timestamp=2021-03-04T12:54:56
    params_dict={'AccessKeyId': 'dummy-key', 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'Timestamp': '2021-03-04T12:54:56'}
    params_url_enc=AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T12%3A54%3A56
    pre_signed:
    GET
    api.huobi.pro
    /v1/account/accounts
    AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T12%3A54%3A56
    sig_bin=b'_\xb9k\x82!\xb4B%A\xfe\x0c \xff\x07%JE\xbe\x82\x8b-<^\xb7\xfc\x06\x85G\xb5$\x81\xd7'
    sig_b64_bytes=b'X7lrgiG0QiVB/gwg/wclSkW+gostPF63/AaFR7Ukgdc='
    sig_b64_str=X7lrgiG0QiVB/gwg/wclSkW+gostPF63/AaFR7Ukgdc=
    sig_url=Signature=X7lrgiG0QiVB%2Fgwg%2FwclSkW%2BgostPF63%2FAaFR7Ukgdc%3D
    url=https://api.huobi.pro/v1/account/accounts?AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T12%3A54%3A56&Signature=X7lrgiG0QiVB%2Fgwg%2FwclSkW%2BgostPF63%2FAaFR7Ukgdc%3D