Search code examples
c#securityhashpasswordssalt

Securing users passwords using c# - Rfc2898DeriveBytes vs SHA512


I've been reading about securing users passwords in the database (https://crackstation.net/hashing-security.htm). The basic idea is understood - generate a random Salt, append it to the password and hash the password.

So here's what I did (I didn't put here some methods that do conversion to strings):

RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create();
byte[] rndBytes = new byte[512];
randomNumberGenerator.GetBytes(rndBytes);
string salt = ToHexString(rndBytes);

var sha512Hasher = SHA512.Create();
string hashedPwd = ToHexString(sha512Hasher.ComputeHash(GetBytes(pwd + salt)))

According to the article this is secured but can be even more secured by using "key stretching" which for my understanding is hashing that done slower (using a parameter) to make brute-force the password harder.

So here's what I did:

RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create();
byte[] salt = new byte[512];
randomNumberGenerator.GetBytes(salt);
Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(user.Password, salt, 1000);
byte[] hashBytes = k1.GetBytes(512);
string hash = ToHexString(hashBytes);

Now here are my questions:

  1. What is the difference between SHA512 and Rfc2898DeriveBytes? which is more secure?
  2. Should I have smaller salt with more iterations? Will it make it more secure?
  3. On a 1000 iterations it runs very fast - how slow should it be? half a second? a second? What is the rule of thumb here?
  4. On the database - should I convert the byte array to string and store strings or should I store the byte array in a binary data field?

Edit (another questions)

  1. If I iterate a 1000 times over rehashing SHA512 - does it give the same security?

Solution

    1. What is the difference between SHA512 and Rfc2898DeriveBytes?

    SHA512 is a cryptographic hash function, while Rfc2898DeriveBytes is a key-derivation function. As you already wrote, hash functions are too fast and can be brute-forced too easily, that's why we need functions with a cost factor like BCrypt, SCrypt, PBKDF2 or Argon2. As far as i know, Rfc2898DeriveBytes implements the PBKDF2 using a HMAC with SHA1. This answers your other question that an iterated SHA is less secure than Rfc2898DeriveBytes.

    1. Should I have smaller salt with more iterations?

    Salt and cost factor are not related and have different purposes. The salt prevents the usage of rainbow tables, the iterations are a counter measure for brute-force attacks. More infos you can get from my tutorial about safe password storage. So no, don't make the salt shorter.

    1. how slow should it be?

    Of course this depends on your server and your requirements for security, slower means harder to brute-force. A rule of thumb is about 50 milliseconds for a single hash.

    1. On the database - should I convert the byte array to string?

    This is up to you. Strings are easier to handle for backups, migration and debugging, while byte arrays need less space in the database. Maybe you should also have a look at BCrypt.Net, it generates strings as output which contain the salt and are easy to store in a single database field [string].