2014-10-09 46 views
4

我一直在这方面工作了一段时间,我只是无法从服务器获得成功的响应。 这一切文档可在Bittrex Exchange Wesite德尔福HMAC SHA512签名调用Bittrex交易所

找到签名位的主要症结可以在标题认证


我一直在使用可以在 Fundamentals在SourceForge上找到散列文件中找到。它是底部被称为基础散列的那个4.00.15

我一直在使用这个文件的原因很简单,它似乎是唯一给我一个正确答案的。或者我应该说,它给了我正确的答案,与此Hashing Website给我的结果相比。

我试过使用Indy组件来生成正确的散列,但它似乎从未匹配网站的值。也许我没有正确地使用它或正确的库或其他东西,但我将为我创建的示例添加该示例。当我写这个,我再次测试,它看起来像我得到正确的答案,去图,也许我正在使用一个更好的OpenSSL库。无论如何,我也将我的INDY示例下面作为好)。


function Test: String; 
const 
    FAPIKey = 'APIKEY'; 
    FAPISecret = 'APISECRET'; 
    FURL = 'https://bittrex.com/api/v1.1/account/getbalances?apikey=%s&nonce=%d'; 
var 
    FPost, FSignature: String; 
    FNonce: Integer; 
    Response: TStringStream; 
    HTTP: TIdHTTP; 
    SSL:TIdSSLIOHandlerSocketOpenSSL; 
begin 
    Result := ''; 

    FNonce := DateTimeToUnix(Now); 
    FPost := Format(FURL, [FAPIKey, FNonce]); 

    HTTP := TIdHTTP.Create; 
    try 
    SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP); 
    try 
     HTTP.IOHandler := SSL; 

     FSignature := SHA512DigestToHex(CalcHMAC_SHA512(FAPISecret, FPost)); 
     HTTP.Request.CustomHeaders.AddValue('apisign', FSignature); 

     Response := TStringStream.Create; 
     try 
     HTTP.Get(FPost, Response); 
     Result := Response.DataString; 
     finally 
     Response := nil; 
     end; 
    finally 
     SSL := nil; 
    end; 
    finally 
    HTTP := nil; 
    end; 
end; 


此前使用这个版本,我永远只能得到 散列 '{ “成功”:假的, “消息”: “APISIGN_NOT_PROVIDED”, “结果”:空}' 我终于移动了,当我制定了自定义HTTP头,现在变得 '{“success”:false,“message”:“INVALID_SIGNATURE”,“result”:null}' 它可能是一个简单的无效nonce,还是那个太旧了? 一切看起来不错,或者我缺少INDY组件的一些基本组件设置?

function Test: String; 
const 
    FAPIKey = 'APIKEY'; 
    FAPISecret = 'APISECRET'; 
    FURL = 'https://bittrex.com/api/v1.1/account/getbalances?apikey=%s&nonce=%d'; 
var 
    FPost, FSignature: String; 
    FNonce: Integer; 
    Response: TStringStream; 
    HTTP: TIdHTTP; 
    SSL:TIdSSLIOHandlerSocketOpenSSL; 
    FSHA512Hasher: TIdHMACSHA512; 
begin 
    Result := ''; 
    if not LoadOpenSSLLibrary then exit; 

    FNonce := DateTimeToUnix(Now); 
    FPost := Format(FURL, [FAPIKey, FNonce]); 

    HTTP := TIdHTTP.Create; 
    try 
    SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP); 
    try 
     HTTP.IOHandler := SSL; 

     FSHA512Hasher := TIdHMACSHA512.Create; 
     try 
     FSHA512Hasher.Key := ToBytes(FAPISecret); 
     FSignature := Lowercase(ToHex(FSHA512Hasher.HashValue(ToBytes(FPost)))); 
     finally 
     FSHA512Hasher := nil; 
     end; 

     HTTP.Request.CustomHeaders.AddValue('apisign', FSignature); 

     Response := TStringStream.Create; 
     try 
     HTTP.Get(FPost, Response); 
     Result := Response.DataString; 
     finally 
     Response := nil; 
     end; 
    finally 
     SSL := nil; 
    end; 
    finally 
    HTTP := nil; 
    end; 
end; 
+3

你在考虑Unicode吗?Ansi字符串的散列与Unicode字符串的字符值> 127不同。 – 2014-10-09 17:29:45

+0

只是一个旁注,有很多,如果':=零;'我期望一个'免费'。 – bummi 2014-10-09 19:33:07

+0

我同意bummi,我懒惰,没有真正专注于内存泄漏atm – mikelittlewood 2014-10-09 21:55:41

回答

0

