Dreamine.Identity 1.0.0.0
Dreamine.Identity 프로젝트의 API와 구성 요소를 제공합니다.
로딩중...
검색중...
일치하는것 없음
DreaminePasswordHasher.cs
이 파일의 문서화 페이지로 가기
1using System.Security.Cryptography;
2using System.Text;
3
4namespace Dreamine.Identity;
5
14public static class DreaminePasswordHasher
15{
24 private const string Version = "v1";
33 private const int SaltSize = 16;
42 private const int HashSize = 32;
51 private const int CurrentIterations = 600_000;
52
93 public static string HashPassword(string password)
94 {
95 ArgumentException.ThrowIfNullOrWhiteSpace(password);
96
97 var salt = RandomNumberGenerator.GetBytes(SaltSize);
98 var hash = Rfc2898DeriveBytes.Pbkdf2(
99 password,
100 salt,
102 HashAlgorithmName.SHA256,
103 HashSize);
104
105 return string.Join(
106 '.',
107 Version,
108 CurrentIterations.ToString(System.Globalization.CultureInfo.InvariantCulture),
109 Convert.ToBase64String(salt),
110 Convert.ToBase64String(hash));
111 }
112
154 string password,
155 string storedHash,
156 out string? upgradedHash)
157 {
158 upgradedHash = null;
159
160 if (string.IsNullOrEmpty(password) || string.IsNullOrWhiteSpace(storedHash))
161 {
162 return PasswordHashVerificationResult.Failed;
163 }
164
165 var pbkdf2Result = VerifyPbkdf2(password, storedHash, out var needsRehash);
166 if (pbkdf2Result)
167 {
168 if (needsRehash)
169 {
170 upgradedHash = HashPassword(password);
171 return PasswordHashVerificationResult.SuccessRehashNeeded;
172 }
173
174 return PasswordHashVerificationResult.Success;
175 }
176
177 if (VerifyLegacySha256(password, storedHash) || FixedTimeEquals(password, storedHash))
178 {
179 upgradedHash = HashPassword(password);
180 return PasswordHashVerificationResult.SuccessRehashNeeded;
181 }
182
183 return PasswordHashVerificationResult.Failed;
184 }
185
218 public static bool VerifyPassword(string password, string storedHash) =>
219 VerifyPassword(password, storedHash, out _) is not PasswordHashVerificationResult.Failed;
220
245 public static string HashPlainTextForStorage(string passwordOrHash)
246 {
247 if (string.IsNullOrWhiteSpace(passwordOrHash) ||
248 IsDreamineHash(passwordOrHash) ||
249 IsLegacySha256Hash(passwordOrHash))
250 {
251 return passwordOrHash;
252 }
253
254 return HashPassword(passwordOrHash);
255 }
256
281 public static bool IsDreamineHash(string value) =>
282 value.StartsWith($"{Version}.", StringComparison.Ordinal);
283
324 private static bool VerifyPbkdf2(string password, string storedHash, out bool needsRehash)
325 {
326 needsRehash = false;
327
328 var parts = storedHash.Split('.');
329 if (parts.Length != 4 || parts[0] != Version)
330 {
331 return false;
332 }
333
334 if (!int.TryParse(
335 parts[1],
336 System.Globalization.NumberStyles.None,
337 System.Globalization.CultureInfo.InvariantCulture,
338 out var iterations))
339 {
340 return false;
341 }
342
343 try
344 {
345 var salt = Convert.FromBase64String(parts[2]);
346 var expected = Convert.FromBase64String(parts[3]);
347 var actual = Rfc2898DeriveBytes.Pbkdf2(
348 password,
349 salt,
350 iterations,
351 HashAlgorithmName.SHA256,
352 expected.Length);
353
354 var verified = CryptographicOperations.FixedTimeEquals(actual, expected);
355 needsRehash = verified && iterations < CurrentIterations;
356 return verified;
357 }
358 catch (FormatException)
359 {
360 return false;
361 }
362 catch (ArgumentException)
363 {
364 return false;
365 }
366 }
367
400 private static bool VerifyLegacySha256(string password, string storedHash)
401 {
402 if (!IsLegacySha256Hash(storedHash))
403 {
404 return false;
405 }
406
407 var actual = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(password))).ToLowerInvariant();
408 return FixedTimeEquals(actual, storedHash.ToLowerInvariant());
409 }
410
435 private static bool IsLegacySha256Hash(string storedHash) =>
436 storedHash.Length == 64 && storedHash.All(Uri.IsHexDigit);
437
470 private static bool FixedTimeEquals(string left, string right)
471 {
472 var leftBytes = Encoding.UTF8.GetBytes(left);
473 var rightBytes = Encoding.UTF8.GetBytes(right);
474 return leftBytes.Length == rightBytes.Length &&
475 CryptographicOperations.FixedTimeEquals(leftBytes, rightBytes);
476 }
477}
478
static PasswordHashVerificationResult VerifyPassword(string password, string storedHash, out string? upgradedHash)
static bool FixedTimeEquals(string left, string right)
static string HashPlainTextForStorage(string passwordOrHash)
static bool IsLegacySha256Hash(string storedHash)
static bool VerifyLegacySha256(string password, string storedHash)
static bool VerifyPassword(string password, string storedHash)
static bool VerifyPbkdf2(string password, string storedHash, out bool needsRehash)
static string HashPassword(string password)