We are currently using HMACSHA512 in .net, with a 128Char (64byte) validation key The salt is 64 char randomly generated string. We allocated 2048 length on the database for the hashed base64 string result. It will be a public facing website. Is this approach reasonable or should it be changed to another approach such as Rfc2898DeriveBytes?
public string HashEncode(string password, string salt, string validationKey) {
byte[] hashKey = BosUtilites.HexStringToByteArray(validationKey);
var sha512 = new HMACSHA512(hashKey);
var hashInput = BosUtilites.StringToByteArray(password + salt);
byte[] hash = sha512.ComputeHash(hashInput);
return Convert.ToBase64String(hash);
}
public string GenerateSimpleSalt(int Size = 64) {
var alphaSet = new char[64]; // use 62 for strict alpha... that random generator for alphas only
//nicer results with set length * int i = 256. But still produces excellent random results.
//alphaset plus 2. Reduce to 62 if alpha requried
alphaSet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890#=".ToCharArray();
var tempSB = GenerateRandomString(Size, alphaSet);
return tempSB.ToString();
}
public StringBuilder GenerateRandomString(int Size, char[] alphaSet) {
using (var crypto = new RNGCryptoServiceProvider()) {
var bytes = new byte[Size];
crypto.GetBytes(bytes); //get a bucket of very random bytes
var tempSB = new StringBuilder(Size);
foreach (var b in bytes) { // use b , a random from 0-255 as the index to our source array. Just mod on length set
tempSB.Append(alphaSet[b%(alphaSet.Length)]);
}
return tempSB;
}
EDIT2: In case Someone finds this via google, I have included the lessons learnt Average sample in tests on workstations was around 300 msecs. This should not be too noticeable during logon. And no more need for a Validation key. Which is a relief :-)
SCrypt package installed via nuget. and rfc2898 PBKDF2 changed to be large number or iterations but only 20bytes output. SAme CPU time.
New passwords are encoded in SCRYPT by default,
<package id="CryptSharpOfficial" version="2.0.0.0" targetFramework="net451" />
// save salt, hash algorithm used and resulting encoding on user record
public string PasswordEncode(string password, byte[] salt, HashAlgorithm hashAlgorithm ) {
switch (hashAlgorithm) {
case HashAlgorithm.PBKDF2:
var deriver2898 = new Rfc2898DeriveBytes(password, salt,<Use a number around 50K>); // approx 300msecs on workstation
byte[] hash = deriver2898.GetBytes(20); //
return Convert.ToBase64String(hash);
case HashAlgorithm.Scrypt:
var key = Encoding.UTF8.GetBytes(password);
byte[] hashScrypt = SCrypt.ComputeDerivedKey(key: key, salt: salt,
cost: 65536, // must be a power of 2 !, on PC, singlethread this is approx 1 sec
blockSize: 8,
parallel: 1,
maxThreads: 1,
derivedKeyLength: 128);
return Convert.ToBase64String(hashScrypt);
default:
throw new ArgumentOutOfRangeException("hashAlgorithm");
}
}