Regira-Packages

Regira Security — Encryption & Hashing

Symmetric encryption and password hashing from Regira.Security, plus the BCrypt hasher in Regira.Security.Hashing.BCryptNet. Both families are interface-first, so a consumer depends on IEncrypter / IHasher and picks the implementation at registration.


Encryption

Two IEncrypter implementations:

public interface IEncrypter
{
    string Encrypt(string plainText, string? key = null);
    string Decrypt(string encryptedText, string? key = null);
}

SymmetricEncrypter

Obsolete. AES-256 with a key and vector derived from CryptoOptions.Secret alone, so a value always encrypts to the same ciphertext. Keep it only to decrypt what it encrypted; encrypt with AesEncrypter. Without a secret it uses the built-in key, which is published with the source.

var legacy = new SymmetricEncrypter(new CryptoOptions { Secret = "my-app-key" });
string plain = legacy.Decrypt(storedCipher);

AesEncrypter

AES-256 with a random salt per value, so it produces different ciphertext on each call — recommended for stored secrets. The costly step, PBKDF2 over the secret (about 0.2 s), runs once per secret and key salt and is cached for the process; each value then gets a key of its own from its salt with HKDF, in microseconds. A process picks one key salt per secret for what it encrypts, so it pays the step once for its own values and once for each earlier process lifetime or other instance whose values it reads: decrypting in an EF value converter stays cheap, and the first read after a restart costs about 0.2 s per earlier writer. The text is aes1: and Base64, 64 bytes longer than the cipher: 32 of salt and a 32-byte HMAC-SHA256 tag, so a value that was changed, or encrypted with another secret, throws CryptographicException. A value without that prefix, written before 6.6.0, still decrypts, paying the derivation on its first read in a process; decrypt and encrypt it again to move it. A version before 6.6.0 cannot read the aes1: format. Encrypting needs a secret, CryptoOptions.Secret or a key passed to Encrypt, and throws InvalidOperationException without one. Decrypt without one uses the built-in key, so what an earlier version encrypted with it stays readable.

var enc = new AesEncrypter(new CryptoOptions { Secret = "my-app-key" });
string cipher = enc.Encrypt("sensitive value");

A value converter still decrypts the column whenever the entity loads, even into a DTO that drops it, and a read DTO that carries it sends the plain value to every client. For a value few screens show (a bank account), store the ciphertext in a plain property encrypted by a prepper from a write-only input, keep it out of the read DTOs (a masked copy can serve the lists), and decrypt it in an endpoint of its own that returns one value.

CryptoOptions

Property Type Default Description
Secret string? null The key AesEncrypter derives from; it refuses to encrypt without one. Without it, decrypting and the obsolete SymmetricEncrypter and SimpleHasher use a built-in key, published with the source
AlgorithmType string? "SHA512" Hash algorithm — read only by SymmetricEncrypter, SimpleHasher, and the BCrypt hasher; AesEncrypter and the PBKDF2 Hasher hard-wire SHA-512
Iterations int? 500000 PBKDF2 iteration count used by the Hasher
Encoding Encoding? UTF-8 Text encoding

Hashing

Two IHasher implementations:

public interface IHasher
{
    string Hash(string plainText);
    bool   Verify(string plainText, string hashedValue);
}

Hasher (PBKDF2)

Stores a per-hash random salt + PBKDF2 digest (500 000 iterations by default — configurable via CryptoOptions.Iterations — SHA-512, 64-byte output). Constant-time verification.

var hasher = new Regira.Security.Hashing.Hasher();
string stored = hasher.Hash("myPassword123");
bool ok       = hasher.Verify("myPassword123", stored);   // true

Security.Hashing.BCryptNet — BCrypt Hasher

Enhanced BCrypt (SHA-384 by default), using the BCrypt.Net default work factor. Recommended for passwords.

var hasher = new Regira.Security.Hashing.BCryptNet.Hasher();
string stored = hasher.Hash("myPassword123");
bool ok       = hasher.Verify("myPassword123", stored);

SimpleHasher

Obsolete: a double SHA with a fixed salt, compared in variable time. Hash with Hasher (PBKDF2), or the BCrypt Hasher for passwords.


Overview

  1. Index — Overview, projects, and choosing a scheme
  2. Encryption & Hashing — Symmetric encryption, PBKDF2 and BCrypt password hashing
  3. JWT Authentication — Self-issued bearer tokens, claims, and refresh tokens
  4. API Key Authentication — Key-based auth for machine callers
  5. External Identity Providers — Validating external bearer tokens; OpenID Connect sign-in
  6. Cookie Authentication — Cookie-backed sessions
  7. Composing Multiple Schemes — Running several schemes side by side
  8. Pre-built Auth Controllers — Account, password and user endpoints
  9. Practical Examples — Complete implementation examples

License

Apache License 2.0 — this package contains no license validation and no runtime limits. See LICENSE. A few companion packages are commercially licensed with a free tier; see the licensing overview.