eeb6174de0
- ASP.NET Core 10 Backend (JWT Auth, Agent config API) - Vue 3 Frontend (Dashboard, Team, Agents, Config Editor) - PostgreSQL Database - Docker Compose setup - Mission Control Dashboard redesign
68 lines
2.2 KiB
C#
68 lines
2.2 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace Nexus.Api.Services;
|
|
|
|
public static class PasswordSecurity
|
|
{
|
|
private const int Iterations = 210_000;
|
|
private const int SaltSize = 16;
|
|
private const int HashSize = 32;
|
|
private const string Version = "v1";
|
|
|
|
public static string Hash(string password)
|
|
{
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(password);
|
|
|
|
var salt = RandomNumberGenerator.GetBytes(SaltSize);
|
|
var hash = Rfc2898DeriveBytes.Pbkdf2(
|
|
password,
|
|
salt,
|
|
Iterations,
|
|
HashAlgorithmName.SHA256,
|
|
HashSize);
|
|
|
|
return string.Join('.', Version, Iterations, Convert.ToBase64String(salt), Convert.ToBase64String(hash));
|
|
}
|
|
|
|
public static bool Verify(string password, string encodedHash, out bool needsUpgrade)
|
|
{
|
|
needsUpgrade = false;
|
|
if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(encodedHash)) return false;
|
|
|
|
var parts = encodedHash.Split('.');
|
|
if (parts.Length == 4 && parts[0] == Version && int.TryParse(parts[1], out var iterations))
|
|
{
|
|
try
|
|
{
|
|
var salt = Convert.FromBase64String(parts[2]);
|
|
var expected = Convert.FromBase64String(parts[3]);
|
|
var actual = Rfc2898DeriveBytes.Pbkdf2(
|
|
password,
|
|
salt,
|
|
iterations,
|
|
HashAlgorithmName.SHA256,
|
|
expected.Length);
|
|
|
|
needsUpgrade = iterations < Iterations;
|
|
return CryptographicOperations.FixedTimeEquals(actual, expected);
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (encodedHash.Length == 64 && encodedHash.All(Uri.IsHexDigit))
|
|
{
|
|
var legacy = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(password)));
|
|
needsUpgrade = true;
|
|
return CryptographicOperations.FixedTimeEquals(
|
|
Encoding.ASCII.GetBytes(legacy),
|
|
Encoding.ASCII.GetBytes(encodedHash.ToUpperInvariant()));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|