Hasher.cs 3.28 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
using System;
using System.Text;
using System.Security.Cryptography;

public interface IHasher
{
	#region Methods

	string Create (string text);

	bool Validate (string text, string security);

	#endregion
}

public class Hasher : IHasher
{
	#region Constructors

	public Hasher ()
	{
	}

	public Hasher (string algorithm, int iterations, int keySize, int saltSize)
	{
		Algorithm = algorithm;
		Iterations = iterations;
		KeySize = keySize;
		SaltSize = saltSize;
	}

	#endregion

	#region Implementation of IHasher

	public string Create (string text)
	{
		byte[] salt = GetSalt ();

		byte[] hash = GetHash (Algorithm, text, salt, Iterations, KeySize);

		return Algorithm + ":" + Iterations + ":" + KeySize + ":" + Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash);
	}

	public bool Validate (string text, string security)
	{
		char[] delimiter = { ':' };

		string[] segments = security.Split(delimiter);

		string algorithm = segments[SecuritySegment.Algorithm];
			
		int keySize = int.Parse(segments[SecuritySegment.KeySize]);
			
		int iterations = int.Parse(segments[SecuritySegment.Iterations]);
			
		byte[] salt = Convert.FromBase64String(segments[SecuritySegment.Salt]);
			
		byte[] hashStored = Convert.FromBase64String(segments[SecuritySegment.Hash]);

		byte[] hashComputed = GetHash(algorithm, text, salt, iterations, keySize);
			
		return ConstantTimeEquals (hashStored, hashComputed);
	}

	#endregion

	#region Private Methods

	private byte[] GetSalt ()
	{
		using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
		{
			byte[] salt = new byte[SaltSize];

			rng.GetBytes (salt);

			return salt;
		}
	}

	private byte[] GetHash (string algorithm, string text, byte[] salt, int iterations, int keySize)
	{
		byte[] textAsBytes = Encoding.UTF8.GetBytes(text);

		byte[] key = GetKey (textAsBytes, salt, iterations, keySize);

		KeyedHashAlgorithm hashAlgorithm = KeyedHashAlgorithm.Create(algorithm);

		hashAlgorithm.Key = key;

		byte[] saltedText = GetSaltedText (textAsBytes, salt);

		return hashAlgorithm.ComputeHash (saltedText);
	}

	private byte[] GetKey (byte[] text, byte[] salt, int iterations, int keySize)
	{
		using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(text, salt, iterations))
		{
			return pbkdf2.GetBytes(keySize);
		}
	}

	private byte[] GetSaltedText (byte[] text, byte[] salt)
	{
		byte[] saltedText = new byte[text.Length + salt.Length];

		for (int i = 0; i < text.Length; i++)
		{
			saltedText[i] = text[i];
		}

		for (int i = 0; i < salt.Length; i++)
		{
			saltedText[text.Length + i] = salt[i];
		}

		return saltedText;
	}

	private bool ConstantTimeEquals (byte[] a, byte[] b)
	{
		uint diff = (uint) a.Length ^ (uint) b.Length;

		for (int i = 0; (i < a.Length) && (i < b.Length); i++)
		{
			diff |= (uint) (a[i] ^ b[i]);
		}

		return diff == 0;
	}

	#endregion

	#region Public Properties

	public string Algorithm { get; set; }

	public int Iterations { get; set; }

	public int SaltSize { get; set; }

	public int KeySize { get; set; }

	#endregion

	#region Private Static Properties

	private static class SecuritySegment
	{
		public static readonly int Algorithm = 0;

		public static readonly int Iterations = 1;

		public static readonly int KeySize = 2;

		public static readonly int Salt = 3;

		public static readonly int Hash = 4;
	}

	#endregion
}