6
votes

Problem

My application (which I will be writing in C#) uses a key derivation method (Rfc2898DeriveBytes, 4000 iterations) along with a salt to generate a "hash" of a password. This hash is then sent to a database so that the user can use that password in the future to authenticate to their account.

So far that should be secure, but after that, I want to use Rfc2898DeriveBytes on the same password to generate a key which can then be used for encryption. Now the way I was going to do that was to use 5000 iterations of the same method to get a different key, but I am concerned that if the hash stored in the database was compromised (or I was forced to reveal it) it would be possible to derive the second key somehow. Is that possible?

Potential Solutions

  • Ideally I would like to use the same salt, but would using a different one fix the problem?
  • What about using SHA for the database hash and Rfc for the encryption key?
  • Appending a unique but hard coded string (like "website" and "encryption" respectively) to each before deriving the key. (Damien_the_Unbeliever)
  • Generate a double-length key, and use the first half of the derived key for authentication, and the second half for encryption. (erickson)

I would appreciate any advice on how best to improve this process. I would post code but I haven't written it yet.

3
If one of the hashes is compromised, and as a result, the password is determined, then the user can access either functionality, by definition, because they have the plaintext password. Of course, if they can't get the plaintext password from the hash because your hash method + salting is beyond their capability to crack, then it shouldn't be a problem. If the malicious user can crack it and get the plaintext, they can access anything that passwords gives access to, no matter what you do.Servy
So what if it can be. The whole point of hashing is that having the hash alone is meaningless. Someone can't just send you the hash of their password and login, if they could then there's no advantage of using hashing in the first place over using a plaintext password, beyond protecting users that use the same password on multiple systems from a malicious system admin.Servy
Just prepend the text Password to the text of the password for one usage, and Encryption to the text of the password for the other (or, in general, just add some fixed text that's significantly different to each usage)Damien_The_Unbeliever
@Servy a compromised hash with a strong hashing algorithm with strong, solid salting practices usually takes the form of a collision, not a plain text discovery. In other words, an attacker won't just be looking up Razick's plaintext passwords in a rainbow table - it would have to be brute forced with no guarantee that the discovered plaintext is the same plain text the user originally entered. A collision with one algorithm is almost certainly not a collision with the same plaintext in a separate algorithm, and with separate salts, a twin collision like that is nigh impossible.Patrick M
@Razick My point is simply that it is a salt. It's not like a salt or similar to a salt. It is the very definition of a salt. While a salt should, generally, not be constant, it is still a salt even if it is constant.Servy

3 Answers

4
votes

For easy reference, I'm compiling the advice I was given into one answer.

Iteration Count

Although this wasn't part of my question erickson pointed out that 5,000 iterations for Rfc2898DeriveBytes (a PBKDF2 method for C#) is far too few and recommended at least 50,000.

After looking elsewhere for more information, it seems that between 50,000 and 1,000,000 is a good number of iterations, but keep in mind there is an inverse relation between speed and security here: As the number of iterations increases so does security, and the amount of time it takes to derive the key (which is why it's more secure).

Solutions

Although according to erickson the first key could not be used to determine the second, it does make a brute-force attack easier. Therefore, any of the following solutions could be used to address the issue:

  • Generate a double-length key, and use the first half of the derived key for authentication, and the second half for encryption. (erickson)
  • Use a separate salt for each key.
  • Appending a unique but hard coded salt (like "website" and "encryption" respectively) to each before deriving the key. A unique salt should be used in addition.(Damien_the_Unbeliever)

Rejected Solutions

  • SHA_512 is too fast to be secure for my purposes (and for most purposes in which hashes are used). Using multiple iterations can help, but it is far more secure to use a PBKDF2 method such as Rfc2898DeriveBytes.

Conclusion

Thank you everyone for your help, if you have any further insight please comment and I'll do my best to add it.

3
votes

You can derive multiple keys from one password.

Whether you use different salts for different purposes, or generate a longer key and use parts of it for different purposes, depends on whether you need the different keys at the same time. For example, suppose a user logs in, and then expects to be able to encrypt and decrypt content without re-entering their password. In that case, it would be more efficient to generate a double-length key, and use the first half of the derived key for authentication, and the second half for encryption. Generating two keys with different salts would take about twice as long.

On the other hand, if the user is expected to enter the password for authentication, and then some other time for encryption, you might as well select two salts.

With your proposal, revealing the authentication hash stored in the database wouldn't lead immediately to a compromise. That is, you couldn't simply iterate the hash function; each iteration of PBKDF2 uses the password as input. But it does mean that only 1000 iterations are required to test a password as an encryption key, and that provides almost no protection from a dictionary attack.

By the way, 5000 iterations is not nearly enough. 50000 iterations would be a good start.

2
votes

Using a different salt between the two calls of Rfc2898DeriveBytes solves your problem.

Think of it this way, two different users use the same password, will knowing the hash of one compromise the other? Only if the two users use the same salt!

What you are doing is no different, just use two good salts for both uses of Rfc2898DeriveBytes and you will be fine. No need to change the number of iterations for the two methods, just change the salt.

There is no reason you need to re-use the salt, the salt is not secret information. Most implementations of file encryption puts the salt (also known as the IV) at the front of the encrypted blob.


For your other solution "What about using SHA for the database hash and Rfc for the encryption key?" This is a VERY bad idea. Just using SHA for the database means it will be much easier for them to get the original password, then they can use that easier task to do the harder task of decrypting the Rfc based one.