我有一个类似的问题,我终于想通了。我使用了通过谷歌搜索发现的hmac.pas单元,但必须修改它才能与10.1 Seattle(特别是IDBytes有点不同)一起使用。

重要提示:我还必须修改hmac.pas单元以从HexStr变体的前面去掉'0x'。

另外:我做了所有的输入ansistring。

我的测试功能如下。

function TBTXClient.Get(sCommand, sAddParams: string): string; 
var 
    sURL: string; 
    nonce: string; 
    res: string; 
    sec2: string; 
    sha: TIDHashSha256; 
    hmac: TIdHMAC; 
    hash: string; 
    ms: TMemoryStream; 
begin 
    nonce := inttostr(getticker); 
    sURL := 'https://bittrex.com/api/v1.1/'+sCommand+'?apikey='+MY_KEY+'&nonce='+nonce+sAddParams; 

    hash := THMACUtils<TIDHMacSha512>.HMAC_HexStr(MY_SECRET,sURL); 

    //Debug.Log('url = '+sUrl); 
    //Debug.Log('hash = '+hash); 
    QuickHTTPSGet(sURL, res, 'apisign', hash); 

    result := res; 

end; 

我修改了hmac.pas单元的版本(有一些新的依赖)

unit hmac; 

interface 

uses 
    System.SysUtils, 
    EncdDecd, 
    IdHMAC, 
    IdSSLOpenSSL, 
    helpers.indy, 
    idglobal, 
    IdHash; 

type 
    localstringtype = ansistring; 

    THMACUtils<T: TIdHMAC, constructor> = class 
    public 
    class function HMAC(aKey, aMessage: localstringtype): TIdBytes; 
    class function HMAC_HexStr(aKey, aMessage: localstringtype): localstringtype; 
    class function HMAC_Base64(aKey, aMessage: localstringtype): localstringtype; 
    end; 

implementation 

class function THMACUtils<T>.HMAC(aKey, aMessage: localstringtype): TIdBytes; 
var 
    _HMAC: T; 
begin 
    if not IdSSLOpenSSL.LoadOpenSSLLibrary then Exit; 
    _HMAC:= T.Create; 
    try 
    _HMAC.Key := AnsiStringToIDBytes(aKey); 
    Result:= _HMAC.HashValue(AnsiStringToIDBytes(aMessage)); 
    finally 
    _HMAC.Free; 
    end; 
end; 

class function THMACUtils<T>.HMAC_HexStr(aKey, aMessage: localstringtype):  localstringtype; 
var 
    I: Byte; 
begin 
    Result:= '';//'0x'; 
    for I in HMAC(aKey, aMessage) do 
    Result:= Result + IntToHex(I, 2); 
end; 

class function THMACUtils<T>.HMAC_Base64(aKey, aMessage: localstringtype):  localstringtype; 
var 
    _HMAC: TIdBytes; 
begin 
    _HMAC:= HMAC(aKey, aMessage); 
    Result:= EncodeBase64(_HMAC, Length(_HMAC)); 
end; 

end. 

你可能也想要这个辅助单元。

unit helpers.indy; 

interface 

uses 
    idglobal, types, classes, sysutils, typex; 


function TBytesToIDBytes(b: TBytes): TIDBytes; 
function AnsiStringToIDBytes(a: ansistring): TIDBytes; 

implementation 


function TBytesToIDBytes(b: TBytes): TIDBytes; 
var 
    t: ni; 
begin 
    setlength(result, length(b)); 
    for t := low(b) to high(b) do 
    result[t] := b[t]; 

end; 
function AnsiStringToIDBytes(a: ansistring): TIDBytes; 
var 
    t: ni; 
begin 
    setlength(result, length(a)); 
    for t := 0 to length(a)-1 do 
    result[t] := ord(a[STRZ+t]); 

end; 

而我从我自己的https单元借来的这个cheesy函数展示了如何处理头文件params。

function QuickHTTPSGet(sURL: ansistring; out sOutREsponse: string; 
    addHead: string =''; addHeadValue: string = ''): boolean; 
var 
    htp: IXMLhttprequest; 
begin 

    htp := ComsXMLHTTP30.create(); 
    try 
    htp.open('GET', sURL, false, null, null); 
    if addHead <> '' then 
     htp.setRequestHeader(addHead, addHeadValue); 
    htp.send(''); 
    result := htp.status = 200; 
    if result then 
     sOutREsponse := htp.responsetext 
    else 
     soutResponse := 'error '+inttostr(htp.status); 
    except 
    on e: Exception do begin 
     result := false; 
     sOutResponse := 'error '+e.message; 
    end; 
    end; 

end;