My C# console app actually prompts the user for a password and process the password as a string:
Console.Write("Please enter your password: ");
string password = GetPassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
Is there a way to modify the code to use a secure password (reducing the risk of having the password be exposed throught a memory dump)?
I would like something like:
Console.Write("Please enter your password: ");
SecureString password = GetSecurePassword();
securePassword = GetSecurePassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
password.Dispose();
Below a complete sample the compiles under Visual Studio 2015:
using System;
using System.Security;
using System.Security.Cryptography;
using System.Text;
namespace PasswordTest
{
internal class Program
{
public static byte[] GenerateSalt()
{
const int saltLength = 32;
using (var randomNumberGenerator = new RNGCryptoServiceProvider())
{
var randomNumber = new byte[saltLength];
randomNumberGenerator.GetBytes(randomNumber);
return randomNumber;
}
}
private static byte[] Combine(byte[] first, byte[] second)
{
var ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static byte[] HashPasswordWithSalt(byte[] toBeHashed, byte[] salt)
{
using (var sha256 = SHA256.Create())
{
return sha256.ComputeHash(Combine(toBeHashed, salt));
}
}
private static string GetPassword()
{
// skip here the code that prompts the password
// to make simple test that just return a password
return "mypa55w0rd";
}
// Here the code that would normally be used to prompt for a password
// and return a SecureString
public static SecureString GetSecurePassword()
{
var password = new SecureString();
// get the first character of the password
var nextKey = Console.ReadKey(true);
while (nextKey.Key != ConsoleKey.Enter)
{
if (nextKey.Key == ConsoleKey.Backspace)
{
if (password.Length > 0)
{
password.RemoveAt(password.Length - 1);
// erase the last * as well
Console.Write(nextKey.KeyChar);
Console.Write(" ");
Console.Write(nextKey.KeyChar);
}
}
else
{
password.AppendChar(nextKey.KeyChar);
Console.Write("*");
}
nextKey = Console.ReadKey(true);
}
Console.WriteLine();
// lock the password down
password.MakeReadOnly();
return password;
}
private static void Main(string[] args)
{
// Generate the salt
var salt = GenerateSalt();
// Hash a password stored in a string variable
// Issue: can be exposed through a memory dump
var password = GetPassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
// How to hash a password stored in a securestring so that the code could become
// something like:
/*
Console.Write("Please enter your password: ");
SecureString password = GetSecurePassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
password.Dispose();
*/
Console.WriteLine("Hashed Password = " + Convert.ToBase64String(hashedPassword));
Console.ReadLine();
}
}
}