.NET 5.0 - Hash and Verify Passwords with BCrypt
Tutorial built with .NET 5.0
Other versions available:
- .NET: .NET 6.0, ASP.NET Core 3.1
- Node: Node.js
This is a quick example of how to hash and verify passwords in .NET 5.0 using the BCrypt.Net-Next
password hashing library which is a C# implementation of the bcrypt
password hashing function.
For more info on the BCrypt.Net-Next password hashing C# library see https://www.nuget.org/packages/BCrypt.Net-Next.
For more info on the underlying bcrypt password hashing function, see https://en.wikipedia.org/wiki/bcrypt.
Installing BCrypt.Net-Next via NuGet
.NET CLI: dotnet add package BCrypt.Net-Next
Visual Studio Package Manager Console: Install-Package BCrypt.Net-Next
Hashing a password in .NET 5.0
This code hashes the password "Pa$$w0rd"
using bcrypt and stores the result in the passwordHash
string variable.
string passwordHash = BCrypt.Net.BCrypt.HashPassword("Pa$$w0rd");
Verify a password against a hash in .NET 5.0
This code verifies the password "Pa$$w0rd"
using bcrypt against the hash stored in the passwordHash
variable.
bool verified = BCrypt.Net.BCrypt.Verify("Pa$$w0rd", passwordHash);
Example usage in a .NET User Service
Below is an example user service with a Register()
method that saves an account with a hashed password and an Authenticate()
method that verifies a provided password against the PasswordHash
of a saved account. The password is hashed on line 57
and verified on line 38
.
The service is a cut down version of the user service from a .NET API tutorial I posted recently, for more info or to download and test the API locally see .NET 5.0 - Simple API for Authentication, Registration and User Management.
using AutoMapper;
using BCryptNet = BCrypt.Net.BCrypt;
using System.Linq;
using WebApi.Authorization;
using WebApi.Entities;
using WebApi.Helpers;
using WebApi.Models.Users;
namespace WebApi.Services
{
public interface IUserService
{
AuthenticateResponse Authenticate(AuthenticateRequest model);
void Register(RegisterRequest model);
}
public class UserService : IUserService
{
private DataContext _context;
private IJwtUtils _jwtUtils;
private readonly IMapper _mapper;
public UserService(
DataContext context,
IJwtUtils jwtUtils,
IMapper mapper)
{
_context = context;
_jwtUtils = jwtUtils;
_mapper = mapper;
}
public AuthenticateResponse Authenticate(AuthenticateRequest model)
{
var user = _context.Users.SingleOrDefault(x => x.Username == model.Username);
// verify password
if (user == null || !BCryptNet.Verify(model.Password, user.PasswordHash))
throw new AppException("Username or password is incorrect");
// authentication successful
var response = _mapper.Map<AuthenticateResponse>(user);
response.JwtToken = _jwtUtils.GenerateToken(user);
return response;
}
public void Register(RegisterRequest model)
{
// validate
if (_context.Users.Any(x => x.Username == model.Username))
throw new AppException("Username '" + model.Username + "' is already taken");
// map model to new user object
var user = _mapper.Map<User>(model);
// hash password
user.PasswordHash = BCryptNet.HashPassword(model.Password);
// save user
_context.Users.Add(user);
_context.SaveChanges();
}
}
}
C# Using Alias Directive
One small thing to note in the above file is the using alias directive on the second line (using BCryptNet = BCrypt.Net.BCrypt;
). This is to avoid having to enter the full path to the class for every call to a BCrypt method (e.g. BCrypt.Net.BCrypt.HashPassword()
) due to the namespace and the class both having the same name (BCrypt
). Another way around this is to move the using statement using BCrypt.Net;
inside the namespace (namespace WebApi.Services
) which would allow you to call BCrypt.HashPassword()
directly. I chose the first approach to keep all of the using statements grouped together at the top of the file.
Need Some .NET Help?
Search fiverr for freelance .NET developers.
Follow me for updates
When I'm not coding...
Me and Tina are on a motorcycle adventure around Australia.
Come along for the ride!