delphidelphi-12-athens

How to hash a Delphi string using SHA256 to match a hash from C#?


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.


Solution

  • 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:

    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);