Using HMACs to authenticate a hash in .NET

In this post we learnt about using hashing in .NET. Hashes provide a one-way encryption of a message where the hash value ideally cannot be “unhashed”, i.e. we cannot build the original string from it. A hash or message digest helps us verify whether the message has been tampered with by a third party after it was sent to us.

We can take a step further and add an extra layer of security on our hashes. After all a message and its hash could originate from anyone. How can we verify the authenticity of the message as well? That’s where Hashed Message Authentication Codes, also called HashMACs or HMACs enter the picture.

Suppose that you get the following message with its hash: “Let’s meet at 7pm in front of the cinema, John”. You examine the hash and come to the conclusion that the message hasn’t been modified. Then you get another message: “Let’s meet at 6pm instead.” Hmmm, OK, that’s unusual, John normally doesn’t change his mind so quickly, did the message really come from him?

HMACs are also hashes but they are given an extra round of calculation using a random hash key. If both parties have access to this key then the message can be verified and authenticated using the agreed key and hash algorithm. If the receiver calculates a HashMac which is different from the one sent along with the message then authentication fails.

HashMacs in .NET derive from the HMAC abstract base class in the System.Security.Cryptography namespace. There are implementations corresponding to the various hashing algorithms we saw before such as MD5 and SHA-256.

For the demo we’ll need a true random number generator that we saw in this post. Here’s a slightly modified helper class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;

namespace RandomNumberGenerator
{
    public class RNG
    {
		public string GenerateRandomCryptographicKey(int keyLength)
		{			
			return Convert.ToBase64String(GenerateRandomCryptographicBytes(keyLength));
		}

		public byte[] GenerateRandomCryptographicBytes(int keyLength)
		{
			RNGCryptoServiceProvider rngCryptoServiceProvider = new RNGCryptoServiceProvider();
			byte[] randomBytes = new byte[keyLength];
			rngCryptoServiceProvider.GetBytes(randomBytes);
			return randomBytes;
		}
	}
}

The ComputeHmac function in the HMACService allows us to pass in any HMAC implementation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace HashingAlgos
{
	public class HMACService
	{
		public string ComputeHmac(string message, HMAC hmac)
		{
			return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(message)));
		}
	}
}

All concrete implementations of HMAC have a ComputeHash method that accepts a byte array. In the above code we convert the incoming message into an array of bytes and return the calculated bytes as a string for easier visualisation.

Here’s some test code that calculates the SHA-1, SHA-256 and SHA-512 HMACs with a 64-bit hash key:

private void TestHmac()
{
	RNG rng = new RNG();
	byte[] hashKey = rng.GenerateRandomCryptographicBytes(64);
	HMACService hmacService = new HMACService();

	string message = "This is another simple message";

	HMAC hmacSha1 = new HMACSHA1(hashKey);
	HMAC hmacSha256 = new HMACSHA256(hashKey);
	HMAC hmacSha512 = new HMACSHA512(hashKey);
	string messageHmacSha1 = hmacService.ComputeHmac(message, hmacSha1);
	string messageHmacSha256 = hmacService.ComputeHmac(message, hmacSha256);
	string messageHmacSha512 = hmacService.ComputeHmac(message, hmacSha512);

	Console.WriteLine(messageHmacSha1);
	Console.WriteLine(messageHmacSha256);
	Console.WriteLine(messageHmacSha512);
}

Here are the HMAC values from top to bottom:

TTz0JeJnjBnG9MVIyZXwfuHPYkM=
fsXBhiPfijlURpR1EneOPxjF4LkYAwnPUc983aeW+PE=
CiC8vIoCyB5i8qXMGTJ4lSiTSw+A4W+m7MMY/hdNcUoTdSWqkonoPh1fdJ66qX/aTSA2JxnsGNwbaurIkNBFvw==

You can view the list of posts on Security and Cryptography here.

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a comment

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.