C# PBKDF2 Encryption Algorithm

2021年8月12日 14点热度 0人点赞 0条评论
内容目录

Directly use the .NET CLR library for generation without relying on third-party frameworks. The code has been updated to the latest implementation, removing deprecated interfaces. The Password-Based Key Derivation Function 2 (PBKDF2) makes it harder for others to guess your account password through brute force.

The PBKDF2 encryption is irreversible, so it can be used to handle passwords and similar data; it can only be compared, but not restored.

For example, each user sets an encrypted key, and each user uses their own key to encrypt their password, which is irreversible. Even if someone obtains a user's password and cracks it, each user's key is different, making large-scale cracking impossible.

    /// <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));
}

file

痴者工良

高级程序员劝退师

文章评论