MD5 Hash creating different results from C# to Delphi

Hi all,
I am trying to emulate an encryption algorithm from C# in Delphi but keep coming up with different results.
The C# function goes like this (And I can’t change it without causing all sorts of issues):

using System.Text;
using System.Security.Cryptography;

static string Encrypt(string cleanString)
{
    Byte[] clearBytes = new UnicodeEncoding().GetBytes(cleanString);
    Byte[] hashedBytes = ((HashAlgorithm)CryptoConfig.CreateFromName("MD5")).ComputeHash(clearBytes);

    return BitConverter.ToString(hashedBytes);
}

Console.WriteLine(Encrypt("FredtheNed"));

which produces a result of:
66-F6-C6-07-9B-44-78-BE-A4-B0-6A-7B-66-3B-97-FF

A try in Delphi:

uses IdHashMessageDigest;

function Encrypt: string;
var
   idmd5 : TIdHashMessageDigest5;
begin
   idmd5 := TIdHashMessageDigest5.Create;
   try
      Result := idmd5.HashString('FredtheNed');
   finally
      idmd5.Free;
   end;
end;

Produces a result of:
81FC4410079C7004A22523F7886E9899

Which, apart from the dashes is obviously very different. Any ideas?
I have tried a number of different libraries in Delphi but they all return the same.

What happens if you convert the string to TBytes in your Delphi code and do an MD5 hash on those bytes?

If you’re still getting different results, double check that the bytes are actually the same between both implementations.

One way of verifying the result would be to use:

Which suggests the Delphi result is correct. There’s a number of different examples in C# here:

Incidentally, later versions (not sure from which version) of Delphi have hashing types in System.Hash. You could use:

THashMD5.GetHashString('FredtheNed')

1 Like

This might have something to do with it, you are probably not comparing the same thing.

I have checked the array’s and they are different.

I believe you are correct. Now to work out what that actually does. The documentation on docs.microsoft.com doesn’t match the usage above…

C# UnicodeEncoding() is UTF16, Indy’s default is ASCII unless you set it to something different.

Changing the Indy code to specify the Encoding type will solve your problem

idmd5.HashStringAsHex('FredtheNed', IndyTextEncoding_UTF16LE);

or getting the bytes as you do in the c# code would sort it out

bytes := IndyTextEncoding_UTF16LE.GetBytes(data);
Result := idmd5.HashBytesAsHex(bytes);

There are a number things that are worth noting with your example:

  1. Be wary of mixing the terms Encrypt and Hash, these are pure hash functions.
  2. When dealing with hashing functions you’re talking about bytes, being explict is specifying the encoding is very important and should not be left to underlying layers to guess what types of bytes you want to derive from a string or you’re going to end up with unexpected hashes / mismatched hashes. The way that you’ve done the code in C# is better than passing a string into a hash function which needs to select a encoding to use. Allowing underlying layers to determine those specifics may mean that if your code is run on different OS platforms or compilers will result in differing hashes.
2 Likes

Sorry it’s taken a while to get back to this. Perfect. Your answer has saved me a mass of pain. Thank you so much.