Singulink.Cryptography.PasswordHasher
Upgradable hash algorithm password management library for .NET.
Install / Use
/learn @Singulink/Singulink.Cryptography.PasswordHasherREADME
Singulink.Cryptography.PasswordHasher
| Project | Package |
| --- | --- |
| Singulink.Cryptography.PasswordHasher | |
| Singulink.Cryptography.PasswordHasher.Argon2 |
|
PasswordHasher greatly simplifies implementing security best practices with upgradable hash algorithm passwords. Hashes are upgradable in the sense that you can easily transition them to a different algorithm or increase the total number of iterations as periodically required in order to maintain the desired level of password security.
Support for PBKDF2 (SHA256/SHA384/SHA512) and Argon2 (Argon2i/Argon2d/Argon2id) is included out-of-the-box. PBKDF2 with SHA1 is also supported but only for reading/upgrading legacy hashes. Other algorithms (i.e. bcrypt, scrypt, etc) can be plugged in by adding a custom implementation of the PasswordHashAlgorithm class.
An additional layer of security can be added by encrypting hashes with a master key that is stored outside of the database so that hashes are not compromised if an attacker gains access to the database. AES128 encryption is included, but other algorithms can be plugged in by adding a custom implementation of the HashEncryptionAlgorithm class. Master keys can be updated or rotated with minimal effort and should be generated from a completely random source.
PasswordHasher implements RFC 8265 / RFC 7613 PRECIS Framework-compliant password normalization to ensure users don't have any Unicode encoding related issues when entering passwords. All spaces are replaced with standard ASCII spaces, invalid Unicode and control characters are blocked, and passwords are normalized to Normalization Form C as per the spec. Normalization can be turned off with a simple boolean property if you don't want normalization or you want to pre-process passwords with your own normalization scheme.
About Singulink
We are a small team of engineers and designers dedicated to building beautiful, functional and well-engineered software solutions. We offer very competitive rates as well as fixed-price contracts and welcome inquiries to discuss any custom development / project support needs you may have.
This package is part of our Singulink Libraries collection. Visit https://github.com/Singulink to see our full list of publicly available libraries and other open-source projects.
Installation
The package is available on NuGet - simply install the Singulink.Cryptography.PasswordHasher package. To add Argon2 support install the Singulink.Cryptography.PasswordHasher.Argon2 package.
Supported Runtimes: Anywhere .NET Standard 2.1+ is supported, including:
- .NET Core 3.0+
- Mono 6.4+
- Xamarin.iOS 12.16+
- Xamarin.Android 10.0+
API
You can view the API on FuGet. The main functionality is exposed via the PasswordHasher class in the Singulink.Cryptography namespace.
Usage
To create a PasswordHasher, at a minimum you need to specify the main hash algorithm and the total number of iterations to use. There are constructors that take an options object or options builder that you can use to specify addional options such as normalization, encryption parameters, and any legacy algorithms / encryption parameters that the hasher must still be capable of reading.
PasswordHasher is thread-safe so instances can be safely shared between threads. It contains the following primary methods:
string Hash(string password);
bool Verify(string hash, string password);
bool RequiresRehash(string hash, string password);
string Rehash(string password);
bool RequiresUpdate(string hash);
string? Update(string hash);
The first two methods should be self-explanatory: Hash produces a password hash, where-as Verify is used to verify a hash/password combo.
The last four methods are where it gets interesting:
RequiresRehash: Returns a value indicating whether a hash string should be regenerated from the known password. Returns true if the hash string contains chained hashes, the main algorithm / number of iterations does not match, the main encryption parameters do not match, or normalization settings do not match.Rehash: Safely rehashes an existing password by falling back to previous normalization settings if normalization fails with current settings.RequiresUpdate: Returns a value indicating whether the hash string needs to be updated, meaning the main encryption parameters do not match or an additional entry needs to be added to the hash chain so that it uses the main algorithm and total required number of iterations.Update: Gets an updated hash string that uses the main encryption parameters and main hash algorithm with the total number of required iterations without knowing the password, or returnsnullif the hash string does not require an update. Changing hash algorithms or adding iterations without knowing the password is achieved by hash chaining. If the hash algorithm or number of iterations has changed then the resulting hash string will returntruewhen passed into theRequiresRehash()method, which should be tested on successful user login so that a new hash string without chaining can be generated with theRehash()method.
Hash String Format
The format of the hash string is as follows:
!normalization_version #encryption_parameters_id hash_algorithm_id:iterations:salt hash
The first two parts are optional, so if normalization or encryption is not enabled then those elements are omitted.
Example 1:
!1 SHA256:1000:FV6nVAAqg1exolA+9fY2Nw== eqko5aiXBc+1BIBMKNi3VIhK9iPPW/dX85FcsVd1ITs=
Normalization: V1 algorithm (PRECIS RFC 8265)
Hash Encryption: None
Hash Algorithm: SHA256 (PBKDF2)
Iterations: 1000
Salt (Base64): FV6nVAAqg1exolA+9fY2Nw==
Hash (Base64): eqko5aiXBc+1BIBMKNi3VIhK9iPPW/dX85FcsVd1ITs=
Example 2:
#123 Argon2idV19-128-4P-512MB:5:1KmmrJ5fTKXOUWqlYCD7zQ== Nw0DxzZnXhe531IhEoE3ziqRJLxQiqh7Ovcs6H8IZVNqiKHilbhYKAJnBYJIyVybtc8U93P1Kr8gvIK18HtkboQYdnpFShbnEVCnjRXiF076kMxf4FtX4+kA+wUHVuzR
Normalization: None
Hash Encryption: Using parameters with ID# 123
Hash Algorithm: Argon2id V19[1.3] (128bit hash, parallelism: 4, memory: 512MB)
Iterations: 5
Salt (Base64): 1KmmrJ5fTKXOUWqlYCD7zQ==
Hash (Base64): Nw0DxzZnXhe531IhEoE3ziqRJLxQiqh7Ovcs6H8IZVNqiKHilbhYKAJnBYJIyVybtc8U93P1Kr8gvIK18HtkboQYdnpFShbnEVCnjRXiF076kMxf4FtX4+kA+wUHVuzR
The hash string in example 2 is the result of encrypting the hash algorithm output using the encyption parameters with ID 123.
If the hash chain was updated at some point (i.e. had additional algorithms or iterations applied to it), then those are added to the list. Each chained algorithm has its own salt value and the output bytes from the previous algorithm are fed into the next one. For example, if we started with SHA256 with 1000 iterations and upgraded to SHA512 with 20,000 iterations, the hash string might look something like this:
SHA256:1000:9QTkU8cSJ8xXkUdrx8qQVg== SHA512:20000:dlZfZk6CQstiyUAnZH5L7w== 07qYVKg1yx+AiRP+2oLxv3ozRmJ4tvb/IkgnsCO40LXT8Pm+bXXQnHoqKTQMy7e4IbMbTzOVH7cDqqBZ5RyygA==
Code Examples
Usage of the library is best demonstrated with some examples:
Basic hashing and verification
using Singulink.Cryprography;
string password = "12345678";
// Create hasher that uses SHA256 with 10,000 PBKDF2 iterations
var hasher = new PasswordHasher(PasswordHashAlgorithm.SHA256, 10000);
// Create a password hash
string hash = hasher.Hash(password);
// Verify the password
bool success = hasher.Verify(hash, password); // true
Turning normalization on or off or changing salt size
PasswordHasherOptions can be configured with additional options:
var hasher = new PasswordHasher(PasswordHashAlgorithm.SHA256, 10000, options => {
options.Normalize = false,
options.SaltSize = 20,
});
Updating hash algorithm or iterations
Hashes can be mass-updated with more iterations or new agorithms by writing a script (i.e. a CSX script) or a small utility program that does something like the following:
// Upgrade hashes in the database to SHA512 with 20,000 iterations. The SHA256 algorithm must be
// passed into the LegacyHashAlgorithms property so the hasher can read the current SHA256 hashes.
var hasher = new PasswordHasher(PasswordHashAlgorithm.SHA512, 20000, options => {
options.LegacyHashAlgorithms.Add(PasswordHashAlgorithm.SHA256);
});
foreach (var user in database.GetUsers())
{
if (hasher.RequiresUpdate(user.PasswordHash))
user.PasswordHash = hasher.Update(user.PasswordHash);
}
database.SaveChanges();
After running the script above, the hash strings in the database would now be composed of a SHA256 10,000 iteration hash chained to a SHA512 20,000 iteration hash. You will then want to rehash passwords to eliminate the chaining upon successful authentication using login code similar to the following:
// The SHA256 algorithm must still be passed into the LegacyHashAlgorithms property since the chained
// hashes contain a SHA256 component until they are rehashed.
var hasher = new PasswordHasher(PasswordHashAlgorithm.SHA512, 20000, options => {
options.LegacyHashAlgorithms.Add(PasswordHashAlgorithm.SHA256);
});
boo
Related Skills
node-connect
347.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
