5

I am writing a class to hash passwords which implements Key Stretching by using the System.Security.Cryptography.Rfc2898DeriveBytes class to generate the keys used to compute the hash value.

The code essentially does this:

// Higher iterations value results in better key strength
var db = new Rfc2898DeriveBytes(password + salt + secretKeyFromConfigFile,
                                saltAsBytes,
                                iterations);

byte[] hashKey = db.GetBytes(64); // 64 bytes is the recommended size for the HMACSHA512 class

var sha512 = new System.Security.Cryptography.HMACSHA512(hashKey);

byte[] hashInput = System.Text.Encoding.UTF8.GetBytes(password + salt + secretKeyFromConfigFile);

byte[] hash = sha512.ComputeHash(hashInput);

return System.Convert.ToBase64String(hash);

The documentation for System.Security.Cryptography.Rfc2898DeriveBytes indicates that it uses "... a pseudo-random number generator based on System.Security.Cryptography.HMACSHA1" to derive the key.

Since SHA512 is better than SHA1 Would it be better to simply apply the SHA512 hash function like so?

byte[] hash;

for (int i = 0; i < iterations; i++)
{
    hash = sha512.ComputeHash(hash ?? hashInput);
}

return System.Convert.ToBase64String(hash);

What is the difference between strengthening the key (as shown in the 1st code block) and strengthening the hash (as shown in the 2nd code block)?

What is the preferred method to do key stretching when hashing passwords?

Rudy
  • 75
  • 6

2 Answers2

4

Unless you're a crypto guru, just stick to Rfc2898DeriveBytes which is very probably correctly implemented. Resist the temptation of hacking together a custom solution which will break the security of your system.

There's no reason to hash the output of Rfc2898DeriveBytes.

Directly use GetBytes(), it's been designed for that intent.

What's the purpose of your key stretching? Cipher or password hash? For a password hash, you probably want to use a different algorithm.

Edouard A.
  • 6,100
  • 26
  • 31
  • The aim is to create a password hashing system that incorporates key stretching. I am not aware of any ready made class or library that already does that, unless you meant to only use Rfc2898DeriveBytes and store the return value from GetBytes() as the hash? I assume by "it's been designed for that intent" you mean designed for key derivation? The purpose of my key stretching is to "secure against a brute force attack by increasing the time it takes to test each possible key". – Rudy Mar 22 '11 at 14:28
  • There is known good solution for this problem: http://en.wikipedia.org/wiki/Blowfish_%28cipher%29#Blowfish_in_practice Otherwise you can use Rfc2898DeriveBytes, store that and to check the password, apply the method on the plain text password and compare the output. – Edouard A. Mar 22 '11 at 14:33
  • 1
    Yes I've had a look at a C# implementation of Bcrypt and it appears to do the same thing as the Rfc2898DeriveBytes class if I'm understanding it right. Except that Bcrypt uses the Blowfish algorithm whereas Rfc2898DeriveBytes uses SHA1, which I felt made it necessary to include a SHA512 hash with the derived key from GetBytes() as the HMACSHA512 key. I guess I'm asking how would you implement a hash method with SHA512 or another algorithm that uses key strengthening. Or are we only left with Bcrypt (Blowfish) and Rfc2898DeriveBytes (SHA1) as the only choices? – Rudy Mar 22 '11 at 14:56
  • @Rudy, Bcrypt != blowfish. Bcrypt blowfish-based and makes use of the known expensive key setup of said algorithm; being slow is one of the principal design features of Bcrypt. Also, you say "(...) do the same thing (...) except (...)", in cryptography, this translates into "They are completely different". – Jacco Mar 22 '11 at 16:11
1

This is pretty old, but I came across this when I was googling for the same question and the solution is indeed to stick to the output of Rfc2898DeriveBytes.

Rfc2898DeriveBytes is the .NET implementation of PBKDF2. It takes your password, a salt and the number of iterations. That is already the whole implementation and there is no need to rehash the outcome with another hashing algorithm.

In fact it is strongly advised to not create your own implementation around this, as you might break the security as other people already mentioned here.

That's what https://openid.stackexchange.com does as well, as you can see here: https://code.google.com/p/stackid/source/browse/OpenIdProvider/Current.cs#1135

dustinmoris
  • 3,195
  • 3
  • 25
  • 33