Saturday, June 23, 2012

.Net Agile Cryptographic Function

A lot has recently been written about the recent LinkedIn hack. I am not going to delve into the details of the hack but I do want to show how you can have an agile cryptographic function for creating hash values. By agile I mean that the code we can use to create our hash value is not hard coded within the program. 
 MD5 is the weakest hashing algorithm we have in the .Net framework. MD5 might be strong enough when our first application was written but now we want to implement a stronger hashing function. We can re-code our application to use windows default hashing function. SHA-1 is the current default hashing algorithm but SHA-1 still doesn’t provide us with the strongest algorithm we can use. So now our boss has told us about the Linkedin hack and has asked us to use SHA512. Using an agile approach we can say “Ok, I can have that done in about 1 hour” without deploying any code. All we have to do is change our app or machine confg file. 

 We can accomplish this by using the following lines of code.


App Code File:



   <add key="HashMethod" value="SHA512"/>

C# Code:

1: preferredHash = HashAlgorithm.Create((string)ConfigurationManager.AppSettings["HashMethod"]);

   2:   
   3:  hash = computeHash(preferredHash, testString);
   4:   
   5:  private string computeHash(HashAlgorithm myHash, string input) {
   6:       byte[] data;
   7:       data = myHash.ComputeHash(Encoding.UTF8.GetBytes(input));
   8:       sb = new StringBuilder();
   9:       for (int i = 0; i < data.Length; i++) {
  10:           sb.Append(data[i].ToString("x2"));
  11:       }
  12:      return sb.ToString();
  13:  }



Line 1 let's us get our hashing algorithm we are going to use from the config file. If we use the machine config file our implementation would be server wide instead of application specific. 

The drawback to this method is key size. I would suggest of giving yourself twice the size of the largest key of hashing algorithm  you could possible use to store hash values. This means we need a varchar of 1024 if we are going to store our hash value in the database.

I will leave it up to the reader to take the hash values through Rainbow tables to see how quickly Raindow tables can return the value of the hash. 

Demo Code:

using System;
using System.Text;
using System.Security.Cryptography;
using System.Configuration;
using System.Collections;

namespace HashDemo {
    class HashTest {

        private MD5 md5Hash;
        private HashAlgorithm cryptoDefaultHash;
        private HashAlgorithm preferredHash;
        private Random random;
        private string testString = string.Empty;
        private string hash;
        private StringBuilder sb;
        private int minSaltSize = 8;
        private int maxSaltSize = 24;
        private int saltSize;

        public HashTest() {
            testString = "MySup3r#Secur3&Pa()sw0rd*";  // My Super Secure Password

            md5Hash = MD5.Create();  // Application hard codes Hash Algorithm. Not good must change code to use a newer Crypto Algorithm.

            cryptoDefaultHash = HashAlgorithm.Create(); // A little better, but uses windows default SHA-1 Crypto Algorithm, but SHA-1 is weak
            ///
            preferredHash = HashAlgorithm.Create((string)ConfigurationManager.AppSettings["HashMethod"]); // Best way is to use app or machine config file to set preferred Crypto Algorithm.
            ///
            Console.WriteLine("The crypto function is: " + md5Hash.GetType() + ".");
            Console.WriteLine("The crypto function is: " + cryptoDefaultHash.GetType() + ".");
            Console.WriteLine("The crypto function is: " + preferredHash.GetType() + ".");
            random = new Random();
            saltSize = random.Next(minSaltSize, maxSaltSize);
        }

        public void test() {
            hash = computeMd5Hash(testString);
            Console.WriteLine("MD5 - The hash is: " + hash + ".");
            hash = computeMd5HashWithSalt(testString);
            Console.WriteLine("MD5 with Salt - The hash is: " + hash + ".");

            hash = computeDefaultHash(cryptoDefaultHash, testString);
            Console.WriteLine("Default hash is: " + hash + ".");
            hash = computeDefaultHashWithSalt(cryptoDefaultHash, testString);
            Console.WriteLine("Default hash with Salt is: " + hash + ".");

            hash = computeHash(preferredHash, testString);
            Console.WriteLine("Preferred hash with Salt is: " + hash + ".");
            hash = computeHashWithSalt(preferredHash, testString);
            Console.WriteLine("Preferred hash with Salt is: " + hash + ".");
        }

