Santanu Paul's Digital Garden

Blog

Modern Cryptography best practices for building secure applications on .NET Core Chapter 1 - Data Integrity and Authenticity with Hashing and HMAC

Published 2019-08-27,
in  
.NET Core

In this chapter of our series about understanding the basics of Cryptography on .NET Core, we will be exploring the concepts of Hashing, and how it can be used for Authentication.

Overview

In software applications, we can think of Hashing as a transformation method, where we take as input a bunch of data (bits and bytes), and generate a fixed-length output.

For a particular algorithm of hashing, the output that gets generated is of a fixed length - no matter how long the input data is.

Also, the algorithm ensures that two different inputs will NEVER generate the same output value.

Last but not least, one should not be able to re-generate the original message from the output of hashing.

These two traits of Hashing makes it an ideal candidate to add integrity to a data.

So when someone sends data to another person over the internet, they will apply a Hash function and get the unique hash output - also known as the Checksum, or Digest. This checksum will be sent along with the original data.

On the recipient side, when someone receives this data, they will apply the same Hash function and compare it with the checksum that they received.

If they are identical, then the data was received without any contamination. If not, then the data that was received is not the same as what was sent.

MD5 and SHA-2 are the two most common Hashing techniques today.

MD5 (Message Digest Algorithm)

MD5 was designed in 1991, by Ronald Rivest, in 1991. It replaced MD4.

Today, it is not recommended to use MD5, as it suffers from many vulnerabilities that has been exploited, thereby making it unsuitable for verifying data integrity.

What is interesting is that despite its flaws, MD5 continues to be used widely for data integrity.

It is things like this, that we as developers need to be cognizant of.

Sometimes developers would use MD5 in their applications because they had used it in some of their older projects, a decade back.

Sometimes, they would choose a 3rd party library, that would do hashing/data integrity checks for them, and internally those services would be using MD5.

Understanding a few basics of Cryptography helps us choose better options, especially in greenfield applications.

To use MD5 hashing in your application, you can use System.Security.Cryptography.MD5 class.

Note: The length of the output will always be 128-bit/16-bytes.

using System;
using System.Security.Cryptography;
using System.Text;

public static class HashUtils
{
    public static byte[] Md5(byte[] data)
    {
        using (var md5 = MD5.Create()) {
            return md5.ComputeHash(data);
        }
    }

    public static string Md5(string data)
    {
        return Convert.ToBase64String(Md5(Encoding.UTF8.GetBytes(data)));
    }
}

SHA (Secure Hash Algorithm)

SHA, or Security Hash Algorithms are a set of hashing algorithms developed by the National Institute of Standards and Technology (NIST). It has multiple variants like SHA-1, SHA-2 and SHA-3.

SHA-3 is not currently supported in .NET framework, but you can probably use a 3rd party library to use SHA-3 in your .NET Core application.

SHA-1 generates a 20-byte hash output. It has some vulnerabilities and is therefore no longer recommended.

SHA-2 has replaced SHA-1. In this family, SHA-256, and SHA-512 are most widely used. They generate outputs (digests), that are 256 bits (32 bytes) and 512 bits (64 bytes) long, respectively.

using System;
using System.Security.Cryptography;
using System.Text;

public static class HashUtils
{
    public static byte[] Sha256(byte[] data)
    {
        using (var sha256 = SHA256.Create()) {
            return sha256.ComputeHash(data);
        }
    }

    public static string Sha256(string data)
    {
        return Convert.ToBase64String(Sha256(Encoding.UTF8.GetBytes(data)));
    }

    public static byte[] Sha512(byte[] data)
    {
        using (var sha512 = SHA512.Create()) {
            return sha512.ComputeHash(data);
        }
    }

    public static string Sha512(string data)
    {
        return Convert.ToBase64String(Sha512(Encoding.UTF8.GetBytes(data)));
    }
}

Extending Hashing to achieve Authentication

We can extend the basic premise of Hashing, i.e., to allow verification of the integrity of a data, into something that can verify the authenticity of the data.

This technique is known as Hash-based Message Authentication Code (HMAC).

In Hashing, we take DATA, pass it through the Hash-function, and get the Digest.
In HMAC, we take DATA, as well as a secret-Key, which is then passed through the Hash-function, to get the Digest.

To re-calculate the digest, one must know the secret key (along with the data, obviously). As a result, this technique sets up a framework using which we can verify the Authentication of a message.

Essentially, the flow would be something like this:

  1. Alice wants to send a message to Bob. Along with the message, she generates an HMAC code, using the original message, and a secret key.
  2. She sends the message, as well as the HMAC code to Bob.
  3. When Bob gets the message, he regenerates the HMAC code, using the same key that Alice had used.
  4. If the calculated HMAC code is identical with the one Bob received in the message, it authenticates the fact that Alice was the sender of the message.
  5. On the other hand, a man-in-the-middle tries to imitate Alice and sends a message to Bob.
  6. This man, does not know the secret-key, that was used, to generate the HMAC code - so he uses his own.
  7. On the receiving side, Bob will calculate HMAC of the message using Alice's key, and therefore the HMAC code that he calculates won't match the one in the message, allowing Bob to reject it.

Of course, one question that remains unanswered, is how will Alice's share her secret key to Bob?
We will go into the details of this problem in later chapters. For now, we assume that Alice and Bob have secretly, and securely shared their secret keys.

In addition to the data, HMAC code's unique-ness now depends on this secret-key as well, and this allows us to perform authentication of the data.

It is of paramount importance, that sender chooses a strong key, to be able to guard against brute-force attacks.

In later chapters, we will see how to generate strong passwords, automatically via code.

using System;
using System.Security.Cryptography;
using System.Text;

public static class HMAC 
{
    public static string Hmac(string data, string key, bool hashViaSha256)
    {
        byte[] keyBytes = Encoding.UTF8.GetBytes(key);

        if (hasViaSha256) return Convert.ToBase64String(HmacSha256(dataBytes, key));
        else return Convert.ToBase64String(HmacSha512(dataBytes, key));
    }

    public static byte[] HmacSha256(byte[] toBeHashed, byte[] key)
    {
        using (var hmac = new HMACSHA256(key)) {
            return hmac.ComputeHash(toBeHashed);
        }
    }

    public static byte[] HmacSha512(byte[] toBeHashed, byte[] key)
    {
        using (var hmac = new HMACSHA512(key)) {
            return hmac.ComputeHash(toBeHashed);
        }
    }
}

The above code shows how to generate HMAC code, from an input data, and a secret key.

I usually have 2 variants of these util functions, one that takes plain strings, and others that can take any binary data, and generate the HMAC code.

If you wish to implement the ability to verify Authenticity of messages in your application, you can these util methods to calculate the HMAC code on the sender side, and then again generate them on the recipient side, and then verify if they are identical.

Summary

  1. Ensure the integrity of transmitted data using Hashing algorithms
    • Use SHA-256 or SHA-512 as your choice of the Hashing algorithm.
    • Avoid MD5, if you can, it has vulnerabilities
  2. If you need to implement authenticity of data, use HMAC
    • Use a strong key
    • Use SHA-256 or SHA-512 as the Hashing algorithm for HAMC
Share this article:

All content © Santanu Paul

HomeBlogContact