Created
September 26, 2014 14:55
-
-
Save georgepowell/956a5be1c4cc337bc57e to your computer and use it in GitHub Desktop.
CryptSharp BCrypt sample with secure salt generation and variable difficulty
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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