简体   繁体   中英

Storing PBKDF2 Iterations

So I am hashing passwords to a database using the Rcfc2898DeriveBytes class in .NET

Everything is working fine for hashing and storing both the final hash and the salt.

However, I am having an issue in figuring out to store the number of iterations that the class runs through when hashing the password.

After Googling, I have seen only that it should be stored in front of the password hash but I am unsure how to go about this in a way that would make the number retrievable in case I were to change the number later (which without storing the iterations, the change would break the old passwords).

I see the bcrypt will do this all for you but I don't have the luxury of added a library to the project.

What is the best way to store this number and how would I go about doing that so it is retrieveable and not just lost in the hash when stored (how would I seperate it from the hash once I get it back)?

Thanks for any advice and information ahead of time!

In principle, you don't have to store the amount of iterations at all. Just like you don't have to store the hash type if you keep it consistent.

I would propose something slightly different: store a single byte (prefixed) that contains a version number, starting with zero, just before the salt & hash. This version number is linked to the number of iterations, the hash method, the character encoding method and of course PBKDF2. If you want to upgrade to a better protocol, simply store 01 as initial byte and link that with new parameters. This way you can distinguish between the old style passwords and new style.

Normally you can only upgrade if the user enters his password as a hash is not reversible, so it's not easily possible to upgrade the number of iterations.

I found what I am looking for at the URL below.

https://cmatskas.com/-net-password-hashing-using-pbkdf2/

Relevant code here:

public class PasswordHash  
{
    public const int SALT_BYTE_SIZE = 24;
    public const int HASH_BYTE_SIZE = 20;
    public const int PBKDF2_ITERATIONS = 1000;
    public const int ITERATION_INDEX = 0;
    public const int SALT_INDEX = 1;
    public const int PBKDF2_INDEX = 2;

    public static string HashPassword(string password)
    {
        var cryptoProvider = new RNGCryptoServiceProvider();
        byte[] salt = new byte[SALT_BYTE_SIZE];
        cryptoProvider.GetBytes(salt);

        var hash = PBKDF2(password, salt, PBKDF2_ITERATIONS,    HASH_BYTE_SIZE);
        return PBKDF2_ITERATIONS + ":" +
               Convert.ToBase64String(salt) + ":" +
               Convert.ToBase64String(hash);
    }

    public static bool ValidatePassword(string password, string correctHash)
    {
        char[] delimiter = { ':' };
        var split = correctHash.Split(delimiter);
        var iterations = Int32.Parse(split[ITERATION_INDEX]);
        var salt = Convert.FromBase64String(split[SALT_INDEX]);
        var hash = Convert.FromBase64String(split[PBKDF2_INDEX]);

        var testHash = PBKDF2(password, salt, iterations, hash.Length);
        return SlowEquals(hash, testHash);
    }
}

I took HashPassword method to save my values to the database for later use. I used the ValidatePassword to get them back out for me to use again. It worked like a charm and allowed me to make the size of the bytes for the salt and hash, along with the number of iterations variable based on the strength needed. The values are just stored in the Web/App config file.

Thank you for the answers everyone!

Take a look at how bcrypt stores the hashes here: What column type/length should I use for storing a Bcrypt hashed password in a Database?

So if you only plan to change iterations, you could go for something simple like this:

$<number of iterations>$<hash>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM