ASP.NET Identity 2 Configurable Password Hasher

ASP.NET Identity

Default Password Hasher

The default password hasher that comes out of the box with ASP.NET Identity 2 ticks all the right boxes:

  • It actually uses a hashing algorithm (for some reason this is still something we need to congratulate in 2017)
  • It generates a per user salt
  • It iteratively hashes a password (not just once like in vanilla ASP.NET Membership)
  • It uses a derived key

The above can pretty much be summed up with "it uses PBKDF2", but that that didn’t read as nice.

Great, so that’s pretty good for an out of the box password hasher from 2014. But for some reason the password hasher contains the following line of code:

private const int PBKDF2IterCount = 1000; // default for Rfc2898DeriveBytes

Unfortunately, the default password hasher has a hard coded iteration count of only 1000. Whilst this was the minimum number of iterations back in 2000, this is well below an appropraite amount of iterations in 2017 and will not give you much time till the passwords are cracked in the event of a breach (as an example the OWASP password storage cheat sheet currently states Apple uses 10000 iterations as of 2016).

Luckily ASP.NET Identity 2 exposes its password hasher as a property, allowing the default password hasher to be overridden with another implementation of the IPasswordHasher interface, where IPasswordHasher consists of two methods:

string HashPassword(string password);
PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword);

Also luckily for is, the ASP.NET Identity 2 source code is available on CodePlex, and since the existing default password hasher is good enough except for the hardcoded iteration count, we can take the existing code and modify it to meet modern requirements!

Configurable Password Hasher

Our goal here is to take the existing password hasher and modify it to have its iteration count configurable by you, the developer. This iteration count is to be set once, it cannot be changed after users start entering the system, without a well thought out migration plan, or else passwords will not be verifiable (hashes will not match).

So let’s take the existing password hasher, conveniently called 'PasswordHasher', from Microsoft.AspNet.Identity.Core and update it to accept an iteration count, again implementing IPasswordHasher.

public class ConfigurablePasswordHasher : IPasswordHasher {
    private readonly int iterationCount;

public ConfigurablePasswordHasher(int iterationCount = 10000) { if (iterationCount < 1) { throw new ArgumentOutOfRangeException("iterationCount", "Password has iteration count cannot be less than 1"); } this.iterationCount = iterationCount; }
public string HashPassword(string password) { return Crypto.HashPassword(password, iterationCount); }
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword) { if (Crypto.VerifyHashedPassword(hashedPassword, providedPassword, iterationCount)) { return PasswordVerificationResult.Success; } return PasswordVerificationResult.Failed; } }

Most of the heavy lifting was being done in the Crypto class, so let’s change the that to use an iteration count supplied as a parameter instead of the previously hardcoded PBKDF2IterCount.

internal static class Crypto {
    public static string HashPassword(string password, int iterationCount) { }
    public static bool VerifyHashedPassword(string hashedPassword, string password, 
        int iterationCount) { }
}

Nuget

This code is available on GitHub and as a Nuget package:

install-package ScottBrady91.AspNet.Identity.ConfigurablePasswordHasher

Which you can simply assign it to your usermanager like so:

usermanager.PasswordHasher = new ConfigurablePasswordHasher(25000);

This can be done in the default Visual Studio template in the IdentityConfig class by adding the same line of code to the ApplicationManager.Create method.

For alternative approaches, check out the approaches used in MembershipReboot and IdentityReboot.

Share this article: