# A BlockHasher helper class

**Date:** 2016-05-23  
**Author:** Kees C. Bakker  
**Categories:** .NET Framework / C#, KeesTalksTech Utility Pack, Projects  
**Original:** https://keestalkstech.com/block-hasher-helper-class/

![A BlockHasher helper class](https://keestalkstech.com/wp-content/uploads/2016/05/hashing2.jpg)

---

There are a few instances in which you'll need to hash a combination of data. You might resort to creating one big string and hashing that. It has a clear disadvantage from a memory and processing point of few. It might even be impractical when files or streams are involved. That's why I created a BlockHasher utility class that helps to generate these types of hashes.

## HashAlgorithm

.NET offers the following through the [HashAlgorithm](https://msdn.microsoft.com/en-us/library/system.security.cryptography.hashalgorithm(v=vs.110).aspx) class:

## Wanted: more features

Basically you want the following features in this scenario:

To make things more readable, you might want to return the hash as a hexadecimal string or - a shorter, but less frequently used - base64 string. I find myself always using the same lines of code, so I added it to the class.

## BlockHasher

Without further ado, let's see the class:

```cs
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

/// <summary>
/// Helps with block hashing.
/// </summary>
public class BlockHasher : IDisposable
{
	private HashAlgorithm _hasher;
	/// <summary>
	/// Initializes a new instance of the <see cref = "BlockHasher"/> class.
	/// </summary>
	/// <param name = "name">The name.</param>
	public BlockHasher(string name)
	{
		if (String.IsNullOrEmpty(name))
		{
			throw new ArgumentNullException("name");
		}

		_hasher = HashAlgorithm.Create(name);
	}

	/// <summary>
	/// Initializes a new instance of the <see cref = "BlockHasher"/> class.
	/// </summary>
	/// <param name = "algorithm">The algorithm.</param>
	public BlockHasher(HashAlgorithm algorithm)
	{
		if (algorithm == null)
		{
			throw new ArgumentNullException("algorithm");
		}

		_hasher = algorithm;
	}

	/// <summary>
	/// Indicates how to format the string.
	/// </summary>
	public enum StringFormat
	{
		Hexadecimal,
		Base64
	}

	/// <summary>
	/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
	/// </summary>
	public void Dispose()
	{
		_hasher.Dispose();
	}

	/// <summary>
	/// Gets the hash.
	/// </summary>
	/// <returns>A byte array with the hash.</returns>
	public byte[] GetHash()
	{
		_hasher.TransformFinalBlock(new byte[0], 0, 0);
		return _hasher.Hash;
	}

	/// <summary>
	/// Gets the string hash.
	/// </summary>
	/// <param name = "format">The format.</param>
	/// <returns>The hash.</returns>
	public string GetStringHash(StringFormat format = StringFormat.Hexadecimal)
	{
		var hash = GetHash();
		switch (format)
		{
			case StringFormat.Hexadecimal:
			{
				string result = "";
				for (int i = 0; i < hash.Length; i++)
				{
					result += hash[i].ToString("x2");
				}

				return result;
			}

			case StringFormat.Base64:
			{
				return Convert.ToBase64String(hash);
			}

			default:
			{
				throw new NotSupportedException();
			}
		}
	}

	/// <summary>
	/// Transforms the specified stream.
	/// </summary>
	/// <param name = "stream">The stream.</param>
	/// <param name = "bufferSize">Size of the buffer.</param>
	public void Transform(Stream stream, int bufferSize = 8 * 1024)
	{
		if (stream == null)
		{
			throw new ArgumentNullException("stream");
		}

		byte[] buffer = new byte[bufferSize];
		int read;
		while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
		{
			Transform(buffer, 0, read);
		}
	}

	/// <summary>
	/// Transforms the specified string. Assumes UTF8 encoding.
	/// </summary>
	/// <param name = "str">The string.</param>
	public void Transform(string str)
	{
		Transform(str, Encoding.UTF8);
	}

	/// <summary>
	/// Transforms the specified string using the specified encoding.
	/// </summary>
	/// <param name = "str">The string.</param>
	/// <param name = "encoding">The encoding.</param>
	public void Transform(string str, Encoding encoding)
	{
		var bytes = encoding.GetBytes(str);
		Transform(bytes);
	}

	/// <summary>
	/// Transforms the specified buffer.
	/// </summary>
	/// <param name = "buffer">The buffer.</param>
	public void Transform(byte[] buffer)
	{
		Transform(buffer, 0, buffer.Length);
	}

	/// <summary>
	/// Transforms the specified buffer.
	/// </summary>
	/// <param name = "buffer">The buffer.</param>
	/// <param name = "offset">The offset.</param>
	/// <param name = "length">The length.</param>
	public void Transform(byte[] buffer, int offset, int length)
	{
		_hasher.TransformBlock(buffer, offset, length, null, 0);
	}

	/// <summary>
	/// Transforms the specified stream asynchronously.
	/// </summary>
	/// <param name = "stream">The stream.</param>
	/// <param name = "bufferSize">Size of the buffer.</param>
	/// <returns>The task.</returns>
	public async Task TransformAsync(Stream stream, int bufferSize = 8 * 1024)
	{
		byte[] buffer = new byte[bufferSize];
		int read;
		while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
		{
			Transform(buffer, 0, read);
		}
	}
}
```

## API Signature Hashing

Lots of API's use a [HMAC](https://www.wikiwand.com/en/Hash-based_message_authentication_code) SHA1 signature to authenticate messages. I've created some example code on how the BlockHasher can be used to generate the signature:

```cs
[TestCategory("UnitTest")]
[TestMethod]
public void BlockHasher_ApiRequest_HMACSHA1()
{
	//api information
	string apiKey = "DvTVukOITB5GJ5r79IEy3J9LALZ1LLex";
	string clientSecret = "5d18SSM38x1lGjMD5qCX1FJGsw4jJ12t";
	//request data
	Dictionary<string, string> request = new Dictionary<string, string>();
	request.Add("Message", "Hello world!");
	request.Add("PublishDate", "1984-09-12");
	request.Add("Tags", "first,message,ever");
	request.Add("Active", "true");
	string signature = null;
	using (var algorithm = new HMACSHA1(Encoding.ASCII.GetBytes(clientSecret)))
	{
		var hasher = new BlockHasher(algorithm);
		var orderedKeys = request.Keys.OrderBy(k => k);
		//hash keys
		foreach (var k in orderedKeys)
		{
			hasher.Transform(k);
			hasher.Transform(",");
		}

		//hash values
		foreach (var k in orderedKeys)
		{
			hasher.Transform(request[k]);
			hasher.Transform(",");
		}

		hasher.Transform(apiKey);
		signature = hasher.GetStringHash(BlockHasher.StringFormat.Base64);
	}

	Assert.AreEqual("279y647EmEXFNFH2ZtNesIc6Skw=", signature);
}
```

## Async stream hashing

Hashing big files using async streams is pretty easy:

```cs
public async Task<string> HashFile(string path, string algorithm = "md5")
{
	using (var file = File.OpenRead(path))
	{
		using (var blockhasher = new BlockHasher("md5"))
		{
			await blockhasher.TransformAsync(file);
			return blockhasher.GetStringHash();
		}
	}
}
```

## Wrap up

So that's it. Be sure to [visit the class on GitHub](https://github.com/KeesCBakker/KeesTalksTech-Utility-Pack/blob/master/KeesTalksTech-Utility-Pack/KeesTalksTech.Utilities/Hashing/BlockHasher.cs) if you think it should be changed / expanded.
