内容目录
直接使用 .NET 的 CLR 库生成,不依赖第三方框架。
已将代码修正为最新实现,去掉过时接口。
基于密码的密钥导出函数2(PBKDF2)让别人更难通过穷举法猜到你的帐户密码。
pbkdf2 加密是不可逆的,因此可以用来处理密码等,只能对比,不能还原。
比如说,每个用户设置一个加密密钥,每个用户使用自己的密钥加密密码,且密码不可逆。
即使拿到一个用户的密码,破解了,每个用户的密钥都是不一样的,没法大规模爆破。
/// <summary>
/// Pbkdf2
/// </summary>
public static class Pbkdf2Helper
{
private const int SALT_SIZE = 128; // size in bytes,salt 的大小
private const int HASH_SIZE = 128; // size in bytes,生成的哈希串的大小
private const int ITERATIONS = 1000; // number of pbkdf2 iterations,表示循环次数
#region 生成 Salt
/// <summary>
/// 随机生成 Salt,相当于生成密钥
/// </summary>
/// <returns></returns>
public static byte[] CreateSalt()
{
// 生成盐
RandomNumberGenerator provider = RandomNumberGenerator.Create();
byte[] salt = new byte[SALT_SIZE];
provider.GetBytes(salt);
return salt;
}
/// <summary>
/// 随机生成 Salt,相当于生成密钥,同时返回此密钥的字符串
/// </summary>
/// <param name="text">Salt 的字符串 表示</param>
/// <returns></returns>
public static byte[] CreateSalt(out string text)
{
// 生成盐
RandomNumberGenerator provider = RandomNumberGenerator.Create();
byte[] salt = new byte[SALT_SIZE];
provider.GetBytes(salt);
text = BitConverter.ToString(salt);
return salt;
}
#endregion
#region 加密
/// <summary>
/// 将一个字符串加密
/// </summary>
/// <param name="text">待加密的字符串</param>
/// <param name="salt">盐</param>
/// <returns></returns>
public static string CreateHashString(string text, out byte[] salt)
{
salt = CreateSalt();
Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(text, salt, ITERATIONS, HashAlgorithmName.SHA1);
var bytes = pbkdf2.GetBytes(HASH_SIZE);
return BitConverter.ToString(bytes);
}
/// <summary>
/// 将一个字符串加密
/// </summary>
/// <param name="text">待加密的字符串</param>
/// <param name="salt">盐</param>
public static string CreateHashString(string text, out string salt)
{
var _salt = CreateSalt();
Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(text, _salt, ITERATIONS, HashAlgorithmName.SHA1);
var bytes = pbkdf2.GetBytes(HASH_SIZE);
salt = BitConverter.ToString(_salt);
return BitConverter.ToString(bytes);
}
/// <summary>
/// 将一个字符串加密
/// </summary>
/// <param name="text">待加密的字符串</param>
/// <param name="salt">盐</param>
/// <returns></returns>
public static string CreateHashString(string text, byte[] salt)
{
Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(text, salt, ITERATIONS, HashAlgorithmName.SHA1);
var bytes = pbkdf2.GetBytes(HASH_SIZE);
return BitConverter.ToString(bytes);
}
#endregion
#region 验证
/// <summary>
/// 检查一个字符串是否与密文相同
/// </summary>
/// <param name="password">未加密的字符串</param>
/// <param name="hashPassword">密文</param>
/// <param name="salt">盐</param>
/// <returns></returns>
public static bool HashCheck(string password, string hashPassword, string salt)
{
// 还原 salt
var chars = salt.Split('-').ToArray();
var saltBytes = chars.Select(x => (byte)int.Parse(x, System.Globalization.NumberStyles.AllowHexSpecifier)).ToArray();
return EqualHash(password, hashPassword, saltBytes);
}
/// <summary>
/// 检查一个字符串是否与密文相同
/// </summary>
/// <param name="text">未加密的字符串</param>
/// <param name="hash">密文</param>
/// <param name="salt">盐</param>
/// <returns></returns>
public static bool EqualHash(string text, string hash, byte[] salt)
{
// 原文生成密文
Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(text, salt, ITERATIONS,HashAlgorithmName.SHA1);
var bytes = pbkdf2.GetBytes(HASH_SIZE);
// 密文还原 byte
var chars = hash.Split('-').ToArray();
var hashBytes = chars.Select(x => (byte)int.Parse(x, System.Globalization.NumberStyles.AllowHexSpecifier)).ToArray();
return EqualsBytes(bytes, hashBytes);
}
#endregion
/// <summary>
/// 判断两个字节数组是否相等
/// </summary>
/// <param name="b1"></param>
/// <param name="b2"></param>
/// <returns></returns>
public static bool EqualsBytes(byte[] b1, byte[] b2)
{
return b1.AsSpan().SequenceEqual(b2.AsSpan());
}
// 转换 salt
public static string ConvertSaltToString(byte[] salt)
{
return BitConverter.ToString(salt);
}
// 转换 salt
public static byte[] ConvertSaltToBytes(string salt)
{
byte[] data = Array.ConvertAll<string, byte>(salt.Split("-"), c => Convert.ToByte(c, 16));
return data;
}
}
void Main()
{
// 加密之后
var a1 = Pbkdf2Helper.CreateHashString("aaa", out byte[] bytes);
Console.WriteLine("a1 加密后:");
Console.WriteLine(a1);
// 密钥 byte[] 存储为字符串
var str = Pbkdf2Helper.ConvertSaltToString(bytes);
// 密钥 字符串 还原为 byte[]
var bs = Pbkdf2Helper.ConvertSaltToBytes(str);
// 使用相同的 salt 加密字符串
var a2 = Pbkdf2Helper.CreateHashString("aaa", bs);
Console.WriteLine("a2 加密后:");
Console.WriteLine(a2);
Console.WriteLine("a1 == a2 :");
Console.WriteLine(a1 == a2);
Console.WriteLine(Pbkdf2Helper.EqualHash("aaa", a1, bytes));
Console.WriteLine(Pbkdf2Helper.EqualHash("aaa", a2, bs));
}
文章评论