Delphi HMAC SHA512 签名调用 Bittrex Exchange

Posted

技术标签:

【中文标题】Delphi HMAC SHA512 签名调用 Bittrex Exchange【英文标题】:Delphi HMAC SHA512 signed calls to Bittrex Exchange 【发布时间】:2014-12-04 11:29:51 【问题描述】:

我已经为此工作了一段时间,但我无法从服务器获得成功的响应。 这方面的所有文档都可以在Bittrex Exchange Wesite

找到

签名位的主要症结可以在标题Authentication下找到


我一直在使用的散列文件可以在 SourceForge 上的Fundamentals 找到。它位于底部,称为 Fundamentals Hash 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;


在使用此版本进行散列之前,我只得到过 '"成功":false,"message":"APISIGN_NOT_PROVIDED","re​​sult":null' 当我制定出自定义 HTTP 标头时,我终于继续前进,现在我得到了 '"success":false,"message":"INVALID_SIGNATURE","re​​sult":null' 它可能是简单的无效随机数,还是太旧? 一切看起来都正常吗,还是我错过了 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;

【问题讨论】:

您是否考虑了 Unicode? Ansi 字符串的哈希值不同于 Unicode 字符串的字符值 > #127。 只是一个旁注,如果:= nil; 我希望Free 有很多。 我同意 bummi,我很懒惰,并没有真正关注 atm 的内存泄漏 谢谢 Remy,我觉得我应该改用 TEncoding.UTF8.GetBytes 之类的东西吗? 一点小改变都不开心 【参考方案1】:

我有一个类似的问题,我最终想通了。我使用了通过谷歌搜索找到的 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 单元中借用的这个俗气的函数显示了如何处理标头参数。

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;

【讨论】:

以上是关于Delphi HMAC SHA512 签名调用 Bittrex Exchange的主要内容,如果未能解决你的问题,请参考以下文章

PBKDF2-HMAC-SHA-512 测试向量

网络安全——Base64编码MD5SHA1-SHA512HMAC(SHA1-SHA512)哈希

python接口自动化23-签名(signature)鉴权(authentication)之加密(HEXMD5HMAC-SHA256)

HMAC 256 与 HMAC 512 JWT 签名加密

HMAC-SHA1签名认证算法

使用 HMAC SHA512 从 API 获取数据 - Swift