Complete C# String Hash Functions

Generate message digest with MD5, SHA1, SHA256, SHA384, and SHA512 hash algorithms by using System.Security.Cryptography library.

For a while I’ve been seeing my blog traffic and surprisingly there are some of you are landed here by searching about MD5 string hash in C#, specifically used for storing password. I wrote the first draft of that tutorial when I was still in college while learning C# as a student, so it’s really old tutorial.

As we know, MD5 isn’t recommended anymore because it is designed to be very fast and efficient. By using modern computers and techniques, it is possible to “brute force” the hash output, in order to retrieve the original input. Because of this, many security experts suggest not to use it for password hashing.

This suggestion isn’t limited to MD5 only, but also SHA1 and possibly other SHA* algorithms, too.

If you’re here to find the best practice to store password, try searching for slow hashes like bcrypt (I’ll write about that later).

This time I’d like to show you how easy it is to generate SHA* (and MD5) string hash in C#. SHA* especially is widely being used to check content integrity.

You can still use SHA* to store password, nobody can’t stop you for that. But I suggest to only use it for non-critical business application or just for learning experience, for example school project. But make sure to use better hash algorithm if you’re building a widely-used commercial software.

Creating An Empty Console Application

Let’s start this tutorial by creating a new project. I’m going to use .NET Core for this, but you can also use any .NET Framework version 3.5 or more. Create a new folder and move to it. Let’s name it StringHasher.

mkdir StringHasher
cd StringHasher

Under this folder, let’s create a new console application.

dotnet new console

After the project is created, let’s create a new file Hasher.cs. We will fill this class with our hash functions.

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

namespace StringHasher
{
    public static class Hasher
    {
        // We create our functions here
    }
}

Let’s first create a universal hasher function call it GenerateHashString. This function role will be used as our base, which mean for every algorithm available, we’re going to call this function to generate hash output. We need to pass two parameters: where algo is hash algorithm we’re going to use and text is user supplied string.

private static string GenerateHashString(HashAlgorithm algo, string text)
{
    // Compute hash from text parameter
    algo.ComputeHash(Encoding.UTF8.GetBytes(text));

    // Get has value in array of bytes
    var result = algo.Hash;

    // Return as hexadecimal string
    return string.Join(
        string.Empty,
        result.Select(x => x.ToString("x2")));
}

Let’s create MD5 first by creating an MD5CryptoServiceProvider object. We pass this object and user-supplied text to function we wrote earlier.

public static string MD5(string text)
{
    var result = default(string);

    using (var algo = new MD5CryptoServiceProvider())
    {
        result = GenerateHashString(algo, text);
    }

    return result;
}

Now with similar pattern, we create other functions for SHA1, SHA256, SHA384, and SHA512. You can see following snippet for those SHA* functions.

public static string SHA1(string text)
{
    var result = default(string);

    using (var algo = new SHA1Managed())
    {
        result = GenerateHashString(algo, text);
    }

    return result;
}

public static string SHA256(string text)
{
    var result = default(string);

    using (var algo = new SHA256Managed())
    {
        result = GenerateHashString(algo, text);
    }

    return result;
}

public static string SHA384(string text)
{
    var result = default(string);

    using (var algo = new SHA384Managed())
    {
        result = GenerateHashString(algo, text);
    }

    return result;
}

public static string SHA512(string text)
{
    var result = default(string);

    using (var algo = new SHA512Managed())
    {
        result = GenerateHashString(algo, text);
    }

    return result;
}

After the class is ready, we can continue to next step and test it.

See It in Action

Let’s test it by creating a simple command line application. Open your Program.cs and we start editing our Main function.

using System;
using System.Collections.Generic;
using System.Linq;

namespace StringHasher
{
    class Program
    {
        static void Main(string[] args)
        {
            // We put our code here
        }
    }
}

First let’s keep our list of hash functions in a Dictionary. We can do this by creating a dictionary with string as the key and Func<string, string> as the value.

var algorithms = new Dictionary<string, Func<string, string>>
{
    {nameof(Hasher.MD5).ToLower(), Hasher.MD5},
    {nameof(Hasher.SHA1).ToLower(), Hasher.SHA1},
    {nameof(Hasher.SHA256).ToLower(), Hasher.SHA256},
    {nameof(Hasher.SHA384).ToLower(), Hasher.SHA384},
    {nameof(Hasher.SHA512).ToLower(), Hasher.SHA512},
};

Next, we need to make some rule of checks. We need exactly 2 arguments: algorithm and text. Check algorithm if it’s in Dictionary object we created earlier, make sure it exists. If one of above rules isn’t met, we quit the program.

if(args.Length < 2)
{
    Console.WriteLine("You need 2 arguments.");
    return;
}

if(args.Length > 2)
{
    Console.WriteLine("Too many arguments.");
    return;
}

var algo = args[0].ToLower();
var text = args[1];

if(!algorithms.ContainsKey(algo))
{
    Console.WriteLine($"Algorithm {algo} is unknown.");
    return;
}

Finally, we can get string hash and print it using following snippet.

var result = algorithms[algo].Invoke(text);

Console.WriteLine(result);

Let’s build it and open your terminal. Head to your binary folder, usually at bin\Debug\net***\. You can run this binary using following format.

dotnet StringHasher.dll ALGO TEXT

Let’s test it by using a string mypassword. You can see the following snippet to see expected results.

$ dotnet StringHasher.dll md5 mypassword
34819d7beeabb9260a5c854bc85b3e44

$ dotnet StringHasher.dll sha1 mypassword
91dfd9ddb4198affc5c194cd8ce6d338fde470e2

$ dotnet StringHasher.dll sha256 mypassword
89e01536ac207279409d4de1e5253e01f4a1769e696db0d6062ca9b8f56767c8

$ dotnet StringHasher.dll sha384 mypassword
95b2d3b2ad7c2759bf3daa53424e2a472bc932798dae30b982621833a449492883b7ae9d31d30d32372f98abdbb256ae

$ dotnet StringHasher.dll sha512 mypassword
a336f671080fbf4f2a230f313560ddf0d0c12dfcf1741e49e8722a234673037dc493caa8d291d8025f71089d63cea809cc8ae53e5b17054806837dbe4099c4ca

Try compare the result with some kind of online hash generator and see if it produces the same result.

Summary

That’s it, now you know how to generate SHA* and MD5 string easily in C#. Please remember that it’s not recommended to use it for password store. You can use it for content integrity or something else. I’ll write about best practice password storing in C#.

Thanks for reading and Happy Coding!

References

Downloads

Fork or Download completed project on GitHub.


Cover Photo by Markus Spiske on Unsplash.