Data.Cloud.CloudAPI.pas has a class function TCloudSHA256Authentication.GetStreamToHashSHA256Hex(const Content: TStream): string; which returns the wrong SHA 256 to some file.
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 Return Error:
The provided x-amz-content-sha256 header does not match what was calculated
GetStreamToHashSHA256Hex seems to produce another sha256 from Amazon:
<ClientComputedContentSHA256>f43ee89e2b7758057bb1f33eb8546d4c2c118f2ab932de89dbd74aabc0651053</ClientComputedContentSHA256> <S3ComputedContentSHA256>3bbf5f864cc139cf6392b4623bd782a69d16929db713bffaa68035f8a5c3c0ce</S3ComputedContentSHA256>
I did some tests with myfile.zip (600 MB) ...
TIdHashSHA256 alternative from Indy returns the right SHA256 (the same from aws s3), for example:
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() from PHP will return the correct SHA256 (the same from aws s3), for example:
hash_file('sha256', 'C:\myfile.zip');
but THashSHA2 returns an invalid sha256, for example:
var LBytes : TBytes; Hash: THashSHA2; begin LBytes := TFile.ReadAllBytes('C:\myfile.zip'); Hash := THashSHA2.Create; Hash.Update(LBytes); Result := Hash.HashAsString; end;
why?
UPDATE
this is a bug fix. Import Data.Cloud.CloudAPI.pas into the project and rewrite these functions:
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;
UPDATE 2
I am also trying to implement a FredS suggestion, it works:
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;