2017-04-18 149 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