在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,但仍然没有收到相同的哈希/盐,我该如何解决这个问题?
除此之外,我是否应该添加任何东西以使密码存储最安全?
这是加密和解密通行证的程序
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}");
}
}
我认为您误解了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);
在 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);
}
}