Reputation: 12423
I've been tasked with the task of writing a new method for computing password hashes to replace the old one which we do not feel is sufficiently secure yet. A good while ago, I read the book Security Driven .NET, in which I learned that the important part is to use a algorithm with configurable amount of revolutions (as opposed to a simple hash), and that in .NET the recommended one was called PBKDF2
for password handling. I also read that as an improvement to how ASP.NET deals with passwords, it would be nice if the resulting hash stored in the database was cryptographically tied to the user (either name or id) by way of using the PBKDF2 to create a master key, and then use the username (or id) to create a derived key using HKDF. But again, this is superficial knowledge that I read from a book that I do not currently have access to, so I can't recheck if my memory is correct.
Also, I've not used the .NET DerivedBytes APIs before, so I might be doing that wrong. So my question is this: Am I doing things right in the following code? Am I using the APIs correctly? And is this implementation "sufficiently secure"? Or am I doing something wrong which completely removes all of the security?
protected override byte[] ComputeHash(string user, string salt, string password)
{
var userBytes = user.ToBytes();
using (var pbkdf2 = new PBKDF2(MacFactories.HMACSHA512, password.ToBytes(), salt.ToBytes()))
{
var masterKey = pbkdf2.GetBytes(128);
using (var hkdf = new HKDF(MacFactories.HMACSHA512, masterKey, userBytes, userBytes))
{
return hkdf.GetBytes(64);
}
}
}
Upvotes: 4
Views: 960
Reputation: 76
You have the correct idea/approach - here is a slightly better implementation:
byte[] ComputeHash(string user, string salt, string password)
{
using (var pbkdf2 = new PBKDF2(HMACFactories.HMACSHA512, password, salt.ToBytes()))
using (var hkdf = new HKDF(HMACFactories.HMACSHA512, pbkdf2.GetBytes(64), user.ToBytes()))
return hkdf.GetBytes(64);
}
You should not ask pbkdf2
for more bytes than the byte-length of the underlying PRF (in your case, SHA512 which produces 64 bytes).
You can leave hkdf
context as null
since you don't seem to have a need for it.
For others who might be wondering what library the above code uses - Inferno crypto.
Upvotes: 6