78 lines
2.1 KiB
C#
78 lines
2.1 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Security.Cryptography;
|
|
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
|
|
|
namespace MaksIT.Core.Security;
|
|
|
|
public static class PasswordHasher {
|
|
private static byte[] CreateSaltBytes() {
|
|
byte[] randomBytes = new byte[16];
|
|
using (var generator = RandomNumberGenerator.Create()) {
|
|
generator.GetBytes(randomBytes);
|
|
}
|
|
return randomBytes;
|
|
}
|
|
|
|
private static string CreateHash(string value, byte[] saltBytes, string pepper) {
|
|
// Combine password and pepper
|
|
var valueWithPepper = value + pepper;
|
|
var valueBytes = KeyDerivation.Pbkdf2(
|
|
password: valueWithPepper,
|
|
salt: saltBytes,
|
|
prf: KeyDerivationPrf.HMACSHA512,
|
|
iterationCount: 100_000, // Increased iteration count
|
|
numBytesRequested: 256 / 8);
|
|
|
|
return Convert.ToBase64String(valueBytes);
|
|
}
|
|
|
|
public static bool TryCreateSaltedHash(
|
|
string value,
|
|
string pepper,
|
|
[NotNullWhen(true)] out (string Salt, string Hash)? saltedHash,
|
|
[NotNullWhen(false)] out string? errorMessage
|
|
) {
|
|
try {
|
|
var saltBytes = CreateSaltBytes();
|
|
var hash = CreateHash(value, saltBytes, pepper);
|
|
var salt = Convert.ToBase64String(saltBytes);
|
|
|
|
saltedHash = (salt, hash);
|
|
errorMessage = null;
|
|
return true;
|
|
}
|
|
catch (Exception ex) {
|
|
saltedHash = null;
|
|
errorMessage = ex.Message;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool TryValidateHash(
|
|
string value,
|
|
string salt,
|
|
string hash,
|
|
string pepper,
|
|
[NotNullWhen(true)] out bool isValid,
|
|
[NotNullWhen(false)] out string? errorMessage
|
|
) {
|
|
try {
|
|
var saltBytes = Convert.FromBase64String(salt);
|
|
var hashToCompare = CreateHash(value, saltBytes, pepper);
|
|
|
|
isValid = CryptographicOperations.FixedTimeEquals(
|
|
Convert.FromBase64String(hashToCompare),
|
|
Convert.FromBase64String(hash)
|
|
);
|
|
|
|
errorMessage = null;
|
|
return true;
|
|
}
|
|
catch (Exception ex) {
|
|
isValid = false;
|
|
errorMessage = ex.Message;
|
|
return false;
|
|
}
|
|
}
|
|
}
|