Hashing
Tharga.Toolkit ships two distinct hash surfaces: cryptographic hash digests (ToHash, HashString, Base32) and password verification (PasswordHasher + the ApiKey service). The first is for fingerprinting data; the second is for authenticating users.
Cryptographic hashing
Compute MD5, SHA1, SHA256, SHA384, or SHA512 from strings, byte arrays, URIs, or streams.
var bytes = "hello".ToHash(HashType.SHA256); // Hash (raw bytes)
var hex = "hello".ToHash(HashFormat.HexLower, HashType.SHA256); // HashString
var b64 = "hello".ToHash(HashFormat.Base64, HashType.SHA256);
await using var stream = File.OpenRead("file.dat");
var streamHash = await stream.ToHashAsync(HashFormat.Base64, HashType.SHA256);
Format conversion
HashString carries both the formatted representation and the original bytes, so you can re-format without recomputing the hash:
var hex = "hello".ToHash(HashFormat.HexLower, HashType.SHA256);
var base32 = hex.ChangeFormat(HashFormat.Base32);
var base64 = hex.ChangeFormat(HashFormat.Base64);
Available formats: Hex, HexLower, HexWithDashes, Base64, Base32.
Implicit conversions
Hash converts implicitly to byte[] and HashString to string, so you can use them anywhere those types are expected:
byte[] raw = "hello".ToHash(HashType.SHA256);
string s = "hello".ToHash(HashFormat.HexLower, HashType.SHA256);
Base32 encoding (RFC 4648)
var encoded = Base32Encoding.Encode(Encoding.UTF8.GetBytes("hello"));
var decoded = Base32Encoding.Decode(encoded);
Password hashing (PBKDF2)
PasswordHasher derives a PBKDF2 hash, prepends the salt, and packages everything into a single string. Verification re-derives and compares in constant time.
var hash = PasswordHasher.HashPassword("secret");
bool ok = PasswordHasher.VerifyPassword("secret", hash); // true
bool bad = PasswordHasher.VerifyPassword("wrong", hash); // false
This is the Standard package's per-process surface — same algorithm, no DI, no options.
API key service (Tharga.Toolkit only)
For backend-issued API keys, ApiKeyService builds, encrypts, verifies, and extracts the username from a key. It's the same PBKDF2 base, packaged for DI registration and configurable iteration count.
// Register with code-based options
builder.Services.RegisterApiKeyService(o =>
{
o.SaltSize = 32;
o.Iterations = 20000;
});
// Or bind from IConfiguration (default section "ApiKey")
builder.Services.RegisterApiKeyService(builder.Configuration);
builder.Services.RegisterApiKeyService(builder.Configuration, "MyApiKeys");
Example appsettings.json:
{
"ApiKey": {
"SaltSize": 32,
"HashSize": 32,
"Iterations": 20000
}
}
Use the service via IApiKeyService:
public class IssueController(IApiKeyService apiKeyService) : ControllerBase
{
[HttpPost("issue/{username}")]
public IActionResult Issue(string username)
{
var apiKey = apiKeyService.BuildApiKey(username);
var encrypted = apiKeyService.Encrypt(apiKey);
// Store `encrypted` in your database. Return `apiKey` to the caller exactly once.
return Ok(new { apiKey });
}
}
public class VerifyMiddleware(IApiKeyService apiKeyService)
{
public bool Authenticate(string apiKey, string storedEncrypted)
=> apiKeyService.Verify(apiKey, storedEncrypted);
public string ExtractUser(string apiKey)
=> apiKeyService.GetUsername(apiKey);
}
The username is embedded in the key, so GetUsername(apiKey) recovers it without a database lookup — useful for routing the verify step to the right stored hash.