HMAC functions in Delphi (HMAC_SHA256, HMAC_SHA1)

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
HMAC Sample Application Delphi

7 comments:

  1. Hello. Please, can you convert this function to Delphi XE7??
    I Have an error with in.
    maxiducoli@gmail.com
    Thank you!!!

    ReplyDelete
  2. {
    I'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;

    ReplyDelete
  3. Hi, I got it working on XE8, but do you have a function for HMAC_MD5? Thank You.

    ReplyDelete
  4. Thanks, I found it.

    function EncryptHMACMD5(Input, AKey : string) : TIdBytes;
    begin
    with TIdHMACMD5.Create do
    try
    Key := ToBytes(AKey);
    Result := HashValue(ToBytes (Input));
    finally
    Free;
    end;
    end;

    ReplyDelete
  5. 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?

    TIdHMACSHA1 works just fine.

    ReplyDelete
  6. 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?

    TIdHMACSHA1 works just fine.

    ReplyDelete