dongyu2047 2014-12-31 01:49
浏览 159
已采纳

Go和Python HMAC库给出不同的结果

I am trying to authenticate using HMAC/sha512 API key scheme.

Here is the example Python code, which works fine:

import urllib, urllib2
import json
import time
import hmac, hashlib
import sys
api_key = "J88PJQEG-LKKICZLN-3H33GWIB-97OGW8I5"
secret = "b9f2e97c5c43e8e759c06219b37fce78478985ae4b0176d47182419c434567405a9386a854bca5d068135d1163d3f1cc9b877cd5d95d03c9d100be6ffcaac864"

# cmd = sys.argv[1]
# args = json.loads(sys.argv[2])

def auth_request(command, args):
    args = [("command", command), ("nonce", 3000000000)]
    post_data = urllib.urlencode(args)
    print post_data
    sign = hmac.new(secret, post_data, hashlib.sha512).hexdigest()
    print sign
    headers = {
        'Sign': sign,
        'Key': api_key
    }

    ret = urllib2.urlopen(urllib2.Request('https://poloniex.com/tradingApi', post_data, headers))
    return ret.read()

print auth_request("returnBalances", {})

And now my Go implementation:

const (
    public_api_url  = "https://poloniex.com/public"
    private_api_url = "https://poloniex.com/tradingApi"
    pubkey := "J88PJQEG-LKKICZLN-3H33GWIB-97OGW8I5"
    privkey := "b9f2e97c5c43e8e759c06219b37fce78478985ae4b0176d47182419c434567405a9386a854bca5d068135d1163d3f1cc9b877cd5d95d03c9d100be6ffcaac864"
)
func CallPrivate(method string, args map[string]string) dynjson.DynNode {

    if args == nil {
        args = make(map[string]string)
    }
    v := make(url.Values)
    v.Set("nonce", "3000000000") //strconv.Itoa(int((time.Now().Unix()))*1000))
    v.Set("command", method)
    for k, val := range args {
        v.Set(k, val)
    }
    final_url := private_api_url + "?" + v.Encode()
    log.Println(final_url)
    client := &http.Client{}
    post_data := v.Encode()
    secret_bytes, err := hex.DecodeString(privkey)
    check(err)
    sighash := hmac.New(sha512.New, secret_bytes)
    sighash.Write([]byte(post_data))
    sigstr := hex.EncodeToString(sighash.Sum(nil))
    log.Println(sigstr)
    j, err := json.Marshal(args)
    check(err)
    buf := bytes.NewBuffer(j)
    req, err := http.NewRequest("POST", final_url, buf)
    check(err)
    req.Header.Set("Key", pubkey)
    req.Header.Set("Sign", sigstr)
    res, err := client.Do(req)
    check(err)
    defer res.Body.Close()
    if res.StatusCode != 200 {
        log.Println("bad status code")
        log.Printf("%s", res)
        panic(errors.New(res.Status))
    }
    res_body, err := ioutil.ReadAll(res.Body)
    check(err)
    //log.Printf("%v", res_body)
    return dynjson.NewFromBytes(res_body)

}

CallPrivate("returnBalances", nil)

Right now I am stuck calling out to the python implementation which is not a pretty solution.

  • 写回答

1条回答 默认 最新

  • dongyan6235 2014-12-31 01:54
    关注

    Python dict is an unordered mapping type.

    >>> args = {}
    >>> args['command'] = 'command'
    >>> args['nonce'] = 10000
    >>> list(args)
    ['nonce', 'command']  # <--- Iterating dictionary will yield in different order
                          #      with item insertion order
    

    urllib.urlencode not only accepts a dictinoary, but it also accept a sequence of key-value-pair sequences. You can use it to guarantee the order of output:

    >>> import urllib
    >>> urllib.urlencode({'command': 'command', 'nonce': 10000})
    'nonce=10000&command=command'
    >>> urllib.urlencode([('command', 'command'), ('nonce', 10000)])
    'command=command&nonce=10000'
    

    Alternatively, instead of dict ({}), you can use collections.OrderedDict which remembers key insertion order.

    UPDATE

    In the Go code, command post data is set with method instead of command parameter.

    v.Set("command", method)
                     ^^^^^^
    

    UPDATE2

    Python code uses the hex string secret as is:

    sign = hmac.new(secret, post_data, hashlib.sha512).hexdigest()
    

    while the Go code decode it before use it:

    secret_bytes, _ := hex.DecodeString(privkey)
    sighash := hmac.New(sha512.New, secret_bytes)
    

    Do the same way as the Python code:

    sighash := hmac.New(sha512.New, []byte(privkey))
    

    UPDATE3

    • Go code does json marshal for post_data, but Python code does not.
    • You need to set header in Go code: "Content-Type=application/x-www-form-urlencoded"

    Here's the modified Go code that will work:

    func CallPrivate(method string, args map[string]string) {
        if args == nil {
            args = make(map[string]string)
        }
        v := make(url.Values)
        v.Set("nonce", "3000000000") //strconv.Itoa(int((time.Now().Unix()))*1000))
        v.Set("command", method)
        for k, val := range args {
            v.Set(k, val)
        }
        final_url := private_api_url
        post_data := v.Encode()
        sighash := hmac.New(sha512.New, []byte(privkey))
        sighash.Write([]byte(post_data))
        sigstr := hex.EncodeToString(sighash.Sum(nil))
    
        client := &http.Client{}
        buf := bytes.NewBuffer([]byte(post_data))
        req, _ := http.NewRequest("POST", final_url, buf)
        req.Header.Set("Key", pubkey)
        req.Header.Set("Sign", sigstr)
        req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
        res, _ := client.Do(req)
        defer res.Body.Close()
    
        res_body, _ := ioutil.ReadAll(res.Body)
        log.Printf("%s", string(res_body))
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥35 平滑拟合曲线该如何生成
  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable
  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 自己瞎改改,结果现在又运行不了了
  • ¥15 链式存储应该如何解决
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站