In C# language, my purpose is to hash password with hash_password(), then verify it with verify() methods. I hash and salt for password 's3cr3t', then check for two examples and return true if password is 's3cr3t' and return false for password 's3cr4t'.
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);
}
}
My question, somehow there is a problem that for verify(pw_hash, "s3cr3t") that returns false however it should return true. In verify(), there is a problem but still could not understand because I give keys k1 and k2 true, but still does not receive hash/salt same, how can I fix this problem?
Apart from this, shuld I add anything to make password storage safest?
CodePudding user response:
Here is program to encrypt and decrypt pass
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}");
}
}
CodePudding user response:
I think you misunderstood the example in the documentation of Rfc2898DeriveBytes. This function can be used for password hashing (PBKDF2), but then you don't need the encryption parts. The original use case of a key derivation function like PBKDF2 was to take a (weak) user password and turn it into a (strong) key which can then be used to encrypt data (thus the name key-derivation). Later one saw that the same functions are suitable to generate password hashes.
In your case I would recommend to use an appropriate library for password hashing, like BCrypt.Net. The usage would look like:
// 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);