Have you ever looked at a user record in an ASP.NET Identity’s users table and wondered just what is being saved on the PasswordHash
column?
It looks like this (it’s in base64):
AQAAAAEAACcQAAAAEJSPbbFM1aeXB8fGRV7RRamLpjzktAF7FjwDWtFx35eol4AxN6vm4zWR9EApc7WPsQ==
Apart from maybe satisfying your curiosity, what could you benefit from knowing exactly what’s on that seemingly random sequence of characters?
Well, you could confidently update from different versions of ASP.NET Identity.
Also, if you ever get tired of ASP.NET Identity for whatever reason, you could move your user accounts to whatever alternative you found that might be better.
Or, you just want to have a simple users’ table and not have to deal with all of what you need to know to make ASP.NET Identity work. All of this while still being able to move your user accounts to ASP.NET Identity some day if you choose to do so.
Whatever that reason may be, lets pause and think first about what makes a good stored password.
First a password should only be know by the the person that holds the account. If we look at what’s saved at the PasswordHash
‘s column that seems to be true. If I don’t tell you which password I entered to generate the PasswordHash
you won’t be able to guess it.
If it becomes public somehow (e.g. the server that hosts the database where that password hash is stored is breached), the attackers won’t be able to do much with it. They might try to guess it, but if it’s a good password they shouldn’t be able to in any reasonable amount of time. For that to be the case, a salt should be used.
If you don’t know what salt is in this context I recommend my other article Things you wanted to know about storing passwords but were afraid to ask.
In case you are in a hurry here’s a summary: it’s a random piece of data that is combined with the password. That makes it impossible for an attacker to generate hashes for common passwords, store them, and then use them to compare with a password hash.
For example, imagine that P@assw0rd
and qwerty
are common passwords (they really are). If an attacker knows the algorithm that was used to hash the user’s passwords he can just compute HASH(P@ssw0rd
) and HASH(qwerty
) and store them. Then the process of figuring out which password was used to generate a particular password hash becomes an exercise in searching for a match in the stored passwords.
That’s much faster than for each password hash, picking a possible password, generating its hash and seeing if it matches.
By using a salt the passwords will all look unique, for example, instead of querty
we’d hash quertyG%2cf#
and save HASH(quertyG%2cf#
) and the salt G%2cf#
.
But the user table in ASP.NET Identity only has a PasswordHash
column, there’s no salt column. Does that mean there’s no salt being used?
Fortunately there’s a salt being used, in fact there’s much more than the password hash in the PasswordHash
column.
By the way, from now on I’ll be referring to the contents of the PasswordHash
column as a password hash, however it’s not. It stores several values in it, the closest one to a hash is the result of the PBKDF2 algorithm, but even that is not really a hash (it’s common to call it a hash as well, and I’m also guilty of it).
It’s so common to refer to all of this as a hash though that I’ll just do it. Hopefully as you read along it will be clear what each thing inside PasswordHash
is, and this shortcoming on my part won’t make things confusing.
PasswordHash breakdown
Even though the PasswordHash
is just one column it contains information about which version of ASP.NET Identity was used, the salt, information about the pseudo random function (PRF) that was used and the hashed password.
If you convert the base64 representation to a byte array (using Convert.FromBase64String
) you’ll get the following configuration:
- 1st byte
- The value
0
to indicate a password hash from Identity Version 2 or the value1
to indicate Version 3
- The value
For version 2 of Identity (when the first byte is 0) you get the following:
- 2nd to 17th
- 16 bytes where the random salt is stored
- 18th to 49th
- 32 bytes where the password hash is stored
For the latest version of Identity (when the first byte is 1) you get the following:
- 2nd to 5th byte
- An unsigned int that will contain a value from the enumeration Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerviationPrf
- 0 for HMACSHA1
- 1 for HMACSHA256
- 2 for HMACSHA512
- 6th to 9th byte
- An unsigned int that stores the number of iterations to perform when generating the hash (if you don’t know what this means go check out Things you wanted to know about storing passwords but were afraid to ask)
- 10th to 13th
- An unsigned int that stores the salt size (it’s always 16)
- 14th to 29th
- 16 bytes where the random salt is stored
- 30th to 61th
- 32 bytes that contain the hashed password
While in the latest version of identity it’s possible to specify the number of iterations to apply through configuration, in version 2 that number is fixed to 1000.
It’s important to know how the unsigned ints are stored. They are stored backwards. Let me explain that. An unsigned int is 4 bytes long, and you can convert one to byte[]
by using BitConverter
, for example:
byte[] unsignedIntAsByteArray = BitConverter.GetBytes((uint)10000);
Here’s how that look if you print the bytes as a sequence of 8 bits in order (byte[0] is the leftmost sequence of eight bits and byte[3] is the rightmost):
00010000 00100111 00000000 00000000
To rightmost bit of the first byte represents the least significant bit, i.e. 2^0, the one left to that 2^1, etc. The first bit of the second set of 8 bits (second byte) represents 2^8, the one left to that one is 2^9, etc.
It’s not easy to read this way, but you can confirm that it’s 10000 by summing up 2^4 + 2^8 + 2^9 + 2^10 + 2^13.
The uints are not stored this way. They are stored in reverse order, i.e. the original byte[0] is on byte[3], byte[1] is on byte[2], etc:
00000000 00000000 00100111 00010000
This is important to know because when creating the byte array that goes into PasswordHash
(as base64) if you don’t order the uints this way it won’t be valid.
Custom PasswordHasher configuration
The class in ASP.NET Identity responsible for generating and validating passwords is called PasswordHasher
, and it’s source code is available in github here.
One thing that is reasonable to assume when we look at the format of the saved “hash” for V3 is that it seems that the number of iterations to perform, the salt size and the particular PRF function to use are all configurable.
Unfortunately that is not the case. You can only configure which version you want to use (V2 or V3) and the number of iterations to perform. Furthermore, the number of iterations is only taken into account if you select V3. If V2 is selected this value is ignored.
Here’s how you can configure an ASP.NET Core web application that uses ASP.NET Identity and set it to use V2:
In Startup.cs’ ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
//...
services.Configure<PasswordHasherOptions>(options => {
options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2;
//options.IterationCount = 50000; //this value will be ignored if you use V2
});
}
If you use V3 you can specify an IterationCount:
public void ConfigureServices(IServiceCollection services)
{
//...
services.Configure<PasswordHasherOptions>(options => {
options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV3;
options.IterationCount = 50000; //this value will be used only if you use V3
});
}
There’s no option to select which PRF function to use (HMACSHA1, HMACSHA256, etc) or change the salt size. The PRF function for V2 is HMACSHA1 and for V3 is HAMCSHA256.
Generating your hash without ASP.NET Identity
Imagine you want to be able to generate your own password hash without having to use ASP.NET Identity. Maybe you don’t like the database structure that ASP.NET Identity creates with all those AspNetSomething tables. Maybe you’re building a web site where you’re the only user that needs an account.
The latter was what I went through recently. The right sidebar (or down at the bottom if you are on mobile) on this website has an “Archive” widget that is showing past blog posts. That widget is powered by an ASP.NET Core website that only has one user, myself. I can login to it and add/edit what shows up in that “Archive”.
I didn’t want to deal with configuring ASP.NET Identity for just one user. Also, I found the idea of being able to set an admin user’s password through configuration appealing.
Another strong motivation for doing this is that if you go look at PasswordHasher
you’ll see that the method for generating the PasswordHash
, which is named HashPassword
takes in as a parameter an instance of TUser
. TUser
is not used in this method, in fact TUser
is generic parameter for the PasswordHasher
class, but isn’t used anywhere in that class. If you want to reuse PasswordHasher
you’ll have to pass in a TUser
, which is awkward because it’s not necessary.
Furthermore, you can’t pick a PRF other than HMACSHA256
and a salt size different than 16, even though the code to validate a password can deal with other PRFs and salt sizes.
Lets first describe how you can generate a V3 password hash “by hand” (the process for V2 is very similar).
First thing that you need to know is how to generate a salt. In .Net core you do that using a RandomNumberGenerator
which you can find in the System.Security.Cryptography
namespace.
Here’s an example of how you can generate a 128 bit (16 bytes) salt:
using (var rng = RandomNumberGenerator.Create())
{
var salt = new byte[128/8];
rng.GetBytes(salt); //The GetMethod fills the salt array with random data
}
The Create
method will give you an instance of a RandomNumberGenerator
that is appropriate for the specific platform you are running under (Windows, Linux or Mac).
The only other thing you need to know is how to use Pbkdf2 (Password Based Key Derivation Function 2). If you want to understand why Pbkdf2 is used I shamelessly recommend reading Things you wanted to know about storing passwords but were afraid to ask.
To get an implementation of Pbkdf2 you need to install the Microsoft.AspNetCore.Cryptography.KeyDerivation nuget package.
$ dotnet add package Microsoft.AspNetCore.Cryptography.KeyDerivation
Even though that NuGet package has AspNetCore in its name it does not depend on ASP.NET, you can run it in a .Net core console application.
Here’s how you can generate a password hash using Pbkdf2 with HMACSHA256 for password “cutecats”:
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
//...
var pbkdf2Hash = KeyDerivation.Pbkdf2(password: "cutecats",
salt: salt,
prf: KeyDerivationPrf.HMACSHA256,
iterationCount: 10000,
numBytesRequested: 32);
Here we are using the same defaults that ASP.NET Identity V3 uses. A 16 byte salt, HMACSHA256, 10000 iterations and a 32 bytes hash (numBytesRequested
).
We now have everything we need to generate a valid ASP.NET Identity PasswordHash
. The only thing we need to do is to put everything together in a byte[]
.
The byte[]
that we need is 61 bytes long, so lets create one:
var identityV3Hash = new byte[1 + 4/*KeyDerivationPrf value*/ + 4/*Iteration count*/ + 4/*salt size*/ + 16 /*salt*/ + 32 /*password hash size*/];;
The first byte is the version marker, for V3 it’s 1:
identityV3Hash[0] = 1;
The next 4 bytes are for an uint that contains the int value of the enumeration KeyDerivationPrf’s HMASHA256 value, which is 1.
uint prf = (uint)KeyDerivationPrf.HMACSHA256; // or just 1
byte[] prfAsByteArray = BitConverter.GetBytes(prf).Reverse().ToArray(); //you need System.Linq for this to work
Buffer.BlockCopy(prfAsByteArray, 0, identityV3Hash, 1, 4);
BitConverter.GetBytes
is how you can get the byte array from a value type like uint. Then you can just call .Reverse
and convert the IEnumerable
that Reverse
returns back to an array.
We then use Buffer.BlockCopy
to copy the uint array to the identityV3Hash array. The signature of BlockCopy
is source array, source offset, destination array, destination offset and number of bytes to copy.
The next 4 bytes contain the iteration count. The default is 10000, so lets use that in this example:
byte[] iterationCountAsByteArray = BitConverter.GetBytes((uint)10000).Reverse().ToArray();
Buffer.BlockCopy(iterationCountAsByteArray, 0, identityV3Hash, 1 + 4, 4);
Similar story for the salt size which is 16 (you can actually use a different value larger than 16, it will be validated correctly, although there’s no way of doing this through configuration):
byte[] saltSizeInByteArray = BitConverter.GetBytes((uint)16).Reverse().ToArray();
Buffer.BlockCopy(saltSizeInByteArray, 0, identityV3Hash, 1 + 4 + 4, 4);
Now we just need to copy the salt we’ve generated earlier:
Buffer.BlockCopy(salt, 0, identityV3Hash, 1 + 4 + 4 + 4, salt.Length);
And finally the actual hash:
Buffer.BlockCopy(pbkdf2Hash, 0, identityV3Hash, 1 + 4 + 4 + 4 + salt.Length, pbkdf2Hash.length);
Convert it to base 64 and you are ready to use it:
var identityV3Base64Hash = Convert.ToBase64String(identityV3Hash);
If you open an ASP.NET Identity AspNetUsers’ table that has at least one user and put identityV3Base64Hash
there, you’ll be able to log in with that user with the password “cutecats”.
Manual Validation
Now that we can generate valid ASP.NET Identity V3 PasswordHash
es we need to be able to validate them, i.e., given a password verify that it is the right password for the PasswordHash
.
The process goes like this: convert the PasswordHash
to a byte[]
and extract from it which pseudo random function was used (value from the KeyDerivationPrf
enumeration), the number of iterations for PBKDF2, the salt and actual PBKDF2 hash.
After this we can recompute the PBKDF2’s hash using an input password and compare that with the stored PBKDF2 hash. If they are equal, then the password is correct.
Let’s do that step by step. Imagine passwordHash
is a string with the base64 stored PasswordHash
. Here’s how we can get the byte[]
we need from it:
var identityV3HashArray = Convert.FromBase64String(passwordHash);
Next step is to get the KeyDerviationPrf
enumeration value. To do that we need to convert for consecutive bytes from position 1 to position 4 in the identityV3HashArray array. They make up the uint we saved in there previously. Remember that the bytes are in reverse order, so we need to reverse them back to their original order. We’ll use BitConverter.ToUInt32
method that expects a byte[]
and a startIndex
in that array from which to read the 4 bytes that make up the uint. Let’s put all that in a method called ConvertFromNetworOrder
:
private static uint ConvertFromNetworOrder(byte[] reversedUint)
{
return BitConverter.ToUInt32(reversedUint.Reverse().ToArray(), 0);
}
The reason for the NetworkOrder
in the name of the method is that it describes the order of the bytes in the byte array. The most significant is comes first. Another way to refer to this way of ordering bits, is big endian.
We can now use out ConvertFromNetworOrder
to fetch the PRF and the other uint
s that are stored in identityV3HashArray
:
var prfAsArray = new byte[4];
Buffer.BlockCopy(identityV3HashArray, 1, prfAsArray, 0, 4);
var prf = (KeyDerivationPrf)ConvertFromNetworOrder(prfAsArray);
Now, the iteration count:
var iterationCountAsArray = new byte[4];
Buffer.BlockCopy(identityV3HashArray, 5, iterationCountAsArray, 0, 4);
var iterationCount = (int)ConvertFromNetworOrder(iterationCountAsArray);
Next is the salt size. Even though it’s always 16 if you use ASP.NET Identity, when verifying a password the salt size can be more than 16. It’s only for generating PasswordHash
that ASP.NET Identity does not have an option for specifying a different size for the salt. You can see this in the VerifyHashedPasswordV3
method in PasswordHasher class.
var saltSizeAsArray = new byte[4];
Buffer.BlockCopy(identityV3HashArray, 9, saltSizeAsArray, 0, 4);
var saltSize = (int)ConvertFromNetworOrder(saltSizeAsArray); //int because Buffer.BlockCopy expects an int in the offset parameter
The actual salt:
var salt = new byte[saltSize];
Buffer.BlockCopy(identityV3HashArray, 13, salt, 0, saltSize);
And finally the saved PBKDF2 hash:
var savedHashedPassword = new byte[identityV3HashArray.Length - 1 - 4 - 4 - 4 - saltSize];
Buffer.BlockCopy(identityV3HashArray, 13 + saltSize, savedHashedPassword, 0, savedHashedPassword.Length);
The only thing we need to do now is to run PBKFD2 with the saved PRF function, iteration count and salt:
var hashFromInputPassword = KeyDerivation.Pbkdf2(password, salt, prf, iterationCount, 32);
We can now compare the saved PBKDF2 hash with the one we just generated from the input password. The proper way to do that is to do it in a way that does not disclose any information about how different the two arrays are. If you compare position for position and stop as soon as they different, an attacker can time the time it takes for a password to fail, and adjust the attempts accordingly.
If this sounds too convoluted, here’s a simpler example where the time it takes to perform an operation discloses information. Imagine a website where if someone enters an invalid username the password isn’t even tested.
Even if the message that gets sent back is “Invalid username/password combination”, which does not disclose that the username is invalid, if an attacker times the responses he can validate that a username is a valid just by the fact that the message “Invalid username/password combination” takes longer to come back as a response when the username exists.
It is therefore a good idea to make these checks take the same amount of time. In the case of comparing arrays make sure that every position is tested every time, even if such isn’t necessary to decide that the arrays are different.
private static bool AreByteArraysEqual(byte[] array1, byte[] array2)
{
if (array1.Length != array2.Length) return false;
var areEqual = true;
for (var i = 0; i < array1.Length; i++)
{
areEqual &= (array1[i] == array2[i]);
}
return areEqual;
}
Finally, the password is valid if AreByteArraysEqual(hashFromInputPassword, savedHashedPassword)
returns true.
Sample project
Here’s a sample project where you can generate PasswordHash
for ASP.NET Identity V3. It’s console application. Here’s an example of how it can be used:
$ ./IdentityV3Hasher hash cutecats
AQAAAAEAACcQAAAAEFWLthQDW2xiWaS3vLgY4ItJdModbW0kzKtb8IVuXBY3fFaIntkbbdqTj8mTXH4mmA==
$ ./IdentityV3Hasher hash "this is a long password"
AQAAAAEAACcQAAAAEDz3Wuf1QjDt14gWSdya6u5D6X8sBqbNJdNjeqGJBO52AIp3RYKXeBzDiPfeL1LPkQ==
And to verify a password/PasswordHash:
$ ./IdentityV3PasswordHasher verify cutecats AQAAAAEAACcQAAAAEFWLthQDW2xiWaS3vLgY4ItJdModbW0kzKtb8IVuXBY3fFaIntkbbdqTj8mTXH4mmA==
Password is correct
$ ./IdentityV3PasswordHasher verify cutecatZ AQAAAAEAACcQAAAAEFWLthQDW2xiWaS3vLgY4ItJdModbW0kzKtb8IVuXBY3fFaIntkbbdqTj8mTXH4mmA==
Wrong password
You can get the project in github here:
$ git clone https://github.com/ruidfigueiredo/IdentityV3PasswordHasher