        private string computeMd5Hash(string input) {
            byte[] data;
            data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
            sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++) {
                sb.Append(data[i].ToString("x2"));
            }
            return sb.ToString();
        }
        private string computeMd5HashWithSalt(string input) {
            byte[] data;
            data = md5Hash.ComputeHash(GetSalt(input));
            sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++) {
                sb.Append(data[i].ToString("x2"));
            }
            return sb.ToString();
        }
        private string computeDefaultHash(HashAlgorithm myHash, string input) {
            byte[] data;
            data = myHash.ComputeHash(Encoding.UTF8.GetBytes(input));
            sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++) {
                sb.Append(data[i].ToString("x2"));
            }
            return sb.ToString();
        }
        private string computeDefaultHashWithSalt(HashAlgorithm myHash, string input) {
            byte[] data;
            data = myHash.ComputeHash(GetSalt(input));
            sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++) {
                sb.Append(data[i].ToString("x2"));
            }
            return sb.ToString();
        }
        private string computeHash(HashAlgorithm myHash, string input) {
            byte[] data;
            data = myHash.ComputeHash(Encoding.UTF8.GetBytes(input));
            sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++) {
                sb.Append(data[i].ToString("x2"));
            }
            return sb.ToString();
        }
        private string computeHashWithSalt(HashAlgorithm myHash, string input) {
            byte[] data;
            data = myHash.ComputeHash(GetSalt(input));
            sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++) {
                sb.Append(data[i].ToString("x2"));
            }
            return sb.ToString();
        }

        private byte[] GetSalt(string input) {
            byte[] data;
            byte[] saltBytes;
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            saltBytes = new byte[saltSize];
            rng.GetNonZeroBytes(saltBytes);
            data = Encoding.UTF8.GetBytes(input);
            byte[] dataWithSaltBytes =
                new byte[data.Length + saltBytes.Length];
            for (int i = 0; i < data.Length; i++)
                dataWithSaltBytes[i] = data[i];
            for (int i = 0; i < saltBytes.Length; i++)
                dataWithSaltBytes[data.Length + i] = saltBytes[i];
            return dataWithSaltBytes;
        }

        ~HashTest() { // dtor
            preferredHash.Clear();
            cryptoDefaultHash.Clear();
            md5Hash.Clear();
        }
    } // end of class
} // end of namespace

Program Output:

The crypto function is: System.Security.Cryptography.MD5CryptoServiceProvider.
The crypto function is: System.Security.Cryptography.SHA1CryptoServiceProvider.
The crypto function is: System.Security.Cryptography.SHA512Managed.

MD5 - The hash is: 6c27f9f54f905b815e01298ccef39420.
MD5 with Salt - The hash is: 60148ef74c3dbce6ab11c5897f110167.

Default hash is: 636f54fe86c4518308f2599e5f5d8adc72cdc79c.
Default hash with Salt is: 2b223ced9cb488dfe842f3acc32c192c14f0606b.

Preferred hash with Salt is: 48ccbc77e2f280e277ebd871af9871b624c915559f677ba5c16
2d9156e70c46aa5fcc539dec4cc51155101c4e638d226746cc4a7b821bbc5501e104fbeee5eb3.
Preferred hash with Salt is: 6cd578abf2d9b65d621d42fb23e57aede34b401507a4d57d3a7
00d2dbd4f2376b4632a3add3e44c71249e1ed5a4ee293ddfe45efd9e2d0102909d10b74b64483.

No comments:

Post a Comment