Last active
September 11, 2021 11:17
-
-
Save edamtoft/86fce86a44d3d88e30f528fdd266c461 to your computer and use it in GitHub Desktop.
Password Hashing in .NET Core - Full Implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public sealed class PasswordHasher : IPasswordHasher | |
{ | |
private const int SaltSize = 16; // 128 bit | |
private const int KeySize = 32; // 256 bit | |
public PasswordHasher(IOptions<HashingOptions> options) | |
{ | |
Options = options.Value; | |
} | |
private HashingOptions Options { get; } | |
public string Hash(string password) | |
{ | |
using (var algorithm = new Rfc2898DeriveBytes( | |
password, | |
SaltSize, | |
Options.Iterations, | |
HashAlgorithmName.SHA512)) | |
{ | |
var key = Convert.ToBase64String(algorithm.GetBytes(KeySize)); | |
var salt = Convert.ToBase64String(algorithm.Salt); | |
return $"{Options.Iterations}.{salt}.{key}"; | |
} | |
} | |
public (bool Verified, bool NeedsUpgrade) Check(string hash, string password) | |
{ | |
var parts = hash.Split('.', 3); | |
if (parts.Length != 3) | |
{ | |
throw new FormatException("Unexpected hash format. " + | |
"Should be formatted as `{iterations}.{salt}.{hash}`"); | |
} | |
var iterations = Convert.ToInt32(parts[0]); | |
var salt = Convert.FromBase64String(parts[1]); | |
var key = Convert.FromBase64String(parts[2]); | |
var needsUpgrade = iterations != Options.Iterations; | |
using (var algorithm = new Rfc2898DeriveBytes( | |
password, | |
salt, | |
iterations, | |
HashAlgorithmName.SHA512)) | |
{ | |
var keyToCheck = algorithm.GetBytes(KeySize); | |
var verified = keyToCheck.SequenceEqual(key); | |
return (verified, needsUpgrade); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment