Click here to Skip to main content
15,888,579 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi i have this script in bash for huobi exchange (https://www.htx.com/en-us/opend/newApiPages/?id=419[^])
Bash
#!/bin/bash

# Set API keys
access_key=""
secret_key=""
host="api.huobi.pro"
path="/v1/account/accounts"
method="GET"

# Generates a timestamp in UTC format and URL-encodes it
timestamp() {
    date -u +"%Y-%m-%dT%H:%M:%S" | sed 's/:/%3A/g'
}

# URL-encode parameters
urlencode() {
    local string="${1}"
    local strlen=${#string}
    local encoded=""
    local pos c o

    for (( pos=0 ; pos<strlen ; pos++ )); do
       c=${string:$pos:1}
       case "$c" in
           [-_.~a-zA-Z0-9] ) o="${c}" ;;
           * )               printf -v o '%%%02x' "'$c"
       esac
       encoded+="${o}"
    done
    echo "${encoded}"
}

# Generate the signature
generate_signature() {
    local params="AccessKeyId=$access_key&SignatureVersion=2&SignatureMethod=HmacSHA256&Timestamp=$(timestamp)"
    local pre_signed_string="$method\n$host\n$path\n$params"
    echo -en "${pre_signed_string}" | openssl dgst -sha256 -hmac "${secret_key}" -binary | base64
}

# Send the GET request
send_request() {
    local signature=$(generate_signature)
    local signed_params="AccessKeyId=$access_key&SignatureVersion=2&SignatureMethod=HmacSHA256&Timestamp=$(timestamp)&Signature=$(urlencode "$signature")"
    local request_url="https://${host}${path}?${signed_params}"

    echo "Full request URL: $request_url"
    curl -s -H "Content-Type: application/x-www-form-urlencoded" -X GET "${request_url}"
}

# Main execution
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    response=$(send_request)
    echo "Response: $response"
fi


its return
{"status":"error","err-code":"api-signature-not-valid","err-msg":"Signature not valid: Verification failure [校验失败]","data":null} my signature is: 185cddeb6b6b6be76e1f5b917f3188dc52349cd67d91136dc90a28be8f43d71e 
but
if i run this python script:
import time
from urllib import parse
import requests
from urllib import parse
from datetime import datetime, timezone
import hmac
import base64
import hashlib
import logger
import json
import urllib.parse


class UrlParamsBuilder(object):

    def __init__(self):
        self.param_map = dict()
        self.post_map = dict()
        self.post_list = list()

    def put_url(self, name, value):
        if value is not None:
            if isinstance(value, (list, dict)):
                self.param_map[name] = value
            else:
                self.param_map[name] = str(value)

    def put_post(self, name, value):
        if value is not None:
            if isinstance(value, (list, dict)):
                self.post_map[name] = value
            else:
                self.post_map[name] = str(value)

    def build_url(self):
        if len(self.param_map) == 0:
            return ""
        encoded_param = urllib.parse.urlencode(self.param_map)
        return "?" + encoded_param

    def build_url_to_json(self):
        return json.dumps(self.param_map)

def _get_url_suffix(method: str, access_key: str, secret_key: str, host: str, path: str,params: dict) -> str:


    timestamp = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S')

    builder = UrlParamsBuilder()

    if params != None and method=="GET":
        for key, value in params.items():
            builder.put_url(key, value)
    builder.put_url("AccessKeyId", access_key)
    builder.put_url("SignatureVersion", "2")
    builder.put_url("SignatureMethod", "HmacSHA256")
    builder.put_url("Timestamp", timestamp)


    keys = sorted(builder.param_map.keys())
    qs0 = '&'.join(['%s=%s' % (key, parse.quote(builder.param_map[key], safe='')) for key in keys])
    payload0 = '%s\n%s\n%s\n%s' % (method, host, path, qs0)
    print("Valore utilizzato per creare la signature:", payload0)  # Stampiamo il valore utilizzato per creare la signature
    dig = hmac.new(secret_key.encode('utf-8'), msg=payload0.encode('utf-8'), digestmod=hashlib.sha256).digest()
    s = base64.b64encode(dig).decode()
    builder.put_url("Signature", s)
    suffix = builder.build_url()
    return suffix

def post(access_key: str, secret_key: str, host: str, path: str, data: dict = None) -> json:
    try:
        url = 'https://{}{}{}'.format(host, path, _get_url_suffix(
            'POST', access_key, secret_key, host, path,None))
        headers = {'Accept': 'application/json',
                   'Content-type': 'application/json'}
        res = requests.post(url, json=data, headers=headers)
        data = res.json()
        return data
    except Exception as e:
        logger.error(e)
    return None

def getcurrentdate():
    pass
    current_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
    return current_date
def datetotimestamp(date):
    datetime_obj = datetime.strptime(date, "%Y-%m-%d %H:%M:%S.%f")
    ret_stamp = int(time.mktime(datetime_obj.timetuple()) * 1000.0 + datetime_obj.microsecond / 1000.0)
    return ret_stamp

