Publish from private repository
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
namespace MyOffice.Web.Identity.Repositories;
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using Domain;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Identity V3 hasher for new passwords; still verifies the legacy
|
||||
/// (16-byte salt + 20-byte PBKDF2-SHA1 @ 100k) format and signals rehash.
|
||||
/// </summary>
|
||||
public class PasswordHasher : IPasswordHasher<ApplicationUser<Guid>>
|
||||
{
|
||||
private const int LegacySaltSize = 16;
|
||||
private const int LegacyHashSize = 20;
|
||||
private const int LegacyIterations = 100_000;
|
||||
private const int LegacyPayloadSize = LegacySaltSize + LegacyHashSize;
|
||||
|
||||
private readonly PasswordHasher<ApplicationUser<Guid>> _identityHasher = new();
|
||||
|
||||
public string HashPassword(ApplicationUser<Guid> user, string password) =>
|
||||
_identityHasher.HashPassword(user, password);
|
||||
|
||||
public PasswordVerificationResult VerifyHashedPassword(
|
||||
ApplicationUser<Guid> user,
|
||||
string hashedPassword,
|
||||
string providedPassword
|
||||
)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hashedPassword) || providedPassword == null)
|
||||
{
|
||||
return PasswordVerificationResult.Failed;
|
||||
}
|
||||
|
||||
// Prefer modern Identity format when payload is not the legacy 36-byte blob.
|
||||
if (!IsLegacyPayload(hashedPassword))
|
||||
{
|
||||
return _identityHasher.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||
}
|
||||
|
||||
if (VerifyLegacyHash(providedPassword, hashedPassword))
|
||||
{
|
||||
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||
}
|
||||
|
||||
return PasswordVerificationResult.Failed;
|
||||
}
|
||||
|
||||
private static bool IsLegacyPayload(string hashedPassword)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bytes = Convert.FromBase64String(hashedPassword);
|
||||
return bytes.Length == LegacyPayloadSize;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string HashLegacyForTests(string password)
|
||||
{
|
||||
var salt = RandomNumberGenerator.GetBytes(LegacySaltSize);
|
||||
var hash = Rfc2898DeriveBytes.Pbkdf2(
|
||||
password,
|
||||
salt,
|
||||
LegacyIterations,
|
||||
HashAlgorithmName.SHA1,
|
||||
LegacyHashSize);
|
||||
|
||||
var payload = new byte[LegacyPayloadSize];
|
||||
Buffer.BlockCopy(salt, 0, payload, 0, LegacySaltSize);
|
||||
Buffer.BlockCopy(hash, 0, payload, LegacySaltSize, LegacyHashSize);
|
||||
return Convert.ToBase64String(payload);
|
||||
}
|
||||
|
||||
private static bool VerifyLegacyHash(string password, string passwordHash)
|
||||
{
|
||||
byte[] hashBytes;
|
||||
try
|
||||
{
|
||||
hashBytes = Convert.FromBase64String(passwordHash);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hashBytes.Length != LegacyPayloadSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var salt = hashBytes.AsSpan(0, LegacySaltSize);
|
||||
var expected = hashBytes.AsSpan(LegacySaltSize, LegacyHashSize);
|
||||
var actual = Rfc2898DeriveBytes.Pbkdf2(
|
||||
password,
|
||||
salt,
|
||||
LegacyIterations,
|
||||
HashAlgorithmName.SHA1,
|
||||
LegacyHashSize);
|
||||
|
||||
return CryptographicOperations.FixedTimeEquals(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
public class AppUserManager : UserManager<ApplicationUser<Guid>>
|
||||
{
|
||||
public AppUserManager(
|
||||
IUserStore<ApplicationUser<Guid>> store,
|
||||
IOptions<IdentityOptions> optionsAccessor,
|
||||
IPasswordHasher<ApplicationUser<Guid>> passwordHasher,
|
||||
IEnumerable<IUserValidator<ApplicationUser<Guid>>> userValidators,
|
||||
IEnumerable<IPasswordValidator<ApplicationUser<Guid>>> passwordValidators,
|
||||
ILookupNormalizer keyNormalizer,
|
||||
IdentityErrorDescriber errors,
|
||||
IServiceProvider services,
|
||||
ILogger<UserManager<ApplicationUser<Guid>>> logger) :
|
||||
base(
|
||||
store,
|
||||
optionsAccessor,
|
||||
passwordHasher,
|
||||
userValidators,
|
||||
passwordValidators,
|
||||
keyNormalizer,
|
||||
errors,
|
||||
services,
|
||||
logger
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user