Part - 1 - How - To - Store - Password - in - Hash - Format - in - ASP - NET - Core - Web
Part - 1 - How - To - Store - Password - in - Hash - Format - in - ASP - NET - Core - Web
Define UserDTO
Next, create another class file named UserDTO.cs within the Models folder and then copy and paste
the following code. This will be our Data Transfer Object which include the required properties which
are required for user Registration and Login functionalities. The action method of our controller will
use this model to get the data from the client.
namespace PasswordHashingDemo.Models
{
public class UserDTO
{
public string UserName { get; set; }
public string Password { get; set; }
}
}
Create Db Context:
Define a context class that inherits from DbContext. So, add a class file named UserDbContext.cs
within the Models folder and then copy and paste the following code. Here, you can see we are using
DbSet<User> as a property for which the entity framework is going to create a database table named
Users which will mapped to the User entity.
using Microsoft.EntityFrameworkCore;
namespace PasswordHashingDemo.Models
{
public class UserDbContext : DbContext
{
public UserDbContext(DbContextOptions<UserDbContext> options) : base(options) { }
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
// This will use the property names as defined in the C# model
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
options.UseSqlServer(builder.Configuration.GetConnectionString("EFCoreDBConnection"));
});
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
}
Database Migration
Next, we need to generate the Migration and update the database schema. So, open Package
Manager Console and Execute the add-migration and update-database commands as follows. You
can give any name to your migration. Here, I am giving Mig1. The name that you are giving it should
not be given earlier.
With this our Database with Users database table is created as shown in the below image:
Implement Password Hashing
For hashing passwords, we can use either HMACSHA256 or HMACSHA512 algorithms. We will
create a utility class to handle password hashing. So, create a class file named PasswordHasher.cs
and then copy and paste the following code:
namespace PasswordHashingDemo.Models
{
public static class PasswordHasher
{
public static void CreatePasswordHash(string password, out byte[] passwordHash, out
byte[] passwordSalt)
{
//var hmac = new System.Security.Cryptography.HMACSHA256()
using (var hmac = new System.Security.Cryptography.HMACSHA512())
{
passwordSalt = hmac.Key; // The Key property provides a randomly generated salt.
passwordHash =
hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
}
}
CreatePasswordHash
This method is used to create a hash and a salt for a given password. This method takes the following
three parameters:
string password: The plain text password input by the user.
out byte[] passwordHash: An output parameter to store the computed hash of the
password.
out byte[] passwordSalt: An output parameter to store the salt used during the hashing
process.
How it works:
Initialization of HMACSHA512: An instance of HMACSHA512 is created. This class is
automatically provided with a randomly generated key (used as salt) when instantiated.
Storing the Salt: The randomly generated key (salt) is stored in the passwordSalt output
parameter.
Computing the Hash: The password is converted into bytes using UTF-8 encoding standard,
and then it is hashed using the HMACSHA512 instance. The resulting hash is stored in the
passwordHash output parameter.
VerifyPasswordHash
This method is used to verify a password against a stored hash and salt. This method takes the
following three parameters:
string password: The plain text password to verify.
byte[] storedHash: The hash stored in the database (or another secure storage) that was
previously computed from the password.
byte[] storedSalt: The salt stored along with the hash used during the initial hashing
process.
How it Works:
Initialization of HMACSHA512: An instance of HMACSHA512 is created using the
storedSalt. The salt helps in recreating the same hash from the input password if it is the
same as the original.
Computing the Hash: The password is again converted into bytes using UTF-8 encoding
and hashed with the initialized HMACSHA512 instance.
Comparison: The newly computed hash (computedHash) is compared byte-by-byte with the
storedHash using the SequenceEqual method from Linq, which ensures that both sequences
are identical.
Return Value: The method returns true if the password matches the original (i.e., if
computedHash equals storedHash); otherwise, it returns false.
namespace PasswordHashingDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly UserDbContext _context;
[HttpPost("register")]
public async Task<ActionResult<User>> Register(UserDTO model)
{
byte[] passwordHash, passwordSalt;
PasswordHasher.CreatePasswordHash(model.Password, out passwordHash, out
passwordSalt);
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
[HttpPost("login")]
public async Task<ActionResult<string>> Login(UserDTO model)
{
var user = await _context.Users.FirstOrDefaultAsync(x => x.Username ==
model.UserName);
if (user == null || !PasswordHasher.VerifyPasswordHash(model.Password,
user.PasswordHash, user.PasswordSalt))
{
return Unauthorized("Invalid username or password.");
}
As you can see the above Registration action method, it uses the Password Hasher utility to generate
the Password hash and salt and then store the same in the database. In this case, if two users trying
to send the same password, then it is going to create two different password hash. This is possible
because of the dynamic generated salt. On the other hand, the Login action method also uses the
Password Hasher utility to generate and compare the password.
With the above changes in place, run the application and test the functionalities and it should work as
expected. For both the action method, you can use the same request body. For example:
{
"UserName": "Pranaya",
"Password": "Test@1234"
}
Now, create a few users and then verify the database. You can see for each user; it is creating a
different password hash and salt as shown in the below image: