2017-04-18 40 views
7

Data.Cloud.CloudAPI.pas有一些文件上返回錯誤的SHA 256的class function TCloudSHA256Authentication.GetStreamToHashSHA256Hex(const Content: TStream): string;delphi THashSHA2在大文件上返回一個錯誤的SHA256

class function TCloudSHA256Authentication.GetStreamToHashSHA256Hex(const Content: TStream): string; 
var 
    LBytes : TBytes; 
    Hash: THashSHA2; 
begin 
    LBytes := TBytesStream(Content).Bytes; 
    //Hash bytes 
    Hash := THashSHA2.Create; 
    Hash.Update(LBytes); 
    Result := Hash.HashAsString; 
end; 

AWS S3返回錯誤:

The provided x-amz-content-sha256 header does not match what was computed

GetStreamToHashSHA256Hex似乎從亞馬遜產生不同的SHA256:

<ClientComputedContentSHA256>f43ee89e2b7758057bb1f33eb8546d4c2c118f2ab932de89dbd74aabc0651053</ClientComputedContentSHA256> 
<S3ComputedContentSHA256>3bbf5f864cc139cf6392b4623bd782a69d16929db713bffaa68035f8a5c3c0ce</S3ComputedContentSHA256> 

我做了一些測試,機智myfile.zip(600 MB)。 ..

TIdHashSHA256 Indy的替代選項返回正確的SHA256(aws s3的相同),例如:

var 
    aFileStream: TFileStream; 
    aHash:  TIdHashSHA256; 
begin 
    aFileStream := TFileStream.Create('C:\myfile.zip', fmOpenRead or fmShareDenyWrite); 
    aHash  := TIdHashSHA256.Create; 

    try 
    Result := aHash.HashStreamAsHex(aFileStream).ToLower; 
    finally 
    aFileStream.Free; 
    aHash.Free; 
    end; 
end; 

hash_file()從PHP返回正確的SHA256(相同AWS的S3),例如:

hash_file('sha256', 'C:\myfile.zip'); 

但THashSHA2返回一個錯誤SHA256,如:

var 
    LBytes : TBytes; 
    Hash: THashSHA2; 
begin 
    LBytes := TFile.ReadAllBytes('C:\myfile.zip'); 
    Hash := THashSHA2.Create; 
    Hash.Update(LBytes); 
    Result := Hash.HashAsString; 
end; 

爲什麼?

UPDATE

這是我的錯誤修復。進口Data.Cloud.CloudAPI.pas到項目和重寫這些功能:

uses IdHash, IdHashSHA, IdSSLOpenSSL; 

class function TCloudSHA256Authentication.GetHashSHA256Hex(HashString: string): string; 
var 
    aHash: TIdHashSHA256; 
begin 
    LoadOpenSSLLibrary; 
    try 
    if not(TIdHashSHA256.IsAvailable) then 
     raise Exception.Create('HashSHA256 Isn''t available!'); 

    aHash := TIdHashSHA256.Create; 
    try 
     Result := aHash.HashStringAsHex(HashString).ToLower; 
    finally 
     aHash.Free; 
    end; 
    finally 
    UnLoadOpenSSLLibrary; 
    end; 
end; 

class function TCloudSHA256Authentication.GetStreamToHashSHA256Hex(const Content: TStream): string; 
var 
    aHash: TIdHashSHA256; 
begin 
    LoadOpenSSLLibrary; 
    try 
    if not(TIdHashSHA256.IsAvailable) then 
     raise Exception.Create('HashSHA256 Isn''t available!'); 

    aHash := TIdHashSHA256.Create; 
    try 
     Result := aHash.HashStreamAsHex(Content).ToLower; 
    finally 
     aHash.Free; 
    end; 
    finally 
    UnLoadOpenSSLLibrary; 
    end; 
end; 

更新2

我也嘗試實行FREDS建議,它的工作原理:

class function TCloudSHA256Authentication.GetHashSHA256Hex(HashString: string): string; 
var 
    Content: TStringStream; 
    Hash: THashSHA2; 
    LBytes: TArray<Byte>; 
    Buffer: PByte; 
    BufLen: Integer; 
    Readed: Integer; 
begin 
    BufLen := 16 * 1024; 

    Buffer := AllocMem(BufLen); 
    Hash := THashSHA2.Create; 
    Content := TStringStream.Create(HashString); 
    try 
    while Content.Position < Content.Size do 
    begin 
     Readed := Content.Read(Buffer^, BufLen); 
     if Readed > 0 then 
     Hash.update(Buffer^, Readed); 
    end; 
    finally 
    Content.Free; 
    FreeMem(Buffer); 
    end; 

    Result := Hash.HashAsString; 
end; 

class function TCloudSHA256Authentication.GetStreamToHashSHA256Hex(const Content: TStream): string; 
var 
    LBytes : TBytes; 
    Hash: THashSHA2; 
    Buffer: PByte; 
    BufLen: Integer; 
    Readed: Integer; 
begin 
    BufLen := 16 * 1024; 

    Buffer := AllocMem(BufLen); 
    Hash := THashSHA2.Create; 
    try 
     Content.Seek(0, soFromBeginning); 

     while Content.Position < Content.Size do 
     begin 
     Readed := Content.Read(Buffer^, BufLen); 
     if Readed > 0 then 
      Hash.update(Buffer^, Readed); 
     end; 

     Content.Seek(0, soFromBeginning); 
    finally 
     FreeMem(Buffer); 
    end; 

    Result := Hash.HashAsString; 
end; 
+0

顯然'THashSHA2'是越野車然後。請與Embarcadero [提交錯誤報告](http://quality.embarcadero.com)。 –

+0

@RemyLebeau THashSHA2是越野車,你知道解決它的任何解決方法嗎? – ar099968

+0

您可以使用許多正確的實現。找一個。用它。 –

回答

4

我剛剛在柏林使用MS Cyrpto和THashSHA2測試了一個+1.5 GB的文件,他們都返回了相同的散列,但像OpenSSL這樣的MS Crypto速度更快。

問題是該文件太大而無法在一個塊中保存TBytes。 我的記錄幫助程序有TBytes.MaxLen = $F000; {61440},因此您需要使用TFileStream並將文件分塊讀入HashSHA2.Update。

更新: 根據David Heffernan的評論,我重新測試了TBytes.MaxLen,它似乎只受限於可用內存。 MS加密和Delphi HashSha2

注之間


實踐例和速度對比:要求絕API

program SHA2SpeedTest; 

    {$APPTYPE CONSOLE} 
    {$R *.res} 

    uses 
    JwaWindows, Winapi.Windows, System.SysUtils, System.Classes, System.Diagnostics, System.Hash; 

    const 
    SHA256_LEN = 256 div 8; 
    ChunkSize  = $F000; 

    type 
    TBytesHelper = record helper for TBytes 
    public 
     function BinToHex: string; 
    end; 

    function TBytesHelper.BinToHex: string; 
    var 
    Len : Integer; 
    begin 
    Len := Length(Self); 
    SetLength(Result, Len * 2)); 
    System.Classes.BinToHex(Self, PChar(Result), Len); 
    end; 

    procedure DelphiHash256(const AStream: TStream; out Bytes: TBytes); 
    var 
    HashSHA2: THashSHA2; 
    BytesRead: Integer; 
    begin 
    HashSHA2 := THashSHA2.create; 
    SetLength(Bytes, ChunkSize); 
    AStream.Position := 0; 
    repeat 
     BytesRead := AStream.Read(Bytes, ChunkSize); 
     if (BytesRead = 0) then Break; // Done 
     HashSHA2.Update(Bytes, BytesRead); 
    until False; 
    Bytes := HashSHA2.HashAsBytes; 
    end; 


    function CryptoHash256(const AStream: TStream; out Bytes: TBytes): Boolean; 
    var 
    SigLen : Cardinal; 
    hHash : HCRYPTHASH; 
    hProv : HCRYPTPROV; 
    BytesRead: Integer; 
    begin 
    hProv := 0; hHash := 0; 
    Result := False; 

    If not CryptAcquireContext(hProv, nil, nil, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) then Exit; 
    try 
     if not CryptCreateHash(hProv, CALG_SHA_256, 0, 0, hHash) then Exit; 
     try 
     SetLength(Bytes, ChunkSize); 
     AStream.Position := 0; 
     repeat 
      BytesRead := AStream.Read(Bytes, ChunkSize); 
      if (BytesRead = 0) then Break; // Done 
      if not CryptHashData(hHash, @Bytes[0], BytesRead, 0) then Exit; 
     until False; 

     SigLen := SHA256_LEN; 
     SetLength(Bytes, SigLen); 
     Result := CryptGetHashParam(hHash, HP_HASHVAL, @Bytes[0], SigLen, 0); 
     finally 
     CryptDestroyHash(hHash); 
     end; 
    finally 
     CryptReleaseContext(hProv, 0); 
    end; 
    end; 

    var 
    Stream: TStream; 
    Bytes : TBytes; 
    sw : TStopwatch; 
    CryptoTicks : int64; 
    FileName : string; 

    {* CheckFileName *} 
    function CheckFileName: boolean; 
    begin 
     if (FileName='') then FileName := ParamStr(0); 
     Result := FileExists(FileName); 
     if not Result then Writeln('Invalid File name'); 
    end; 
    begin 
    repeat 
     Writeln('Please Enter a valid File name, empty for this Executable'); 
     Readln(FileName); 
    until CheckFileName; 

    try 
     Stream := TFileStream.Create(FileName, fmOpenRead + fmShareDenyNone); 
     try 
     WriteLn('Crypto - Calculating Checksum'); 
     sw.Start; 
     if not CryptoHash256(Stream, Bytes) then raise Exception.Create('Something Happened :)'); 
     sw.Stop; 
     Writeln(Bytes.BinToHex); 
     WriteLn('Elapsed: ' + sw.Elapsed.ToString); 
     CryptoTicks := sw.ElapsedTicks; 

     WriteLn('Delphi - Calculating Checksum'); 
     sw.Reset; sw.Start; 
     DelphiHash256(Stream, Bytes); 
     sw.Stop; 
     Writeln(Bytes.BinToHex); 
     WriteLn('Elapsed: ' + sw.Elapsed.ToString); 

     Writeln(Format('MS Crypto is %d%% faster', [(sw.ElapsedTicks-CryptoTicks) * 100 div CryptoTicks])); 
     finally 
     Stream.Free; 
     end; 
     Writeln('Hit <Enter> to exit'); 
     Readln; 
    except 
     on E: Exception do Writeln(E.ClassName, ': ', E.Message); 
    end; 

    end. 
+5

該代碼的其他問題包括硬轉換爲TBytesStream的一段引人矚目的代碼。對於Emba來說,質量對他們來說很重要,這讓我很擔心。 –

+0

你能提供一個實際的例子嗎?我發現了另一個修復程序(請參閱我的問題更新) – ar099968

+0

請參閱我的更新2,我是否以正確的方式實施了您的建議? – ar099968