MD5 Hashing in Delphi 2009
Asked Answered
P

7

7

In borland delphi 7 and even in delphi 2007 everything worked, but in delphi 2009 it just returns the wrong hash!

I use wcrypt2 script (http://pastebin.com/m2f015cfd)

Just have a look:

string : "123456"

hash:

Delphi 7 : "e10adc3949ba59abbe56e057f20f883e" - real hash.
Delphi 2007 : "e10adc3949ba59abbe56e057f20f883e" - real hash too.
And... Delphi 2009 : "5fa285e1bebe0a6623e33afc04a1fbd5" - WTF??

I've tried a lot of md5 scripts, but delphi 2009 does the same with all of them. Any help? Thanks.

Purposely answered 25/12, 2008 at 9:7 Comment(0)
A
31

Your library is not Unicode aware. Just passing it an AnsiString won't be enough because it probably uses strings internally to store data.

You could try to update that library, wait for the author to update it, or just use the MessageDigest_5.pas that ships with Delphi 2009. It is in the source\Win32\soap\wsdlimporter folder, which you will need to either add to your path, or explicitly include it in your project.

Here is some sample code using it in Delphi 2009:

uses Types, MessageDigest_5;

procedure TForm16.Edit1Change(Sender: TObject);
var
  MD5: IMD5;
begin
  MD5 := GetMD5;
  MD5.Init;
  MD5.Update(TByteDynArray(RawByteString(Edit1.Text)), Length(Edit1.Text));
  Edit2.Text := LowerCase(MD5.AsString);
end;

And you are in business:

MD5(123456) = e10adc3949ba59abbe56e057f20f883e

You could wrap it in a simple function call if you wanted to. It is important you cast to a RawByteString before casting to a TByteDynArray since the RawByteString cast drops all the extra Unicode characters. Granted if the edit contains Unicode characters then you could end up with bad data.

Keep in mind that GetMD5 is returning an interface, so it is reference counted, etc.

Merry Christmas!

Armbruster answered 25/12, 2008 at 11:11 Comment(1)
The casts here are completely bogus. You cannot reasonably cast a UnicodeString to RawByteString. And you absolutely cannot cast RawByteString to byte array. This answer is so badly wrong, and so erroneously voted on that it should simply be deleted. The right solution uses TEncoding.GetBytes.Calicut
G
5

Before someone can comment on hashing algorithms, it helps if they have at least a fundamental understanding of the underlying concepts and principles. All of the responses so far which have focused on endless typecasting are completely overkill, but even worse, will result in unreliable results if a unicode string is being hashed.

The first thing you need to understand is that hashing and encryption algorithms operate at the byte-level. That means they don't care what you're hashing or encrypting. You can hash integers, chars, plain ASCII, full unicode, bytes, longwords, etc etc. The algorithm doesn't care.

When working with strings, the ONLY thing you have to ensure is that the internal function of your hashing library returns an AnsiString in the function which spits out your resulting hash. That's it. That's all that matters.

Your actual code for YOUR project can (and should) be based on normal string input, which maps to unicodestring in Delphi 2009. You shouldn't be typecasting anything to ansistring or rawbytestring. By doing so, you immediately create a broken hash if and when the user tries to hash anything outside the scope of the ANSI character set. And in the world of hashing, a broken hash is both unreliable AND insecure.

Groyne answered 9/4, 2009 at 23:23 Comment(1)
Thanks for the voice of reason. +1.Chug
L
1

Have you checked that your library has been correctly updated for D2009 and unicodification? I kinda doubt the same code would do D7/D2007 and D2009 for this sort of things.

Leatherleaf answered 25/12, 2008 at 9:45 Comment(0)
G
1

It is obvious that your lib is not unicode enabled.

Convert your string to AnsiString or RawByteString or UTF8String by declaring temp AnsiString and assign your uniode string to it.

Note that if you are using unicode specific chars that can't be translated to single codepage, you should convert your string to UTF8.

Then call MD5(PAnsiChar(YourTempString)).

Check that your lib may have PWideChar or UNICODE declarations, to skip this.

Goatsbeard answered 25/12, 2008 at 10:41 Comment(0)
J
1

If you have wcrypt2.pas, use this function.

function md5ansi(const Input: AnsiString): AnsiString;
var
  hCryptProvider: HCRYPTPROV;
  hHash: HCRYPTHASH;
  bHash: array[0..$7f] of Byte;
  dwHashBytes: Cardinal;
  pbContent: PByte;
  i: Integer;
begin
  dwHashBytes := 16;
  pbContent := Pointer(PAnsiChar(Input));
  Result := '';

  if CryptAcquireContext(@hCryptProvider, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT or CRYPT_MACHINE_KEYSET) then
  begin
    if CryptCreateHash(hCryptProvider, CALG_MD5, 0, 0, @hHash) then
    begin
      if CryptHashData(hHash, pbContent, Length(Input) * sizeof(AnsiChar), 0) then
      begin
        if CryptGetHashParam(hHash, HP_HASHVAL, @bHash[0], @dwHashBytes, 0) then
        begin
          for i := 0 to dwHashBytes - 1 do
          begin
            Result := Result + AnsiString(Format('%.2x', [bHash[i]]));
          end;
        end;
      end;
      CryptDestroyHash(hHash);
    end;
    CryptReleaseContext(hCryptProvider, 0);
  end;

  Result := AnsiString(AnsiLowerCase(String(Result)));
end;
Joggle answered 28/12, 2010 at 9:36 Comment(1)
@JW Kim, welcome to StackOverflow. Please, take some time reading the faq. If you have previous experience with forums or newsgoups, you'll notice the site works in a different way. You're answering a rather old question with an accepted answer, thus it is valid, it is not used. To publish source code you have to indent it by 4 space characters to make it look source code. :)Rebak
A
0

Are you perchance casting a generic string (which in Delphi 2009 is a UnicodeString) to a PAnsiChar and passing that into the hash function? That will not work. You first must cast the string into an AnsiString and then cast that one to PAnsiChar, a la:

PAnsiChar(AnsiString('123456'))

Also, try using RawByteString instead of AnsiString like dmajkic suggested. Avoid UTF8String since that's not an AnsiString and any characters outside the ASCII range (0..127) might get reinterpreted into multibyte characters.

Afebrile answered 25/12, 2008 at 9:37 Comment(0)
C
-3

In Jim's answer:

if we change

MD5.Update(TByteDynArray(RawByteString(Edit1.Text)), Length(Edit1.Text));

to

MD5.Update(TByteDynArray(RawByteString(Edit1.Text)), Length(RawByteString(Edit1.Text)));

will support better while Chinese characters exists.

Chitwood answered 18/3, 2009 at 8:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.