I came across HMAC (Hash-based message authentication code) functions when developing a RESTful client application in Delphi. The RESTful Web Service API required me to send HMAC_SHA256 signatures (Base64 encoded) with each HTTP request.
HMAC functions take two parameters: a key and a message. The purpose of the HMAC function is to authenticate the message and guarantee the data integrity of the message.
The cryptographic strength of the HMAC function lies on the underlying hashing function that it uses: MD5, SHA1, SHA256, etc.
So, these functions are usually are termed HMAC_SHA256, HMAC_SHA1, HMAC_MD5 to connote the core hashing function being used.
The outcome of a HMAC function is basically an array of bytes, but it is usually represented as a hexadecimal string or encoded as a Base64 string. (The RESTful Web Service API needed the Base64 encoded output).
I Googled around for a bit, but I didn’t get a clean implementation of HMAC_SHA256 in Delphi (encoded as Base64). I glued together the pieces from some questions on StackOverflow and coded an Indy based implementation that uses generics to specify the core hashing function.
Brief description: I created a helper class called THMACUtils. Note that this class uses generics to indicate the hashing algorithm (TIdHMACSHA256, TIdHMACSHA1). Three functions are provided: the main thing happens in the HMAC(...) function; HMAC_HexStr(...) and HMAC_Base64(...) are simply decorations of the output.
unit HMAC;
interface
uses
System.SysUtils,
EncdDecd,
IdHMAC,
IdSSLOpenSSL,
IdHash;
type
THMACUtils<T: TIdHMAC, constructor> = class
public
class function HMAC(aKey, aMessage: RawByteString): TBytes;
class function HMAC_HexStr(aKey, aMessage: RawByteString): RawByteString;
class function HMAC_Base64(aKey, aMessage: RawByteString): RawByteString;
end;
implementation
class function THMACUtils<T>.HMAC(aKey, aMessage: RawByteString): TBytes;
var
_HMAC: T;
begin
if not IdSSLOpenSSL.LoadOpenSSLLibrary then Exit;
_HMAC:= T.Create;
try
_HMAC.Key := BytesOf(aKey);
Result:= _HMAC.HashValue(BytesOf(aMessage));
finally
_HMAC.Free;
end;
end;
class function THMACUtils<T>.HMAC_HexStr(aKey, aMessage: RawByteString): RawByteString;
var
I: Byte;
begin
Result:= '0x';
for I in HMAC(aKey, aMessage) do
Result:= Result + IntToHex(I, 2);
end;
class function THMACUtils<T>.HMAC_Base64(aKey, aMessage: RawByteString): RawByteString;
var
_HMAC: TBytes;
begin
_HMAC:= HMAC(aKey, aMessage);
Result:= EncodeBase64(_HMAC, Length(_HMAC));
end;
end.
Below there’s an example of how to use the THMACUtils class.
program HMACSample;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
HMAC,
IdHMACSHA1,
IdHashMessageDigest;
begin
try
Write('HMAC_SHA1("key", "message")'#9#9'= ');
Writeln(THMACUtils<TIdHMACSHA1>.HMAC_HexStr('key', 'message' ));
Writeln;
Write('HMAC_SHA256("key", "message")'#9#9'= ');
Writeln(THMACUtils<TIdHMACSHA256>.HMAC_HexStr('key', 'message' ));
Writeln;
Write('HMAC_SHA1_Base64("key", "message")'#9'= ');
Writeln(THMACUtils<TIdHMACSHA1>.HMAC_Base64('key', 'message' ));
Writeln;
Write('HMAC_SHA256_Base64("key", "message")'#9'= ');
Writeln(THMACUtils<TIdHMACSHA256>.HMAC_Base64('key', 'message' ));
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
The console application above looks like this:
HMAC Sample Application Delphi |
Hello. Please, can you convert this function to Delphi XE7??
ReplyDeleteI Have an error with in.
maxiducoli@gmail.com
Thank you!!!
Sorry. I don't have a Delphi XE7 copy.
Delete{
ReplyDeleteI'm a newbie, but this works and got the same results posted, in XE8 the evaluation version
}
...
implementation
uses IdGlobal;
class function THMACUtils.HMAC(aKey, aMessage: RawByteString): TBytes;
var
_HMAC: T;
temp, temp2 : TBytes; // some errors occurs, this are for debug
temp3 : TIdBytes;
begin
if not IdSSLOpenSSL.LoadOpenSSLLibrary then Exit;
_HMAC:= T.Create;
try
{
Use TBytes instead.
The WriteAllBytes method takes TBytes which is defined as TArray and so is an array of bytes, not just a single Byte.
var
OutputPath: string;
OutputBuffer: TBytes;
begin
// use SetLength to set the length of your OutputBuffer
// byte array, fill it somehow and then call WriteAllBytes
TFile.WriteAllBytes(OutputPath, OutputBuffer);
end; }
temp:= BytesOf(aKey);
_HMAC.Key := TIdBytes(temp); //BytesOf(aKey);
temp2:= BytesOf(aMessage);
//Result:= _HMAC.HashValue(BytesOf(aMessage));
temp3:= TIdBytes(temp2);
Result:= TBytes(_HMAC.HashValue(temp3));
finally
_HMAC.Free;
end;
end;
Hi, I got it working on XE8, but do you have a function for HMAC_MD5? Thank You.
ReplyDeleteThanks, I found it.
ReplyDeletefunction EncryptHMACMD5(Input, AKey : string) : TIdBytes;
begin
with TIdHMACMD5.Create do
try
Key := ToBytes(AKey);
Result := HashValue(ToBytes (Input));
finally
Free;
end;
end;
When I try to use TIdHMACSHA256 in Delphi 2010, the compiler tells me "Undeclared Identifier" Do you have any idea why D2010, with the indy installed, can't do the TIdHMACSHA256?
ReplyDeleteTIdHMACSHA1 works just fine.
When I try to use TIdHMACSHA256 in Delphi 2010, the compiler tells me "Undeclared Identifier" Do you have any idea why D2010, with the indy installed, can't do the TIdHMACSHA256?
ReplyDeleteTIdHMACSHA1 works just fine.