无法在 C# 中验证哈希密码

问题描述 投票:0回答:3

在C#语言中,我的目的是使用hash_password()对密码进行哈希处理,然后使用verify()方法对其进行验证。我对密码“s3cr3t”进行哈希和盐处理,然后检查两个示例,如果密码是“s3cr3t”则返回 true,而密码“s3cr4t”则返回 false。

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

public class Pbkdf2_test4
{
    public const int salt_size = 24;
    public const int hash_size = 24;
    public const int iteration = 100000;
    static byte[] salt1 = new byte[salt_size];

    private static Rfc2898DeriveBytes hash_password(string password)
    {
        RandomNumberGenerator generator = RandomNumberGenerator.Create();
        byte[] salt = new byte[salt_size];
        generator.GetBytes(salt);

        salt1 = salt;

        Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt1, iteration);
        return pbkdf2;
    }
    
    private static bool verify(Rfc2898DeriveBytes pw_hash, string password)
    {
        //data1 can be a string or contents of a file.
        string data1 = "Some test data";

        try
        {
            Rfc2898DeriveBytes k1 = pw_hash;
            Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(password, salt1, iteration);

            // Encrypt the data.
            Aes encAlg = Aes.Create();
            encAlg.Key = k1.GetBytes(16);
            MemoryStream encryptionStream = new MemoryStream();
            CryptoStream encrypt = new CryptoStream(encryptionStream, encAlg.CreateEncryptor(), CryptoStreamMode.Write);
            byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(data1);

            encrypt.Write(utfD1, 0, utfD1.Length);
            encrypt.FlushFinalBlock();
            encrypt.Close();
            byte[] edata1 = encryptionStream.ToArray();
            k1.Reset();

            // Try to decrypt, thus showing it can be round-tripped.
            Aes decAlg = Aes.Create();
            decAlg.Key = k2.GetBytes(16);
            decAlg.IV = encAlg.IV;
            MemoryStream decryptionStreamBacking = new MemoryStream();
            CryptoStream decrypt = new CryptoStream(decryptionStreamBacking, decAlg.CreateDecryptor(), CryptoStreamMode.Write);
            decrypt.Write(edata1, 0, edata1.Length);
            decrypt.Flush();
            decrypt.Close();
            k2.Reset();
            string data2 = new UTF8Encoding(false).GetString(decryptionStreamBacking.ToArray());

            if (!data1.Equals(data2))
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        catch (Exception e)
        {
            return false;
        }
    }
    public static void Run()
    {
        Rfc2898DeriveBytes pw_hash = hash_password("s3cr3t");
        Console.WriteLine(System.Text.Encoding.UTF8.GetString(pw_hash.GetBytes(hash_size)));

        var result1 = verify(pw_hash, "s3cr3t");
        Console.WriteLine(result1);

        var result2 = verify(pw_hash, "s3cr4t");
        Console.WriteLine(result2);
    }
}

我的问题,不知何故存在一个问题,即 verify(pw_hash, "s3cr3t") 返回 false,但它应该返回 true。在 verify() 中,出现问题但仍然无法理解,因为我给键 k1 和 k2 true,但仍然没有收到相同的哈希/盐,我该如何解决这个问题?

除此之外,我是否应该添加任何东西以使密码存储最安全?

c# security hash passwords salt
3个回答
0
投票

