Almost every software has to deal with some kind of user/identity management – be it desktop applications that usually should integrate with the operating system’s and/or business environment’s identity and authentication concepts or be it cloud based/mobile applications or web services that need to be able to control access on their own behalf.
The issue will even become more important in the near future, because the number of mobile users grows at a high rate, and at the same time we can read almost every day about stolen passwords and hacked websites. Also, the activities of Intelligence services around the world don’t really make one feel more secure…
So, with mobile application development and cross-platform compatibility in mind, I fired up my favorite search engine and was looking for a password hashing implementation that met the following requirements:
- PCL – compatible
- Easy to use
- And, of course, being cryptographically as secure as possible
To my surprise, I found nothing that met all the above requirements and was usable out of the box. What I did find were various different components, that had to be re-combined into a new class (called PasswordHash
).
Generally, a lot can be found on the web about cryptography and password security. I especially liked this article, as it is very concise and straight to the point, and at the same time gives a good explanation about how passwords can be hacked – and therefore, to which aspects one should pay extra attention. I strongly suggest you this article when you’re interested in some basic knowledge about password security. In this post, I won’t go into details about these more general aspects, but focus on more .NET-specific issues and on basic usage of the PasswordHash
class.
Creating a Hash
To create a strong hash from a password, you need to perform the following steps:
- Generate a long random salt using a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG).
- Append the salt to the password (‘salt the password’) and hash this combination with a hash function.
- Save both the salt and the hash in your data store along with the username.
The API that I had in mind was as simple as this:
1 |
public static string CreateHash(string password) |
But when I tried out the C# sample code that was accompanying the aforementioned article, it didn’t compile into a .NET Portable Class Library, because the namespace System.Security.Cryptography
is not supported in the .NET portable subset. So I needed to rewrite the code to make it PCL-compatible…
CSPRNG with Bouncy Castle
The first thing needed is a CSPRNG generator for PCLs. In standard .NET, you would use System.Security.Cryptography.RNGCryptoServiceProvider
, but this is one of the pieces that are not available in .NET Portable Class Libraries. Here, Bouncy Castle comes to the rescue. Bouncy Castle is an OSS project that maintains a library which provides APIs all around cryptography. And best of all, there is a NuGet package for PCLs (called BouncyCastle-PCL) that we can use as a replacement for the missing standard .NET pieces. This is a possible implementation of a CSPRNG generation method using this package:
1 2 3 4 5 6 7 8 9 |
internal static byte[] CreateRandomSalt() { Random random = SecureRandom.GetInstance("SHA256PRNG", true); var salt = new byte[SaltByteSize]; random.NextBytes(salt); return salt; } |
Generating the Hash
With this salt, the next thing would be to create a hash from the [password]:[salt]
combination that we currently have. To do this, we use an implementation of the PBKDF2 algorithm, which is a standardized key derivation function. I found a Bouncy Castle implementation of this algorithm in the answer to this Stackoverflow question.
With the Pbkdf2
class in place, our hash creation function is as simple as this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/// <summary> /// Creates a salted Pbkdf2 hash of the password. /// </summary> /// <param name="password">The password to hash.</param> /// <returns>The hash of the password.</returns> public static string CreateHash(string password) { lock (LockObject) { var salt = CreateRandomSalt(); // Hash the password and encode the parameters byte[] hash = new Pbkdf2().GenerateDerivedKey(HashByteSize, Encoding.UTF8.GetBytes(password), salt, Pbkdf2Iterations); return Pbkdf2Iterations.ToString() + Delimiter + Convert.ToBase64String(salt) + Delimiter + Convert.ToBase64String(hash); } } |
The return value from this method is in the format
1 |
<#iterations>:<salt>:<hash> |
and looks something like this:
1 |
750:XDiHo0tyhXUqiJa8WdvrK5+p73s9H10ARd4yTPUO7Mg=:Bo5iAvVesPhfj27iRBP/nrSt3rlnDu/UElxQ7iT3qxkhijrs1AYX3UyXSfyANIFcvZ6yWQfE9l/4wekQyNqwnL8t4oao9ZDUP4i3CSsFMWt8Ewu5HuGSkUBL+X3es0EjMDDj9ii2LoGc2bDKVDXmqSkS00dw2KWh25QcFOaf38k= |
This is the final string that we store together with the user’s name. It is variable along three dimensions:
- Length (in bytes) of the salt
- Length (in bytes) of the hash
- The number of iterations, i.e. how often the
PBKDF2
key derivation algorithm was repeatedly applied
These three dimensions are reflected through respective properties in the PasswordHash
class.
Validating a Hash
The other operation that our PasswordHash
class needs to support is hash validation. We need to be able to answer the question: Was the given hash generated from the specified password? So the signature of the other public operation of the PasswordHash
class would be:
1 |
public static bool ValidatePassword(string password, string correctHash) |
Validating a password/hash pair involves the following steps:
- Retrieve the user’s hash and salt from the data store
- Append the salt to the given password and hash it with the same hash function that was used to originally create the hash.
- Compare this test hash to the one that was retrieved from the data store. If they match, the password is correct.
Following these steps, the complete implementation of the method is this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/// <summary> /// Validates a password given a hash of the correct one. /// </summary> /// <param name="password">The password to check.</param> /// <param name="correctHash">A hash of the correct password.</param> /// <returns>True if the password is correct. False otherwise.</returns> public static bool ValidatePassword(string password, string correctHash) { lock (LockObject) { // Extract the parameters from the hash string[] hashParts = correctHash.Split(Delimiter); int iterations = Int32.Parse(hashParts[IterationIndex]); byte[] salt = Convert.FromBase64String(hashParts[SaltIndex]); byte[] hash = Convert.FromBase64String(hashParts[Pbkdf2Index]); byte[] testHash = new Pbkdf2().GenerateDerivedKey(hash.Length, Encoding.UTF8.GetBytes(password), salt, iterations); return SlowEquals(hash, testHash); } } |
(SlowEquals()
is a special time-constant comparison function that participates in making the password hash more secure by hardening the validation algorithm against timing-based attacks.)
Points of Interest
There are mainly to aspects to the code presented here: (1) The hashes are completely random and (2) their creation parameters can vary.
Never use the same hash twice (except for internal validation)
One important aspect is, that the generated hashes are completely random. In other words: The CreateHash()
function never generates the same hash twice for a given password. The following test (using xUnit.Net) exemplifies this:
1 2 3 4 5 6 7 8 9 10 11 12 |
[Theory] [InlineData("test")] [InlineData("B4y^KYsa")] public void CanValidatePasswordAgainstDifferntHashes(string password) { string hash1 = PasswordHash.CreateHash(password); string hash2 = PasswordHash.CreateHash(password); Assert.NotEqual(hash1, hash2); Assert.True(PasswordHash.ValidatePassword(password, hash1)); Assert.True(PasswordHash.ValidatePassword(password, hash2)); } |
Password/Hash Validation is Agnostic to Varying Hash Creation Parameters
Hash creation can vary along the dimensions salt-length, hash-length, and number of iterations. However, such a change does not affect password validation in any way, as long as the same algorithm is used for both hash creation and validation. This is demonstrated by the below test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[Fact] public void CanValidateAnyHashWithAnySettings() { const string password = "blah123XY"; // set hash creation values, then create hash PasswordHash.SaltByteSize = 75; PasswordHash.HashByteSize = 48; PasswordHash.Pbkdf2Iterations = 1500; string hash = PasswordHash.CreateHash(password); // now set different values PasswordHash.SaltByteSize = 122; PasswordHash.HashByteSize = 99; PasswordHash.Pbkdf2Iterations = 500; // assert that the hash validates anyway Assert.True(PasswordHash.ValidatePassword(password, hash)); } |
The sample solution
A small sample solution (VS 2013) containing the PasswordHash
class and the above client code can be found here on GitHub. Note that the unit tests thereinare not meant to prove anything in a strict sense. Rather, they serve to demonstrate the usage and to reasonably verify that the code does what it should do.