In C#, we have this function:
public string GetStringSha256Hash(string text, Encoding coding)
{
string result = "";
if (text != null)
{
using (SHA256 sHA256Managed = SHA256.Create())
{
byte[] bytes = coding.GetBytes(text);
byte[] array = sHA256Managed.ComputeHash(bytes);
result = BitConverter.ToString(array).Replace("-", string.Empty);
}
}
return result;
}
I am trying to do the same in Delphi, but keep getting different results.
Something as simple as this:
Result := THashSHA2.GetHashString(Text);
returns a different result.
I suspect because in C# we always use UTF-8, but I cannot find how to use encoding in Delphi.
I tried this:
var
Hash: TIdHashSHA256;
begin
Hash := TIdHashSHA256.Create;
try
SHA256Hash := Hash.HashString(Text, TEncoding.UTF8);
but it gives a compile error
[dcc32 Error] Unit1.pas(216): E2010 Incompatible types: 'IIdTextEncoding' and 'TEncoding'
What is the proper way of doing this in Delphi?
The value 12345
returns 1445217533E7D4D0CFFD9109C4EDB60D62A02C0F0DE9537BE44F5E00D348EB4F
in C#, but 5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5
in Delphi.
I want the Delphi version to return the same value as the C# version.
Hashes are calculated on bytes, not on text characters. Any code you use to hash a string will need to encode the characters into bytes before then calculating the hash on the bytes. When hashing strings across different systems, you need to make sure they are using the same byte encoding.
The C# code is encoding the input string
to a byte array via coding.GetBytes(text)
. You did not indicate which Encoding
is being passed to GetStringSha256Hash()
, but the C# hash result you did show is what you get when hashing a string using UTF-16LE (ie Encoding.Unicode
).
When using the Delphi RTL's THashSHA2
class, it does not allow you to specify a byte encoding when hashing a string
. The GetHashString()
method that takes in a string
is hard-coded to use TEncoding.UTF8
. That is consistent with the Delphi hash result you have shown. To use a different byte encoding, alternative options are to either:
use the THashSHA2.GetHashString()
overload that takes a TStream
, and then you can pass in a stream containing UTF-16LE encoded bytes, eg:
function GetStringSha256Hash(const Text: string; Encoding: TEncoding): string;
begin
Result := '';
if Text <> '' then
begin
var Stream := TStringStream.Create(Text, Encoding);
try
Result := THashSHA2.GetHashString(Stream);
finally
Stream.Free;
end;
end;
end;
var SHA256Hash := GetStringSha256Hash('1235', TEncoding.Unicode);
use the THashSHA2.Update()
method, passing it a UTF-16 encoded byte array, eg:
function GetStringSha256Hash(const Text: string; Encoding: TEncoding): string;
begin
Result := '';
if Text <> '' then
begin
var Hash := THashSHA2.Create(TSHA2Version.SHA256);
Hash.Update(Encoding.GetBytes(Text));
Result := Hash.HashAsString;
end;
end;
var SHA256Hash := GetStringSha256Hash('1235', TEncoding.Unicode);
When using Indy's TIdHashSHA256
class, the compiler error is caused by the fact that Indy does not use the Delphi RTL's TEncoding
class 1, it instead uses its own IIdTextEncoding
interface (because Indy still supports pre-Unicode versions of Delphi which don't have the SysUtils.TEncoding
class available), eg:
function GetStringSha256Hash(const Text: string; const Encoding: IIdTextEncoding): string;
begin
Result := '';
if Text <> '' then
begin
var Hash := TIdHashSHA256.Create;
try
Result := Hash.HashString(Text, Encoding);
finally
Hash.Free;
end;
end;
end;
var SHA256Hash := GetStringSha256Hash('1235', IndyTextEncoding_UTF16LE);
1: If you want to use a SysUtils.TEncoding
with Indy, you can wrap it into an IIdTextEncoding
using the IndyTextEncoding()
function in the IdGlobal
unit, eg:
function GetStringSha256Hash(const Text: string; Encoding: TEncoding): string;
begin
Result := '';
if Text <> '' then
begin
var Hash := TIdHashSHA256.Create;
try
Result := Hash.HashString(Text, IndyTextEncoding(Encoding));
finally
Hash.Free;
end;
end;
end;
var SHA256Hash := GetStringSha256Hash('1235', Encoding.Unicode);