def get(access_key: str, secret_key: str, host: str, path: str, params: dict ) -> json:
    try:
        url = 'https://{}{}{}'.format(host, path, _get_url_suffix(
            'GET', access_key, secret_key, host, path,params))
        headers = {'Content-type': 'application/x-www-form-urlencoded'}
        print("Full_request_Url:",url)
        res = requests.get(url, headers=headers)
        data = res.json()


        return data
    except Exception as e:
        logger.error(e)
    return None

if __name__ == '__main__':

    # APIkey
    access_key = ''
    secret_key = ''

    host = 'api.huobi.pro'
    path = '/v1/account/accounts'
    params = dict()

    acc_json = get(access_key, secret_key, host, path, params)
    print("response", acc_json)

it work and return sign iZD79Fj4H2yAwY%2F%2FkxyKE7WWLVBpbT9Rfz%2BAXDuG%2FsA%3D (little bit short ) with the same input param why ?

What I have tried:

other bash version that i tested is
_Huobi_Get_all_Accounts_of_the_Current_User() {
	
    _Init_Huobi_ApiSecretIDData "huobi"
    
    local PRE_URL="v1/account/accounts"
    echo "PART OF URL:---->  $PRE_URL"
    local apiUrl="$HTXApiUrl$PRE_URL"
    echo "COMPLEATE ULR:----> $apiUrl"
    local ACCESSKEYID="AccessKeyId=$HTXApiKey"
    echo "ACCESS KEY: -----> $ACCESSKEYID"
    local SIGNATUREVERSION="SignatureVersion=2"
    echo "VERSION SIGN:-----> $SIGNATUREVERSION"
    local SIGNATUREMETHOD="SignatureMethod=HmacSHA256"
    echo "METHOD SIGN: -----> $SIGNATUREMETHOD"
    local TIMESTAMP="Timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S" | sed 's/:/%3A/g')"
    echo "TIMESTAMP: ----> $TIMESTAMP"
    
    # La stringa di query deve essere ordinata in base all'ASCII prima di essere firmata
    local QUERYSTRING=$(echo -n "$ACCESSKEYID&$SIGNATUREMETHOD&$SIGNATUREVERSION&$TIMESTAMP" | sort)
    echo "QUERY 4 RAW MESSAGE: ------> $QUERYSTRING"
    
    # La firma viene creata normalizzando la stringa di query

    local RAW_MESSAGE="GET\n$HTXApiUrl\n$PRE_URL\n$QUERYSTRING"
	echo "RAW MESSAGE 4 SIGNATURE: ------> $RAW_MESSAGE"
	local SIGNATURE=$(echo -n "$RAW_MESSAGE" | openssl dgst -sha256 -hmac $HTXSecretKey | cut -c 18-)

	#local SIGNATURE=$(echo -n "GET"$'\x0A'"$HTXApiUrl"$'\x0A'"$PRE_URL"$'\x0A'"$QUERYSTRING" | openssl dgst -sha256 -hmac $HTXSecretKey | cut -c 18-)   
    echo "SIGNATURE -----> $SIGNATURE"
    local SIGNATUREPARAM="Signature=$SIGNATURE"
    #echo "$SIGNATUREPARAM"
    
    # Utilizza curl e cattura l'output nella variabile response
    response=$(curl -s -H  "Content-type: application/x-www-form-urlencoded" -X GET "$apiUrl?$QUERYSTRING&$SIGNATUREPARAM")
    echo "CALL CURL WITH ALL FEED: ---->curl -H 'Content-Type: application/x-www-form-urlencoded' -X GET \"$apiUrl?$QUERYSTRING&$SIGNATUREPARAM\""
    

}
Posted

1 solution

You bash script already seems to print out the URL it sends in send_request(), but I can't see that your python script does the same. The first thing I would do, is get the python script to print out the URL and compare. If you want to see exactly what the scripts are sending, you could modify them to talk to a local port eg http:://localhost:9000/$URL. In a separate window you can then do ncat -l 9000. When you connect you should get something like :
Terminal
$ ncat -l 9000
GET //foo.bar/ HTTP/1.1
Host: localhost:9000
User-Agent: curl/7.85.0
Accept: */*

^C
If you need to use an SSL connection you can add --ssl to the ncat command line, and ncat will auto-generate a self-signed key for use. In that case you'll have to tell both the python script and the bash script to expect a "non secure" key. For curl, use the -k key. You'll have to research how the python libraries accomplish that.
 
Share this answer
 
v4
Comments
GiulioRig 31-Mar-24 18:08pm    
@k5054 in bash i print signature echo -en "${pre_signed_string}" | openssl dgst -sha256 -hmac "${secret_key}" -binary | base64 and in python i print signature print("Valore utilizzato per creare la signature:", payload0) # Stampiamo il valore utilizzato per creare la signature

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900