Salt + Hash 密码加密,兼容 windows xp
为什么要用哈希函数来加密密码?
如果你需要保存密码(比如网站用户的密码),你要考虑如何保护这些密码数据,直接将密码写入数据库中是极不安全的,因为任何可以打开数据库的人,都将可以直接看到这些密码。解决的办法是将密码加密后再存储进数据库,比较常用的加密方法是使用哈希函数。
哈希函数的具体定义,大家可以在网上或者相关书籍中查阅到,简单地说,它的特性如下:
(1)原始密码经哈希函数计算后得到一个哈希值
(2)改变原始密码,哈希函数计算出的哈希值也会相应改变
(3)同样的密码,哈希值也是相同的
(4)哈希函数是单向、不可逆的。也就是说从哈希值,你无法推算出原始的密码是多少
那么存储经过哈希函数加密后的密码是否就是安全的呢?我们先来看一下几种常见的破解密码的方法。
几种常见的破解密码的方法
最简单、常见的破解方式当属字典破解(Dictionary Attack)和暴力破解(Brute Force Attack)方式。这两种方法说白了就是猜密码。
字典破解和暴力破解都是效率比较低的破解方式。如果你知道了数据库中密码的哈希值,你就可以采用一种更高效的破解方式,查表法(Lookup Tables)。还有一些方法,比如逆向查表法(Reverse Lookup Tables)、彩虹表(Rainbow Tables)等,都和查表法大同小异。
现在我们来看一下查表法的原理:
查表法不像字典破解和暴力破解那样猜密码,它首先将一些比较常用的密码的哈希值算好,然后建立一张表,当然密码越多,这张表就越大。当你知道某个密码的哈希值时,你只需要在你建立好的表中查找该哈希值,如果找到了,你就知道对应的密码了。
为密码加盐(Salt)
从上面的查表法可以看出,即便是将原始密码加密后的哈希值存储在数据库中依然是不够安全的。那么有什么好的办法来解决这个问题呢?答案是加盐。盐(Salt)是什么?就是一个随机生成的字符串。我们将盐与原始密码连接(concat)在一起(放在前面或后面都可以),然后将concat后的字符串加密。采用这种方式加密密码,查表法就不灵了(因为盐是随机生成的)。
总结:单单使用哈希函数来为密码加密是不够的,需要为密码加盐来提高安全性,盐的长度不能过短,并且盐的产生应该是随机的。
IHashPassword.cs代码:
// <summary>
// HashPassword接口
// </summary>
namespace PasswordHashEncrypt
{
public interface IHashPassword
{
#region Public Properties
/// <summary>
/// 获取哈希字节长度
/// </summary>
int HashByteLength { get; }
#endregion
#region Public Methods and Operators
/// <summary>
/// 生成
/// </summary>
/// <param name="passwordString">
/// password字符串.
/// </param>
string Generate(string passwordString);
/// <summary>
/// 验证.
/// </summary>
/// <param name="passwordString">
/// password字符串.
/// </param>
/// <param name="hashValue">
/// hash值.
/// </param>
bool Validate(string passwordString, string hashValue);
#endregion
}
}
HashPasswordBase.cs
using System;
using System.Linq;
using System.Security.Cryptography;
namespace PasswordHashEncrypt
{
/// <summary>
/// 哈希密码库
/// </summary>
public abstract class HashPasswordBase : IHashPassword
{
#region 公共属性
/// <summary>
/// 获取哈希字节长度
/// </summary>
public int HashByteLength
{
get { return GetHashByteLength(); }
}
#endregion
#region 公共方法和操作
/// <summary>
/// 生成
/// </summary>
/// <param name="password">
/// 密码
/// </param>
/// <returns>
/// </returns>
public string Generate(string password)
{
byte[] salt = GenerateSalt();
byte[] hash = Generate(password, salt);
return string.Format("{0}:{1}", Convert.ToBase64String(salt), Convert.ToBase64String(hash));
}
/// <summary>
/// 验证.
/// </summary>
/// <param name="password">
/// 密码.
/// </param>
/// <param name="hashValue">
/// 散列值.
/// </param>
/// <returns>
/// </returns>
public bool Validate(string password, string hashValue)
{
string[] splits = hashValue.Split(':');
if (splits.Length == 2)
{
byte[] salt = Convert.FromBase64String(splits[0]);
byte[] hash = Convert.FromBase64String(splits[1]);
return Validate(password, salt, hash);
}
return false;
}
#endregion
#region Methods
/// <summary>
/// 计算哈希
/// </summary>
/// <param name="buffer">
/// 缓冲区
/// </param>
/// <returns>
/// </returns>
protected abstract byte[] ComputeHash(byte[] buffer);
/// <summary>
/// 生成
/// </summary>
/// <param name="password">
/// 密码
/// </param>
/// <param name="salt">
/// 盐值
/// </param>
/// <returns>
/// </returns>
protected byte[] Generate(string password, byte[] salt)
{
byte[] bytes = BlockCopy(password);
byte[] combined = Combine(salt, bytes);
return ComputeHash(combined);
}
/// <summary>
/// 生成盐值
/// </summary>
/// <returns>
/// </returns>
protected byte[] GenerateSalt()
{
return RandomSalt(HashByteLength);
}
/// <summary>
/// 获取哈希字节长度
/// </summary>
/// <returns>
/// </returns>
protected abstract int GetHashByteLength();
/// <summary>
/// 验证
/// </summary>
/// <param name="password">
/// 密码
/// </param>
/// <param name="salt">
/// 盐值
/// </param>
/// <param name="goodHash">
/// 好hash
/// </param>
/// <returns>
/// </returns>
protected bool Validate(string password, byte[] salt, byte[] goodHash)
{
byte[] hash = Generate(password, salt);
return SlowEquals(hash, goodHash);
}
/// <summary>
/// 块复制
/// </summary>
/// <param name="input">
/// 输入值
/// </param>
/// <returns>
/// </returns>
private static byte[] BlockCopy(string input)
{
var bytes = new byte[input.Length*sizeof (char)];
Buffer.BlockCopy(input.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
/// <summary>
/// 组合
/// </summary>
private static byte[] Combine(byte[] a, byte[] b)
{
return a.Concat(b).ToArray();
}
/// <summary>
/// 随机盐值
/// </summary>
/// <param name="length">
/// 长度
/// </param>
private static byte[] RandomSalt(int length)
{
var rng = new RNGCryptoServiceProvider();
var salt = new byte[length];
rng.GetBytes(salt);
return salt;
}
private static bool SlowEquals(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
}
}
SHA256HashPassword.cs
using System;
using System.Security.Cryptography;
namespace PasswordHashEncrypt
{
/// <summary>
/// 一个256位哈希密码
/// </summary>
public class SHA256HashPassword : HashPasswordBase
{
#region Constants
/// <summary>
/// 哈希字节长度常量
/// </summary>
public const int HashByteLengthConst = 32;
#endregion
#region Methods
/// <summary>
/// 计算hash
/// </summary>
/// <param name="buffer">
/// 缓冲区
/// </param>
protected override byte[] ComputeHash(byte[] buffer)
{
SHA256 service;
try
{
service = new SHA256CryptoServiceProvider();
}
catch (PlatformNotSupportedException)
{
service = new SHA256Managed();
}
return service.ComputeHash(buffer);
}
/// <summary>
/// 获取哈希字节长度
/// </summary>
protected override int GetHashByteLength()
{
return HashByteLengthConst;
}
#endregion
}
}
SHA512HashPassword.cs
using System;
using System.Security.Cryptography;
namespace PasswordHashEncrypt
{
/// <summary>
/// 一个512位哈希密码
/// </summary>
public class SHA512HashPassword : HashPasswordBase
{
#region 常量
/// <summary>
/// 哈希字节长度常量
/// </summary>
public const int HashByteLengthConst = 64;
#endregion
#region 方法
/// <summary>
/// 计算hash
/// </summary>
/// <param name="buffer">
/// 缓冲区
/// </param>
protected override byte[] ComputeHash(byte[] buffer)
{
SHA512 service;
try
{
service = new SHA512CryptoServiceProvider();
}
catch (PlatformNotSupportedException)
{
service = new SHA512Managed();
}
return service.ComputeHash(buffer);
}
/// <summary>
/// 获取哈希字节长度
/// </summary>
protected override int GetHashByteLength()
{
return HashByteLengthConst;
}
#endregion
}
}
Program.cs
class Program
{
/// <summary>
/// 哈希密码
/// </summary>
private static IHashPassword hashPassword;
private static void Main(string[] args)
{
//hashPassword = new SHA256HashPassword();
hashPassword = new SHA512HashPassword();
const string TestPassword = "abc123";
string hashValue = hashPassword.Generate(TestPassword);
Console.WriteLine(hashValue);
bool result = hashPassword.Validate(TestPassword, hashValue);
Console.WriteLine(result);
Console.ReadKey();
}
}