How To: Hash Data with Salt (C#/VB.NET)

///
// SAMPLE: Hashing data with salt using MD5 and several SHA algorithms.
//
// To run this sample, create a new Visual C# project using the Console
// Application template and replace the contents of the Class1.cs file with
// the code below.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
// Copyright (C) 2002 Obviex(TM) . All rights reserved.
//

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

/// <summary>
/// This class generates and compares hashes using MD5, SHA1, SHA256, SHA384,
/// and SHA512 hashing algorithms. Before computing a hash, it appends a
/// randomly generated salt to the plain text, and stores this salt appended
/// to the result. To verify another plain text value against the given hash,
/// this class will retrieve the salt value from the hash string and use it
/// when computing a new hash of the plain text. Appending a salt value to
/// the hash may not be the most efficient approach, so when using hashes in
/// a real-life application, you may choose to store them separately. You may
/// also opt to keep results as byte arrays instead of converting them into
/// base64-encoded strings.
/// </summary>

public class SimpleHash
{
/// <summary>
/// Generates a hash for the given plain text value and returns a
/// base64-encoded result. Before the hash is computed, a random salt
/// is generated and appended to the plain text. This salt is stored at
/// the end of the hash value, so it can be used later for hash
/// verification.
/// </summary>
/// <param name="plainText">
/// Plaintext value to be hashed. The function does not check whether
/// this parameter is null.
/// </param>
/// <param name="hashAlgorithm">
/// Name of the hash algorithm. Allowed values are: "MD5", "SHA1",
/// "SHA256", "SHA384", and "SHA512" (if any other value is specified
/// MD5 hashing algorithm will be used). This value is case-insensitive.
/// </param>
/// <param name="saltBytes">
/// Salt bytes. This parameter can be null, in which case a random salt
/// value will be generated.
/// </param>
/// <returns>
/// Hash value formatted as a base64-encoded string.
/// </returns>

public static string ComputeHash(string plainText,
string hashAlgorithm,
byte [] saltBytes)
{
// If salt is not specified, generate it on the fly.
if (saltBytes == null )
{
// Define min and max salt sizes.
int minSaltSize = 4 ;
int maxSaltSize = 8 ;

// Generate a random number for the size of the salt.
Random random = new Random();
int saltSize = random.Next(minSaltSize, maxSaltSize);

// Allocate a byte array, which will hold the salt.
saltBytes = new byte [saltSize];

// Initialize a random number generator.
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

// Fill the salt with cryptographically strong byte values.
rng.GetNonZeroBytes(saltBytes);
}

// Convert plain text into a byte array.
byte [] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

// Allocate array, which will hold plain text and salt.
byte [] plainTextWithSaltBytes =
new byte [plainTextBytes.Length + saltBytes.Length];

// Copy plain text bytes into resulting array.
for (int i=0 ; i < plainTextBytes.Length; i++)
plainTextWithSaltBytes[i] = plainTextBytes[i];

// Append salt bytes to the resulting array.
for (int i=0 ; i < saltBytes.Length; i++)
plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];

// Because we support multiple hashing algorithms, we must define
// hash object as a common (abstract) base class. We will specify the
// actual hashing algorithm class later during object creation.

HashAlgorithm hash;

// Make sure hashing algorithm name is specified.
if (hashAlgorithm == null )
hashAlgorithm = "" ;

// Initialize appropriate hashing algorithm class.
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 ;
}

// Compute hash value of our plain text with appended salt.
byte [] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);

// Create array which will hold hash and original salt bytes.
byte [] hashWithSaltBytes = new byte [hashBytes.Length +
saltBytes.Length];

// Copy hash bytes into resulting array.
for (int i=0 ; i < hashBytes.Length; i++)
hashWithSaltBytes[i] = hashBytes[i];

// Append salt bytes to the result.
for (int i=0 ; i < saltBytes.Length; i++)
hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];

// Convert result into a base64-encoded string.
string hashValue = Convert.ToBase64String(hashWithSaltBytes);

// Return the result.
return hashValue;
}

/// <summary>
/// Compares a hash of the specified plain text value to a given hash
/// value. Plain text is hashed with the same salt value as the original
/// hash.
/// </summary>
/// <param name="plainText">
/// Plain text to be verified against the specified hash. The function
/// does not check whether this parameter is null.
/// </param>
/// <param name="hashAlgorithm">
/// Name of the hash algorithm. Allowed values are: "MD5", "SHA1",
/// "SHA256", "SHA384", and "SHA512" (if any other value is specified,
/// MD5 hashing algorithm will be used). This value is case-insensitive.
/// </param>
/// <param name="hashValue">
/// Base64-encoded hash value produced by ComputeHash function. This value
/// includes the original salt appended to it.
/// </param>
/// <returns>
/// If computed hash mathes the specified hash the function the return
/// value is true; otherwise, the function returns false.
/// </returns>

public static bool VerifyHash(string plainText,
string hashAlgorithm,
string hashValue)
{
// Convert base64-encoded hash value into a byte array.
byte [] hashWithSaltBytes = Convert.FromBase64String(hashValue);

// We must know size of hash (without salt).
int hashSizeInBits, hashSizeInBytes;

// Make sure that hashing algorithm name is specified.
if (hashAlgorithm == null )
hashAlgorithm = "" ;

// Size of hash is based on the specified algorithm.
switch (hashAlgorithm.ToUpper())
{
case "SHA1" :
hashSizeInBits = 160 ;
break ;

case "SHA256" :
hashSizeInBits = 256 ;
break ;

case "SHA384" :
hashSizeInBits = 384 ;
break ;

case "SHA512" :
hashSizeInBits = 512 ;
break ;

default : // Must be MD5
hashSizeInBits = 128 ;
break ;
}

// Convert size of hash from bits to bytes.
hashSizeInBytes = hashSizeInBits / 8 ;

// Make sure that the specified hash value is long enough.
if (hashWithSaltBytes.Length < hashSizeInBytes)
return false ;

// Allocate array to hold original salt bytes retrieved from hash.
byte [] saltBytes = new byte [hashWithSaltBytes.Length -
hashSizeInBytes];

// Copy salt from the end of the hash to the new array.
for (int i=0 ; i < saltBytes.Length; i++)
saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];

// Compute a new hash string.
string expectedHashString =
ComputeHash(plainText, hashAlgorithm, saltBytes);

// If the computed hash matches the specified hash,
// the plain text value must be correct.

return (hashValue == expectedHashString);
}
}

/// <summary>
/// Illustrates the use of the SimpleHash class.
/// </summary>

public class SimpleHashTest
{
/// <summary>
/// The main entry point for the application.
/// </summary>

[STAThread]
static void Main(string [] args)
{
string password = "myP@5sw0rd" ; // original password
string wrongPassword = "password" ; // wrong 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());
}
}
//
// END OF FILE
///

^ Back to top


VB.NET code

[printer-friendly version ] [code output ]

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' SAMPLE: Hashing data with salt using MD5 and several SHA algorithms.
'
' To run this sample, create a new Visual Basic.NET project using the Console
' Application template and replace the contents of the Module1.vb file with
' the code below.
'
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
' EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
' WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
'
' Copyright (C) 2002 Obviex(TM) . All rights reserved.
'

Imports System
Imports System.Text
Imports System.Security.Cryptography

Module Module1

' <summary>
' This class generates and compares hashes using MD5, SHA1, SHA256, SHA384,
' and SHA512 hashing algorithms. Before computing a hash, it appends a
' randomly generated salt to the plain text, and stores this salt appended
' to the result. To verify another plain text value against the given hash,
' this class will retrieve the salt value from the hash string and use it
' when computing a new hash of the plain text. Appending a salt value to
' the hash may not be the most efficient approach, so when using hashes in
' a real-life application, you may choose to store them separately. You may
' also opt to keep results as byte arrays instead of converting them into
' base64-encoded strings.
' </summary>

Public Class SimpleHash

' <summary>
' Generates a hash for the given plain text value and returns a
' base64-encoded result. Before the hash is computed, a random salt
' is generated and appended to the plain text. This salt is stored at
' the end of the hash value, so it can be used later for hash
' verification.
' </summary>
' <param name="plainText">
' Plaintext value to be hashed. The function does not check whether
' this parameter is null.
' </param>
' < name="hashAlgorithm">
' Name of the hash algorithm. Allowed values are: "MD5", "SHA1",
' "SHA256", "SHA384", and "SHA512" (if any other value is specified
' MD5 hashing algorithm will be used). This value is case-insensitive.
' </param>
' < name="saltBytes">
' Salt bytes. This parameter can be null, in which case a random salt
' value will be generated.
' </param>
' <returns>
' Hash value formatted as a base64-encoded string.
' </returns>

Public Shared Function ComputeHash(ByVal plainText As String , _
ByVal hashAlgorithm As String , _
ByVal saltBytes() As Byte ) _
As String

' If salt is not specified, generate it on the fly.
If (saltBytes Is Nothing ) Then

' Define min and max salt sizes.
Dim minSaltSize As Integer
Dim maxSaltSize As Integer

minSaltSize = 4
maxSaltSize = 8

' Generate a random number for the size of the salt.
Dim random As Random
random = New Random()

Dim saltSize As Integer
saltSize = random.Next(minSaltSize, maxSaltSize)

' Allocate a byte array, which will hold the salt.
saltBytes = New Byte (saltSize - 1 ){}

' Initialize a random number generator.
Dim rng As RNGCryptoServiceProvider
rng = New RNGCryptoServiceProvider()

' Fill the salt with cryptographically strong byte values.
rng.GetNonZeroBytes(saltBytes)
End If

' Convert plain text into a byte array.
Dim plainTextBytes As Byte ()
plainTextBytes = Encoding.UTF8.GetBytes(plainText)

' Allocate array, which will hold plain text and salt.
Dim plainTextWithSaltBytes() As Byte = _
New Byte (plainTextBytes.Length + saltBytes.Length - 1 ){}

' Copy plain text bytes into resulting array.
Dim I As Integer
For I = 0 To plainTextBytes.Length - 1
plainTextWithSaltBytes(I) = plainTextBytes(I)
Next I

' Append salt bytes to the resulting array.
For I = 0 To saltBytes.Length - 1
plainTextWithSaltBytes(plainTextBytes.Length + I) = saltBytes(I)
Next I

' Because we support multiple hashing algorithms, we must define
' hash object as a common (abstract) base class. We will specify the
' actual hashing algorithm class later during object creation.

Dim hash As HashAlgorithm

' Make sure hashing algorithm name is specified.
If (hashAlgorithm Is Nothing ) Then
hashAlgorithm = ""
End If

' Initialize appropriate hashing algorithm class.
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

' Compute hash value of our plain text with appended salt.
Dim hashBytes As Byte ()
hashBytes = hash.ComputeHash(plainTextWithSaltBytes)

' Create array which will hold hash and original salt bytes.
Dim hashWithSaltBytes() As Byte = _
New Byte (hashBytes.Length + _
saltBytes.Length - 1 ) {}

' Copy hash bytes into resulting array.
For I = 0 To hashBytes.Length - 1
hashWithSaltBytes(I) = hashBytes(I)
Next I

' Append salt bytes to the result.
For I = 0 To saltBytes.Length - 1
hashWithSaltBytes(hashBytes.Length + I) = saltBytes(I)
Next I

' Convert result into a base64-encoded string.
Dim hashValue As String
hashValue = Convert.ToBase64String(hashWithSaltBytes)

' Return the result.
ComputeHash = hashValue
End Function

' <summary>
' Compares a hash of the specified plain text value to a given hash
' value. Plain text is hashed with the same salt value as the original
' hash.
' </summary>
' <param name="plainText">
' Plain text to be verified against the specified hash. The function
' does not check whether this parameter is null.
' </param>
' < name="hashAlgorithm">
' Name of the hash algorithm. Allowed values are: "MD5", "SHA1",
' "SHA256", "SHA384", and "SHA512" (if any other value is specified
' MD5 hashing algorithm will be used). This value is case-insensitive.
' </param>
' < name="hashValue">
' Base64-encoded hash value produced by ComputeHash function. This value
' includes the original salt appended to it.
' </param>
' <returns>
' If computed hash mathes the specified hash the function the return
' value is true; otherwise, the function returns false.
' </returns>

Public Shared Function VerifyHash(ByVal plainText As String , _
ByVal hashAlgorithm As String , _
ByVal hashValue As String ) _
As Boolean

' Convert base64-encoded hash value into a byte array.
Dim hashWithSaltBytes As Byte ()
hashWithSaltBytes = Convert.FromBase64String(hashValue)

' We must know size of hash (without salt).
Dim hashSizeInBits As Integer
Dim hashSizeInBytes As Integer

' Make sure that hashing algorithm name is specified.
If (hashAlgorithm Is Nothing ) Then
hashAlgorithm = ""
End If

' Size of hash is based on the specified algorithm.
Select hashAlgorithm.ToUpper()

Case "SHA1"
hashSizeInBits = 160

Case "SHA256"
hashSizeInBits = 256

Case "SHA384"
hashSizeInBits = 384

Case "SHA512"
hashSizeInBits = 512

Case Else ' Must be MD5
hashSizeInBits = 128

End Select

' Convert size of hash from bits to bytes.
hashSizeInBytes = hashSizeInBits / 8

' Make sure that the specified hash value is long enough.
If (hashWithSaltBytes.Length < hashSizeInBytes) Then
VerifyHash = False
End If

' Allocate array to hold original salt bytes retrieved from hash.
Dim saltBytes() As Byte = New Byte (hashWithSaltBytes.Length - _
hashSizeInBytes - 1 ) {}

' Copy salt from the end of the hash to the new array.
Dim I As Integer
For I = 0 To saltBytes.Length - 1
saltBytes(I) = hashWithSaltBytes(hashSizeInBytes + I)
Next I

' Compute a new hash string.
Dim expectedHashString As String
expectedHashString = ComputeHash(plainText, hashAlgorithm, saltBytes)

' If the computed hash matches the specified hash,
' the plain text value must be correct.

VerifyHash = (hashValue = expectedHashString)
End Function
End Class

' <summary>
' The main entry point for the application.
' </summary>

Sub Main()

Dim password As String ' original password
Dim wrongPassword As String ' wrong password

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
'
' END OF FILE
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

^ 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值