Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MD5 base64 hash is different when compiled in Delphi as 32-bit or 64-bit - how can I make them the same?

I am using Delphi XE2 and the below code to create an MD5 base64 hash to use with Amazon MWS. It works if I compile it for 32-bit Windows, but if I compile for 64-bit windows the return hash changes. What is causing this and how can I change this so that they return the same hash?

function getMd5HashString(value: string): string;
var
  MessageDigest: TIdHashMessageDigest5;
  Content: TBytes;
begin
  Content := TEncoding.UTF8.GetBytes(value);
  MessageDigest:=TIdHashMessageDigest5.Create;
  Result:=Data.Cloud.CloudAPI.EncodeBytes64(MessageDigest.HashBytes(Content));
end;

Thansk in advance.

Edit:

I am using the above function in the following test;

procedure Button1Click(Sender: TObject);
begin
  Edit2.Text := getMd5HashString(Edit1.Text);
end;

I am passing

<?xml version="1.0" encoding="utf-8"?> 

as the string, just to test with. If I compile the program with a Target Platform of Windows 32-bit the returned hash is;

I3pK/R+hpYOKY1IQRviZbQ==

Whereas if I compile the program with a Target Platform of Windows 64-bit I get;

bmkkAOXGhLdDFCUuNBuSxw==

I hope that answers you David?

Edit2: Full program as suggested by David;

unit ContentHashTest;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.Cloud.CloudAPI, IdGlobal, IdHash, IdHashMessageDigest,
  IdCoder, IdCoderMIME, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    Edit2: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    function getMd5HashString(value: string): string;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit2.Text := getMd5HashString(Edit1.Text);
end;

function TForm1.getMd5HashString(value: string): string;
var
  MessageDigest: TIdHashMessageDigest5;
  Content: TBytes;
begin
  Content := TEncoding.UTF8.GetBytes(value);
  MessageDigest:=TIdHashMessageDigest5.Create;
  Result:=Data.Cloud.CloudAPI.EncodeBytes64(MessageDigest.HashBytes(Content));
end;

end.

That was my starting attempt. After David's suggestion below I changed this to;

unit ContentHashTest;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdGlobal, IdHash, IdHashMessageDigest,
  IdCoder, IdCoderMIME, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    Edit2: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    function getMd5HashString(value: string): string;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit2.Text := getMd5HashString(Edit1.Text);
end;

function TForm1.getMd5HashString(value: string): string;
var
  MessageDigest: TIdHashMessageDigest5;
  Content: TIdBytes;
begin
  Content := TIdTextEncoding.UTF8.GetBytes(value);
  MessageDigest := TIdHashMessageDigest5.Create;
  try
    Result := TIdEncoderMIME.EncodeBytes(MessageDigest.HashBytes(Content));
  finally
    MessageDigest.Free;
  end;
end;

end.

Unfortunately with the same differing results.

Win32 = I3pK/R+hpYOKY1IQRviZbQ==

Win64 = bmkkAOXGhLdDFCUuNBuSxw==

like image 265
Clint Cronwright Avatar asked Mar 27 '17 09:03

Clint Cronwright


1 Answers

I turned the extracts in the question into this complete program:

{$APPTYPE CONSOLE}

uses
  SysUtils,
  IdGlobal,
  IdCoderMIME,
  IdHashMessageDigest;

function getMd5HashString(value: string): string;
var
  MessageDigest: TIdHashMessageDigest5;
  Content: TIdBytes;
begin
  //Content := TIdTextEncoding.UTF8.GetBytes(value); // for older versions of Indy
  Content := IndyTextEncoding_UTF8.GetBytes(value);
  MessageDigest := TIdHashMessageDigest5.Create;
  try
    Result := TIdEncoderMIME.EncodeBytes(MessageDigest.HashBytes(Content));
  finally
    MessageDigest.Free;
  end;
end;

begin
  Writeln(getMd5HashString('<?xml version="1.0" encoding="utf-8"?>'));
  Readln;
end.

I'm using Indy to perform all of the conversions: text to UTF-8 bytes, MD5 hashing, and the base64 encoding. But you should use any libraries for this, since they should all give the same output.

This program produces the same output for 32 and 64 bit, as would be expected:

I3pK/R+hpYOKY1IQRviZbQ==

You report different behaviour though, so what could be the cause? I can think of the following possible explanations:

  1. You are not in fact passing the same input value to the function in both versions.
  2. TEncoding.UTF8.GetBytes is defective.
  3. TIdHashMessageDigest5.HashBytes is defective.
  4. Data.Cloud.CloudAPI.EncodeBytes64 is defective.

In any case, I don't see any sense in using Data.Cloud.CloudAPI to perform base64 encoding, although I have no reason to believe that it does not work. Since you are already using the Indy library, it makes more sense to do that uniformly.

I cannot tell from here which of the above explanations is the cause of what you report. You should be able to debug this further to find out. Look at the output of each step in the process to figure out where the behaviour varies.


Update: I've managed now to reproduce the error, but only by using the version of Indy that ships with XE2. I was using a more up-to-date version of Indy in my initial experiments.

The problem appears to be item 3 in my list. This gives incorrect results under the x64 compiler.

You should solve the problem by finding a different MD5 implementation. My recommendation is to do this by upgrading Indy.

like image 68
David Heffernan Avatar answered Nov 11 '22 23:11

David Heffernan