Last active
August 5, 2020 07:08
-
-
Save ntxinh/d8421902cbe7121db0c9116a99573ea4 to your computer and use it in GitHub Desktop.
Storing Passwords in .NET Core
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Hashing": { | |
"Iterations": 10000 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Source: https://medium.com/dealeron-dev/storing-passwords-in-net-core-3de29a3da4d2 | |
namespace Core.Services.Hash | |
{ | |
public sealed class HashingOptions | |
{ | |
public const string Hashing = "Hashing"; | |
public int Iterations { get; set; } = 10000; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Core.Services.Hash | |
{ | |
public interface IPasswordHasher | |
{ | |
string Hash(string password); | |
(bool Verified, bool NeedsUpgrade) Check(string hash, string password); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Security.Cryptography; | |
using Microsoft.Extensions.Options; | |
namespace Core.Services.Hash | |
{ | |
public sealed class PasswordHasher : IPasswordHasher | |
{ | |
private const int SaltSize = 16; // 128 bit | |
private const int KeySize = 32; // 256 bit | |
public PasswordHasher(IOptions<HashingOptions> options) | |
{ | |
Options = options.Value; | |
} | |
private HashingOptions Options { get; } | |
public string Hash(string password) | |
{ | |
using (var algorithm = new Rfc2898DeriveBytes( | |
password, | |
SaltSize, | |
Options.Iterations, | |
HashAlgorithmName.SHA512)) | |
{ | |
var key = Convert.ToBase64String(algorithm.GetBytes(KeySize)); | |
var salt = Convert.ToBase64String(algorithm.Salt); | |
return $"{Options.Iterations}.{salt}.{key}"; | |
} | |
} | |
public (bool Verified, bool NeedsUpgrade) Check(string hash, string password) | |
{ | |
var parts = hash.Split('.', 3); | |
if (parts.Length != 3) | |
{ | |
throw new FormatException("Unexpected hash format. " + | |
"Should be formatted as `{iterations}.{salt}.{hash}`"); | |
} | |
var iterations = Convert.ToInt32(parts[0]); | |
var salt = Convert.FromBase64String(parts[1]); | |
var key = Convert.FromBase64String(parts[2]); | |
var needsUpgrade = iterations != Options.Iterations; | |
using (var algorithm = new Rfc2898DeriveBytes( | |
password, | |
salt, | |
iterations, | |
HashAlgorithmName.SHA512)) | |
{ | |
var keyToCheck = algorithm.GetBytes(KeySize); | |
var verified = keyToCheck.SequenceEqual(key); | |
return (verified, needsUpgrade); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Core | |
{ | |
public class Startup | |
{ | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.Configure<HashingOptions>(Configuration.GetSection(HashingOptions.Hashing)); | |
services.AddScoped<IPasswordHasher, PasswordHasher>(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment