Skip to content

Instantly share code, notes, and snippets.

@georgepowell
Created September 26, 2014 14:55
Show Gist options
  • Save georgepowell/956a5be1c4cc337bc57e to your computer and use it in GitHub Desktop.
Save georgepowell/956a5be1c4cc337bc57e to your computer and use it in GitHub Desktop.
CryptSharp BCrypt sample with secure salt generation and variable difficulty
using System;
using System.Text;
using CryptSharp;
using CryptSharp.Utility;
using System.Security.Cryptography;
using System.Diagnostics;
namespace PasswordHashingTest
{
class Program
{
// Hashing time increases exponentially as 'Difficulty' increases linearly. Between 4 and 31.
// Represents something like the logarithm of the number of iterations.
// Difficulty should be as high as possible without annoying users, maybe 250-500ms.
static int Difficulty = 10;
static void Main(string[] args)
{
while (true)
{
//TimingDemo();
HashDemo();
}
}
// Calculates a hash 20 times and averages time taken to calculate them.
private static void TimingDemo()
{
Console.WriteLine("Enter a difficulty from 4 to 30. (Any higher than 14 takes ages...)");
Difficulty = Convert.ToInt32(Console.ReadLine());
int iterations = 20;
Action hashingAction = () =>
{
CalculateBCrypt("asdfasdfasdf");
};
Console.WriteLine("Average hashing time is {0}ms over {1} iterations at difficulty {2}",
TimeAverage(hashingAction, iterations),
iterations,
Difficulty);
Console.WriteLine("Play again?");
Console.ReadLine();
}
// Generates a salt and calculates a hash for a given password.
private static void HashDemo()
{
Console.WriteLine("Enter a password:");
string password = Console.ReadLine();
Action hashingAction = () => { CalculateBCrypt(password); };
int elapsedTime = Time(hashingAction);
Console.WriteLine();
Console.WriteLine(" took {0}ms at difficulty {1}", elapsedTime, Difficulty);
Console.WriteLine();
}
// Runs the action a number of times and returns the average execution time
static int TimeAverage(Action action, int iterations)
{
int sum = 0;
for (int i = 0; i < iterations; i++)
{
int time = Time(action);
sum += time;
Console.WriteLine();
Console.WriteLine("attempt {0} took {1}ms", i, time);
Console.WriteLine();
}
return sum / iterations;
}
// Generates a salt and calculates the bcrypt hash for the given password. Outputs the results to the console.
private static void CalculateBCrypt(string password)
{
var crypter = new BlowfishCrypter();
var passwordBytes = Encoding.Unicode.GetBytes(password); // Convert passwords to bytes.
var salt = GenerateSalt(); // Generate byte array 16 long from cryptographically random source.
var saltString = Convert.ToBase64String(salt); // Convert to string for printing.
var hash = BlowfishCipher.BCrypt(passwordBytes, salt, Difficulty); // Perform bcrypt algorithm with set difficulty.
var hashString = Convert.ToBase64String(hash); // Convert hash to string for storing/printing
Console.WriteLine();
Console.WriteLine("salt: {0}", saltString);
Console.WriteLine("hash: {0}", hashString);
}
// Calculates the execution time for the given action
static int Time(Action action)
{
var timer = new Stopwatch();
timer.Start();
action.Invoke();
timer.Stop();
return (int)timer.ElapsedMilliseconds;
}
// Generate a 16 byte salt using cryptographic random number generator.
static byte[] GenerateSalt()
{
// To be completely secure we should generate a salt from a cryptographical random number source.
var prov = new RNGCryptoServiceProvider();
byte[] salt = new byte[16];
prov.GetBytes(salt);
return salt;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment