|
How To: Hash Data with Salt (C#/VB.NET)
The code below demonstrates how to hash data and verify hashes.
It supports MD5, SHA-1, SHA-256, SHA-384, and SHA-512 hashing algorithms.
To help reduce the risk of dictionary attacks, the code appends random bytes
(so-called "salt") to the original plain text before generating hashes.
Please keep in mind that salt can only help against prebuilt dictionaries.
If an intruder gets access to your system and uses a brute force attack,
salt will not provide much value.
Code samples are provided in
C#
and
Visual Basic.NET.
IMPORTANT: DATA HASHES CANNOT BE "DECRYPTED" BACK TO PLAIN TEXT.
Notes
These examples are offered for demonstration purpose only.
In a real application you may need to modify the code to make it more efficient.
For example, instead of appending salt values to generated hashes,
you may want to store them separately.
Another performance improvement can be achieved by not converting results into
base64-encoded strings, but manipulating them in a byte array format.
For additional information about hashing, check
MSDN sample
describing how to use password hashes stored in a database for user authentication.
See also the How To Encrypt Data With Salt sample,
which explains how to use salt with symmetric-key encryption.
Resources
Our FREE
CipherLite.NET™
tool provides a GUI and library, which you can use to hash strings and verify data hashes
(without salt).
C# code
[printer-friendly version]
[code output]
using System;
using System.Text;
using System.Security.Cryptography;
public class SimpleHash
{
public static string ComputeHash(string plainText,
string hashAlgorithm,
byte[] saltBytes)
{
if (saltBytes == null)
{
int minSaltSize = 4;
int maxSaltSize = 8;
Random random = new Random();
int saltSize = random.Next(minSaltSize, maxSaltSize);
saltBytes = new byte[saltSize];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(saltBytes);
}
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] plainTextWithSaltBytes =
new byte[plainTextBytes.Length + saltBytes.Length];
for (int i=0; i < plainTextBytes.Length; i++)
plainTextWithSaltBytes[i] = plainTextBytes[i];
for (int i=0; i < saltBytes.Length; i++)
plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
HashAlgorithm hash;
if (hashAlgorithm == null)
hashAlgorithm = "";
switch (hashAlgorithm.ToUpper())
{
case "SHA1":
hash = new SHA1Managed();
break;
case "SHA256":
hash = new SHA256Managed();
break;
case "SHA384":
hash = new SHA384Managed();
break;
case "SHA512":
hash = new SHA512Managed();
break;
default:
hash = new MD5CryptoServiceProvider();
break;
}
byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
byte[] hashWithSaltBytes = new byte[hashBytes.Length +
saltBytes.Length];
for (int i=0; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];
for (int i=0; i < saltBytes.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];
string hashValue = Convert.ToBase64String(hashWithSaltBytes);
return hashValue;
}
public static bool VerifyHash(string plainText,
string hashAlgorithm,
string hashValue)
{
byte[] hashWithSaltBytes = Convert.FromBase64String(hashValue);
int hashSizeInBits, hashSizeInBytes;
if (hashAlgorithm == null)
hashAlgorithm = "";
switch (hashAlgorithm.ToUpper())
{
case "SHA1":
hashSizeInBits = 160;
break;
case "SHA256":
hashSizeInBits = 256;
break;
case "SHA384":
hashSizeInBits = 384;
break;
case "SHA512":
hashSizeInBits = 512;
break;
default:
hashSizeInBits = 128;
break;
}
hashSizeInBytes = hashSizeInBits / 8;
if (hashWithSaltBytes.Length < hashSizeInBytes)
return false;
byte[] saltBytes = new byte[hashWithSaltBytes.Length -
hashSizeInBytes];
for (int i=0; i < saltBytes.Length; i++)
saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];
string expectedHashString =
ComputeHash(plainText, hashAlgorithm, saltBytes);
return (hashValue == expectedHashString);
}
}
public class SimpleHashTest
{
[STAThread]
static void Main(string[] args)
{
string password = "myP@5sw0rd";
string wrongPassword = "password";
string passwordHashMD5 =
SimpleHash.ComputeHash(password, "MD5", null);
string passwordHashSha1 =
SimpleHash.ComputeHash(password, "SHA1", null);
string passwordHashSha256 =
SimpleHash.ComputeHash(password, "SHA256", null);
string passwordHashSha384 =
SimpleHash.ComputeHash(password, "SHA384", null);
string passwordHashSha512 =
SimpleHash.ComputeHash(password, "SHA512", null);
Console.WriteLine("COMPUTING HASH VALUES\r\n");
Console.WriteLine("MD5 : {0}", passwordHashMD5);
Console.WriteLine("SHA1 : {0}", passwordHashSha1);
Console.WriteLine("SHA256: {0}", passwordHashSha256);
Console.WriteLine("SHA384: {0}", passwordHashSha384);
Console.WriteLine("SHA512: {0}", passwordHashSha512);
Console.WriteLine("");
Console.WriteLine("COMPARING PASSWORD HASHES\r\n");
Console.WriteLine("MD5 (good): {0}",
SimpleHash.VerifyHash(
password, "MD5",
passwordHashMD5).ToString());
Console.WriteLine("MD5 (bad) : {0}",
SimpleHash.VerifyHash(
wrongPassword, "MD5",
passwordHashMD5).ToString());
Console.WriteLine("SHA1 (good): {0}",
SimpleHash.VerifyHash(
password, "SHA1",
passwordHashSha1).ToString());
Console.WriteLine("SHA1 (bad) : {0}",
SimpleHash.VerifyHash(
wrongPassword, "SHA1",
passwordHashSha1).ToString());
Console.WriteLine("SHA256 (good): {0}",
SimpleHash.VerifyHash(
password, "SHA256",
passwordHashSha256).ToString());
Console.WriteLine("SHA256 (bad) : {0}",
SimpleHash.VerifyHash(
wrongPassword, "SHA256",
passwordHashSha256).ToString());
Console.WriteLine("SHA384 (good): {0}",
SimpleHash.VerifyHash(
password, "SHA384",
passwordHashSha384).ToString());
Console.WriteLine("SHA384 (bad) : {0}",
SimpleHash.VerifyHash(
wrongPassword, "SHA384",
passwordHashSha384).ToString());
Console.WriteLine("SHA512 (good): {0}",
SimpleHash.VerifyHash(
password, "SHA512",
passwordHashSha512).ToString());
Console.WriteLine("SHA512 (bad) : {0}",
SimpleHash.VerifyHash(
wrongPassword, "SHA512",
passwordHashSha512).ToString());
}
}
^ Back to top
VB.NET code
[printer-friendly version]
[code output]
Imports System
Imports System.Text
Imports System.Security.Cryptography
Module Module1
Public Class SimpleHash
Public Shared Function ComputeHash(ByVal plainText As String, _
ByVal hashAlgorithm As String, _
ByVal saltBytes() As Byte) _
As String
If (saltBytes Is Nothing) Then
Dim minSaltSize As Integer
Dim maxSaltSize As Integer
minSaltSize = 4
maxSaltSize = 8
Dim random As Random
random = New Random()
Dim saltSize As Integer
saltSize = random.Next(minSaltSize, maxSaltSize)
saltBytes = New Byte(saltSize - 1){}
Dim rng As RNGCryptoServiceProvider
rng = New RNGCryptoServiceProvider()
rng.GetNonZeroBytes(saltBytes)
End If
Dim plainTextBytes As Byte()
plainTextBytes = Encoding.UTF8.GetBytes(plainText)
Dim plainTextWithSaltBytes() As Byte = _
New Byte(plainTextBytes.Length + saltBytes.Length - 1){}
Dim I As Integer
For I = 0 To plainTextBytes.Length - 1
plainTextWithSaltBytes(I) = plainTextBytes(I)
Next I
For I = 0 To saltBytes.Length - 1
plainTextWithSaltBytes(plainTextBytes.Length + I) = saltBytes(I)
Next I
Dim hash As HashAlgorithm
If (hashAlgorithm Is Nothing) Then
hashAlgorithm = ""
End If
Select hashAlgorithm.ToUpper()
Case "SHA1"
hash = New SHA1Managed()
Case "SHA256"
hash = New SHA256Managed()
Case "SHA384"
hash = New SHA384Managed()
Case "SHA512"
hash = New SHA512Managed()
Case Else
hash = New MD5CryptoServiceProvider()
End Select
Dim hashBytes As Byte()
hashBytes = hash.ComputeHash(plainTextWithSaltBytes)
Dim hashWithSaltBytes() As Byte = _
New Byte(hashBytes.Length + _
saltBytes.Length - 1) {}
For I = 0 To hashBytes.Length - 1
hashWithSaltBytes(I) = hashBytes(I)
Next I
For I = 0 To saltBytes.Length - 1
hashWithSaltBytes(hashBytes.Length + I) = saltBytes(I)
Next I
Dim hashValue As String
hashValue = Convert.ToBase64String(hashWithSaltBytes)
ComputeHash = hashValue
End Function
Public Shared Function VerifyHash(ByVal plainText As String, _
ByVal hashAlgorithm As String, _
ByVal hashValue As String) _
As Boolean
Dim hashWithSaltBytes As Byte()
hashWithSaltBytes = Convert.FromBase64String(hashValue)
Dim hashSizeInBits As Integer
Dim hashSizeInBytes As Integer
If (hashAlgorithm Is Nothing) Then
hashAlgorithm = ""
End If
Select hashAlgorithm.ToUpper()
Case "SHA1"
hashSizeInBits = 160
Case "SHA256"
hashSizeInBits = 256
Case "SHA384"
hashSizeInBits = 384
Case "SHA512"
hashSizeInBits = 512
Case Else
hashSizeInBits = 128
End Select
hashSizeInBytes = hashSizeInBits / 8
If (hashWithSaltBytes.Length < hashSizeInBytes) Then
VerifyHash = False
End If
Dim saltBytes() As Byte = New Byte(hashWithSaltBytes.Length - _
hashSizeInBytes - 1) {}
Dim I As Integer
For I = 0 To saltBytes.Length - 1
saltBytes(I) = hashWithSaltBytes(hashSizeInBytes + I)
Next I
Dim expectedHashString As String
expectedHashString = ComputeHash(plainText, hashAlgorithm, saltBytes)
VerifyHash = (hashValue = expectedHashString)
End Function
End Class
Sub Main()
Dim password As String
Dim wrongPassword As String
password = "myP@5sw0rd"
wrongPassword = "password"
Dim passwordHashMD5 As String
Dim passwordHashSha1 As String
Dim passwordHashSha256 As String
Dim passwordHashSha384 As String
Dim passwordHashSha512 As String
passwordHashMD5 = _
SimpleHash.ComputeHash(password, "MD5", Nothing)
passwordHashSha1 = _
SimpleHash.ComputeHash(password, "SHA1", Nothing)
passwordHashSha256 = _
SimpleHash.ComputeHash(password, "SHA256", Nothing)
passwordHashSha384 = _
SimpleHash.ComputeHash(password, "SHA384", Nothing)
passwordHashSha512 = _
SimpleHash.ComputeHash(password, "SHA512", Nothing)
Console.WriteLine("COMPUTING HASH VALUES")
Console.WriteLine("")
Console.WriteLine("MD5 : {0}", passwordHashMD5)
Console.WriteLine("SHA1 : {0}", passwordHashSha1)
Console.WriteLine("SHA256: {0}", passwordHashSha256)
Console.WriteLine("SHA384: {0}", passwordHashSha384)
Console.WriteLine("SHA512: {0}", passwordHashSha512)
Console.WriteLine("")
Console.WriteLine("COMPARING PASSWORD HASHES")
Console.WriteLine("")
Console.WriteLine("MD5 (good): {0}", _
SimpleHash.VerifyHash( _
password, "MD5", _
passwordHashMD5).ToString())
Console.WriteLine("MD5 (bad) : {0}", _
SimpleHash.VerifyHash( _
wrongPassword, "MD5", _
passwordHashMD5).ToString())
Console.WriteLine("SHA1 (good): {0}", _
SimpleHash.VerifyHash( _
password, "SHA1", _
passwordHashSha1).ToString())
Console.WriteLine("SHA1 (bad) : {0}", _
SimpleHash.VerifyHash( _
wrongPassword, "SHA1", _
passwordHashSha1).ToString())
Console.WriteLine("SHA256 (good): {0}", _
SimpleHash.VerifyHash( _
password, "SHA256", _
passwordHashSha256).ToString())
Console.WriteLine("SHA256 (bad) : {0}", _
SimpleHash.VerifyHash( _
wrongPassword, "SHA256", _
passwordHashSha256).ToString())
Console.WriteLine("SHA384 (good): {0}", _
SimpleHash.VerifyHash( _
password, "SHA384", _
passwordHashSha384).ToString())
Console.WriteLine("SHA384 (bad) : {0}", _
SimpleHash.VerifyHash( _
wrongPassword, "SHA384", _
passwordHashSha384).ToString())
Console.WriteLine("SHA512 (good): {0}", _
SimpleHash.VerifyHash( _
password, "SHA512", _
passwordHashSha512).ToString())
Console.WriteLine("SHA512 (bad) : {0}", _
SimpleHash.VerifyHash( _
wrongPassword, "SHA512", _
passwordHashSha512).ToString())
End Sub
End Module
^ Back to top
Code Output
COMPUTING HASH VALUES
MD5 : SC4LSYSAkKILp2rPW1ZVpOP1WK7g
SHA1 : E0CfoAleTy9lDL8PmqLlY76jg3k/as3G5DPe
SHA256: p4OqMcDW33DzkGR7+UskcFv75yq/Jb7K49mRwRYHLdw0+HTwq3sS
SHA384: Tq6F1p1Hhan+tGPLOS+T6ltPh7wvTtPqgvgd4BKCTPEGnXCOEQpcrm0IELEjnobkWKY9...
SHA512: UjtzgRAx4BWpMKYb1Qnrhn3Nlj84MrKNX1zJbNW33saM9IEtRmpzn4Ny6Y5oITg3TkSZ...
COMPARING PASSWORD HASHES
MD5 (good): True
MD5 (bad) : False
SHA1 (good): True
SHA1 (bad) : False
SHA256 (good): True
SHA256 (bad) : False
SHA384 (good): True
SHA384 (bad) : False
SHA512 (good): True
SHA512 (bad) : False
^ Back to top
|