这是加密和解密通行证的程序

    internal class Program
    {
        static void Main(string[] args)
        {
            Pbkdf2_test4 test4 = new Pbkdf2_test4();
            test4.Run();
            Console.ReadLine();
        }
    }

    public class Pbkdf2_test4
    {
        static string key = string.Empty;
        static string iv = string.Empty;

        public Pbkdf2_test4()
        {
            Aes encAlg = Aes.Create();
            encAlg.GenerateKey();
            key = Convert.ToBase64String(encAlg.Key);
            encAlg.GenerateIV();
            iv = Convert.ToBase64String(encAlg.IV);
        }

        public string Encrypt(string plainText, string Key, string IV)
        {
            // Check arguments.
            if (string.IsNullOrWhiteSpace(plainText))
                throw new ArgumentNullException(nameof(plainText));
            if (string.IsNullOrWhiteSpace(Key))
                throw new ArgumentNullException(nameof(Key));
            if (string.IsNullOrWhiteSpace(IV))
                throw new ArgumentNullException(nameof(IV));
            byte[] encrypted;

            // Create an AesCryptoServiceProvider object
            // with the specified key and IV.
            using (Aes aes = Aes.Create())
            {
                aes.Key = Convert.FromBase64String(Key);
                aes.IV = Convert.FromBase64String(IV);

                // Create an encrypt-or to perform the stream transform.
                ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                    }
                    encrypted = msEncrypt.ToArray();
                }                
            }

            // Return the encrypted bytes from the memory stream.
            return Convert.ToBase64String(encrypted);

        }

        public string Decrypt(string cipherText, string Key, string IV)
        {
            // Check arguments.
            if (string.IsNullOrWhiteSpace(cipherText))
                throw new ArgumentNullException(nameof(cipherText));
            if (string.IsNullOrWhiteSpace(Key))
                throw new ArgumentNullException(nameof(Key));
            if (string.IsNullOrWhiteSpace(IV))
                throw new ArgumentNullException(nameof(IV));

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = string.Empty;

            // Create an AesCryptoServiceProvider object
            // with the specified key and IV.
            using (Aes aes = Aes.Create())
            {
                aes.Key = Convert.FromBase64String(Key);
                aes.IV = Convert.FromBase64String(IV);

                // Create a decrypt-or to perform the stream transform.
                ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(cipherText)))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.

                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
            return plaintext;
        }

        public void Run()
        {
            Console.WriteLine("Key is : " + key);
            Console.WriteLine("IV is : " + iv);

            string passord = "s3cr3t";
            string encryptedPassowrd = Encrypt(passord, key, iv);
            string decryptedPassowrd = Decrypt(encryptedPassowrd, key, iv);

            Console.WriteLine($"Password = {passord} and Encrypted password = {encryptedPassowrd}");
            Console.WriteLine($"Password = {passord} and Decrypted password = {decryptedPassowrd}");


            string passord1 = "s3cr4t";
            string encryptedPassowrd1 = Encrypt(passord1, key, iv);
            string decryptedPassowrd1 = Decrypt(encryptedPassowrd1, key, iv);

            Console.WriteLine($"Password = {passord1} and Encrypted password = {encryptedPassowrd1}");
            Console.WriteLine($"Password = {passord1} and Decrypted password = {decryptedPassowrd1}");

        }
    }

0
投票

我认为您误解了Rfc2898DeriveBytes文档中的示例。此函数可用于密码散列(PBKDF2),但您不需要加密部分。像 PBKDF2 这样的密钥派生函数的原始用例是采用(弱)用户密码并将其转换为(强)密钥,然后可用于加密数据(因此称为密钥派生)。后来发现同样的函数也适合生成密码哈希值。

对于您的情况,我建议使用适当的库进行密码散列,例如BCrypt.Net。用法如下:

// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
string hashToStoreInDb = BCrypt.HashPassword(password);

// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from existingHashFromDb.
bool isPasswordCorrect = BCrypt.Verify(password, existingHashFromDb);

0
投票

在 C# 中努力使用 HMACSHA512 验证哈希密码

using HMACSHA512;
using System.Collections;
using System.Security.Cryptography;
using System.Text;

string password = "mySecurePassword123";

// Hash the password
string hashedPassword = HashPassword(password);
Console.WriteLine("Hashed Password: " + hashedPassword);

// Verify the password
bool isPasswordCorrect = VerifyPassword(password, hashedPassword);
Console.WriteLine("Password is correct: " + isPasswordCorrect);


 static string HashPassword(string password)
{
    // Generate a random salt
    byte[] salt = new byte[64]; // 64 bytes for SHA-512
    using (var rng = new RNGCryptoServiceProvider())
    {
        rng.GetBytes(salt);
    }

    // Compute hash using salt
    using (HMACSHA512 hmac = new HMACSHA512(salt))
    {
        byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
        byte[] hash = hmac.ComputeHash(passwordBytes);

        // Combine salt and hash into a single byte array
        byte[] saltedHash = new byte[salt.Length + hash.Length];
        Array.Copy(salt, 0, saltedHash, 0, salt.Length);
        Array.Copy(hash, 0, saltedHash, salt.Length, hash.Length);

        // Convert to base64 string for storage
        string hashedPassword = Convert.ToBase64String(saltedHash);
        return hashedPassword;
    }
}

// Method to verify a password against its hash
 static bool VerifyPassword(string password, string hashedPassword)
{
    // Extract the salt from the hashed password
    byte[] saltedHash = Convert.FromBase64String(hashedPassword);
    byte[] salt = new byte[64]; // SHA-512 produces a 64-byte hash
    byte[] storedHash = new byte[saltedHash.Length - salt.Length];
    Array.Copy(saltedHash, 0, salt, 0, salt.Length);
    Array.Copy(saltedHash, salt.Length, storedHash, 0, storedHash.Length);

    // Compute the hash of the input password with the extracted salt
    using (HMACSHA512 hmac = new HMACSHA512(salt))
    {
        byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
        byte[] computedHash = hmac.ComputeHash(passwordBytes);

        // Compare the computed hash with the stored hash
        return StructuralComparisons.StructuralEqualityComparer.Equals(storedHash, computedHash);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.