PasswordHasher
An ASP.NET Core implementation for hashing passwords without using TUser and PasswordVerificationResult, which is in the standard implementation. SHA384 and SHA512 hashing algorithms are also supported.
Install / Use
/learn @datarza/PasswordHasherREADME
Did you like how the PasswordHasher was implemented in ASP.NET Core? What is the <TUser> used for in class definition? Why only SHA1 and SHA256 are supported? Why the size of salt is a constant? Is the default ASP.NET Core implementation a good choice?
PasswordHasher in ASP.NET Core could be better implemented
If you are not familiar with the default implementation of PasswordHasher in ASP.NET Core, I would like to recommend an excellent article which explains everything.
Some ideas for better implementation of PasswordHasher:
- Setting only the hashing algorithm name should be enough for working with the PasswordHasher
- The salt size and iterations count can be defined as parameters by user or can be counted as default values related to the hashing algorithms
- SHA1 is good for runtime performance, SHA256 is enough for security storage, SHA384 and SHA512 hashing algorithms should be also implemented
- The result should not contain any hashing parameters, which means: do no not keep salt size, iterations count or hashing algorithm name in the hash
- The result should be the <code>string</code> that can be stored in the database with predictable length
- Internal <code>byte[]</code> comparator for verifing passwords should be a microservice
Implementation
The default password hasher for ASP.NET Core Identity uses <code>PBKDF2</code> for password hashing that is not support all hashing algorithms. The Rfc2898DeriveBytes class from the <code>System.Security.Cryptography</code> namespace supports all that we need to get the result we wanted. This class can generate pseudo-randomized salt and supports all SHA hashing algorithms.
Method for hashing passwords
public string HashPassword(string password)
{
byte[] saltBuffer;
byte[] hashBuffer;
using (var keyDerivation = new Rfc2898DeriveBytes(password, options.SaltSize, options.Iterations, options.HashAlgorithmName))
{
saltBuffer = keyDerivation.Salt;
hashBuffer = keyDerivation.GetBytes(options.HashSize);
}
byte[] result = new byte[options.HashSize + options.SaltSize];
Buffer.BlockCopy(hashBuffer, 0, result, 0, options.HashSize);
Buffer.BlockCopy(saltBuffer, 0, result, options.HashSize, options.SaltSize);
return Convert.ToBase64String(result);
}
Method for verifing the hash and passwords
public bool VerifyHashedPassword(string hashedPassword, string providedPassword)
{
byte[] hashedPasswordBytes = Convert.FromBase64String(hashedPassword);
if (hashedPasswordBytes.Length != options.HashSize + options.SaltSize)
{
return false;
}
byte[] hashBytes = new byte[options.HashSize];
Buffer.BlockCopy(hashedPasswordBytes, 0, hashBytes, 0, options.HashSize);
byte[] saltBytes = new byte[options.SaltSize];
Buffer.BlockCopy(hashedPasswordBytes, options.HashSize, saltBytes, 0, options.SaltSize);
byte[] providedHashBytes;
using (var keyDerivation = new Rfc2898DeriveBytes(providedPassword, saltBytes, options.Iterations, options.HashAlgorithmName))
{
providedHashBytes = keyDerivation.GetBytes(options.HashSize);
}
return comparer.Equals(hashBytes, providedHashBytes);
}
Setting up
The parameters for PasswordHasher can be specified in <code>Startup.cs</code> via Options pattern. Also, in <code>Startup.cs</code> can be registered the PasswordHasher as a microservice.
public void ConfigureServices(IServiceCollection services)
{
// Configuring PasswordHasher
services.Configure<PasswordHasherOptions>(options =>
{
options.HashAlgorithm = PasswordHasherAlgorithms.SHA1;
options.SaltSize = 16;
options.Iterations = 8192;
});
// Registering PasswordHasher
services.AddPasswordHasher();
services.AddMvc();
}
Usage example
public class IndexModel : PageModel
{
private readonly IPasswordHasher hasher;
public IndexModel(IPasswordHasher hasher)
{
this.hasher = hasher;
}
public void OnGet()
{
var password = "my password";
var passwordHash = hasher.HashPassword(password);
var passwordCheck = hasher.VerifyHashedPassword(passwordHash, password);
}
}
Support or Contact
Having questions? Contact me and I will help you sort it out.
<style>.inner { min-width: 800px !important; max-width: 60% !important;}</style>