Asp.Net Identity'S Default Password Hasher - How Does It Work and Is It Secure

ASP.NET Identity's default Password Hasher - How does it work and is it secure?

Here is how the default implementation (ASP.NET Framework or ASP.NET Core) works. It uses a Key Derivation Function with random salt to produce the hash. The salt is included as part of the output of the KDF. Thus, each time you "hash" the same password you will get different hashes. To verify the hash the output is split back to the salt and the rest, and the KDF is run again on the password with the specified salt. If the result matches to the rest of the initial output the hash is verified.

Hashing:

public static string HashPassword(string password)
{
byte[] salt;
byte[] buffer2;
if (password == null)
{
throw new ArgumentNullException("password");
}
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, 0x10, 0x3e8))
{
salt = bytes.Salt;
buffer2 = bytes.GetBytes(0x20);
}
byte[] dst = new byte[0x31];
Buffer.BlockCopy(salt, 0, dst, 1, 0x10);
Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20);
return Convert.ToBase64String(dst);
}

Verifying:

public static bool VerifyHashedPassword(string hashedPassword, string password)
{
byte[] buffer4;
if (hashedPassword == null)
{
return false;
}
if (password == null)
{
throw new ArgumentNullException("password");
}
byte[] src = Convert.FromBase64String(hashedPassword);
if ((src.Length != 0x31) || (src[0] != 0))
{
return false;
}
byte[] dst = new byte[0x10];
Buffer.BlockCopy(src, 1, dst, 0, 0x10);
byte[] buffer3 = new byte[0x20];
Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, dst, 0x3e8))
{
buffer4 = bytes.GetBytes(0x20);
}
return ByteArraysEqual(buffer3, buffer4);
}

Asp.net Identity password hashing


"Is it possible to use password salting for more secure encryption in
ASP.net Identity and via this interface?"

Yes, the interface is provided for the new implementation of PasswordHasher already present in Core framework.

Also note that the default implementation is already using Salt+Bytes.

After creating custom PasswordHasher (say MyPasswordHasher), you can assign it to UserManager instance like userManager.PasswordHasher=new MyPasswordHasher()

See one example of such IPasswordHasher

To implement a custom system using the interfaces (instead of using the standard Entity Framework implementation included in the MVC 5 template) an IPasswordHasher is required.

For implementing alternate system from EF,
- You shall implement all Core interfaces.
- IPasswordHasher implementation is not required. PasswordHasher is already provided in Core framework as it's implementation.

How to manually hash a password using Asp.Net Core 2.2 / IdentityServer4 / SP.NET Core Identity


If a user signs in, I will have it automatically enter the default password for them.

If you are using .net core Identity , you can use UserManager.CreateAsync to create the specified user in the backing store with given password :

public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult> CreateAsync (TUser user, string password);

Code below is for your reference :

var user = new ApplicationUser { UserName = "wx2@hotmail.com", Email = "wx2@hotmail.com" };
var result = await _userManager.CreateAsync(user, "YourPassWord");
if (result.Succeeded)
{

}

The Identity system will help create the password hash and store in the database . If you still need to manually hash the password , see IPasswordHasher interface .


Edit :

If you want to directly insert/update via database context , you should set correct NormalizedUserName and SecurityStamp to make the system work :

ApplicationUser applicationUser = new ApplicationUser();
Guid guid = Guid.NewGuid();
applicationUser.Id = guid.ToString();
applicationUser.UserName = "wx@hotmail.com";
applicationUser.Email = "wx@hotmail.com";
applicationUser.NormalizedUserName = "wx@hotmail.com";

_context.Users.Add(applicationUser);


var hasedPassword = _passwordHasher.HashPassword(applicationUser, "YourPassword");
applicationUser.SecurityStamp = Guid.NewGuid().ToString();
applicationUser.PasswordHash = hasedPassword;

_context.SaveChanges();

Is Asp.net identity hashing Secured?

Yes, ASP.NET's password hashing method is secure.

In the example you provided, the user is using an encryption technique known as Advanced Encryption Standard (AES, also known as Rijndael). This is why the secret can be decrypted.

The user only uses the Rfc2898DeriveBytes class in order to get a key and an initialisation vector.

The class is not used to hash the secret message. The encryption is what hides the message.

ASP.NET uses the Rfc2898DeriveBytes class to hash a password. This procedure cannot be reversed.

Why do identity framework core hashed passwords all start the same?

According to implementation, Microsoft.AspNetCore.Identity generates a result value using the following format (actual for version 3):


HEADER

A) byte #0: format version (0x00 for version 2, 0x01 for version 3)

B) byte #1-4: int (4 bytes), KeyDerivationPrf Enum - key derivation algorithm. HMACSHA256 in V3, HMACSHA512 since .NET7

C) byte #5-8: int (4 bytes), iterations count. 10000 in V3, 100k since .NET7

D) byte #9-12: int (4 bytes), salt length. 16 in V3


BODY

E) byte #13-28 - salt. (length takes from Header D)

F) byte #29-60 - hash (HMACSHA256 at the Header B says it has 256/8 = 32 bytes length)


Result byte array stores in BASE64 format


So obviously, since header is the same for all V3 hashes, all V3 hashes will have the same characters at the beginning (first 13 of 61 bytes).

Can someone explain to me that the algorithm of the password hash that use in ASP.NET CORE 6 MVC (Identity Entity Framework Core Ver 6.0.8)?

user207421 is absolutely correct: "Encrypting" plaintext (e.g. a password), and generating a password hash are two completely different things.

I'm not sure what you actually wish to accomplish, or why. But besides the Microsoft documentation, you might find these two links useful:

  • https://andrewlock.net/exploring-the-asp-net-core-identity-passwordhasher/
  • https://andrewlock.net/migrating-passwords-in-asp-net-core-identity-with-a-custom-passwordhasher/

Per the first link:

  • ASP.NET Identity Version 2: PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations
  • ASP.NET Core Identity Version 3: PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations

A wise caveat from the second article:

You should always think carefully before replacing security-related
components, as a lot of effort goes into making the default components
secure by default. This article solves a specific problem, but you
should only use it if you need it!


How to Change AspNet Identity Password Hash to AspNet.Core Identity Password Hash

You cannot simply convert the hash value from one password hasher to another unless they ofcourse use the exact same hashing mechanism (in which case there would be no need for conversion in the first place).

I have encountered this scenario in few projects where we changed the password hashing mechanism and effectively, we had to send everyone password reset emails. In another project, it was not feasible so basically we forced the user to change their password upon first login (and we used the old hasher to validate their current password) and the new passwords would have been then hashed using the new hasher.

How can i manually hash password just like asp.net identity does

I found the answer here

public static string HashPassword(string password)
{
byte[] salt;
byte[] buffer2;
if (password == null)
{
throw new ArgumentNullException("password");
}
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, 0x10, 0x3e8))
{
salt = bytes.Salt;
buffer2 = bytes.GetBytes(0x20);
}
byte[] dst = new byte[0x31];
Buffer.BlockCopy(salt, 0, dst, 1, 0x10);
Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20);
return Convert.ToBase64String(dst);
}


Related Topics



Leave a reply



Submit