Marai Cryptography

NuGet Stable License

Version: 1.0.2
Target Framework: net10.0+
NuGet Package ID: Marai.Cryptography


Introduction

Marai.Cryptography is a production-ready cryptography library for .NET providing AES-256 encryption across multiple modes, HMAC-SHA256 request signing, and secure PBKDF2 password hashing with a clean, consistent API.

Major capabilities:

  • AES-256 encryption and decryption in GCM, CBC, CTR, CFB, and GCMFormatted modes
  • SHA-256 hashing of arbitrary text
  • Key and IV generation for AES-256
  • HMAC-SHA256 request signature creation and validation with replay-attack protection
  • Secure PBKDF2/SHA-256 password hashing with randomized salt and iteration count
  • Constant-time comparison for signature and password verification
  • Microsoft Dependency Injection integration

Table of Contents


Installation

1 dotnet add package Marai.Cryptography
1 Install-Package Marai.Cryptography

Getting Started

Register with Dependency Injection

1 2 3 4 5 6 7 8 9 using Marai.Cryptography.DependencyInjection; // With a pre-configured key and IV (required for EncryptGcm / DecryptGcm) builder.Services.AddMaraiCryptography( key: “your-base64-encoded-256-bit-key==”, vector: “your-base64-encoded-iv==”); // Without a key (only explicit-key overloads will work) builder.Services.AddMaraiCryptography();

AddMaraiCryptography registers:

  • ISecurityAESProvider (singleton) — encryption and hashing
  • ISecurityHMACSignatureProvider (singleton) — HMAC request signing
  • ISecurityPasswordProvider (singleton) — password hashing

Generate a Key and IV

1 2 3 var (key, iv) = aesProvider.GenerateAes256KeyAndIv(); Console.WriteLine($”Key: {key}”); Console.WriteLine($”IV: {iv}”);

AES Provider

Encryption Modes

Value Name Description Recommended
GCMFormatted AES-256-GCM with header GCM with magic-byte prefix for format detection. Layout: [GCM(3)][nonce(12)][tag(16)][ciphertext] Yes — default
GCM AES-256-GCM raw Authenticated GCM without format header. Layout: [nonce(12)][tag(16)][ciphertext] Yes
CBC AES-256-CBC Block cipher with PKCS7 padding. Layout: [iv(16)][ciphertext] Acceptable
CTR AES-256-CTR Stream cipher mode. Layout: [counter(16)][ciphertext] Acceptable
CFB AES-256-CFB Cipher Feedback mode (CFB-128). Layout: [iv(16)][ciphertext] Acceptable
ECB AES-256-ECB No IV. Identical plaintext blocks produce identical ciphertext. Deprecated. No
ECB is marked [Obsolete] in the library. Do not use it for any data where block-level patterns could be revealing.

Default Encrypt and Decrypt

EncryptGcm and DecryptGcm use the key and IV passed to AddMaraiCryptography.

1 2 3 4 5 6 7 8 9 10 11 12 public class DataService(ISecurityAESProvider aes) { public string StoreSecret(string plainText) { return aes.EncryptGcm(plainText); } public string ReadSecret(string encrypted) { return aes.DecryptGcm(encrypted); } }

Explicit Mode Encrypt and Decrypt

1 2 3 4 5 6 7 8 // Encrypt with GCMFormatted (default) string encrypted = aes.Encrypt(plainText, myKeyBase64, EncryptionFormats.GCMFormatted, myIvBase64); // Decrypt string decrypted = aes.Decrypt(encrypted, myKeyBase64, EncryptionFormats.GCMFormatted, myIvBase64); // Encrypt with CBC string cbcEncrypted = aes.Encrypt(plainText, myKeyBase64, EncryptionFormats.CBC, myIvBase64);
Keys and IVs are passed as strings. The library derives a fixed-length byte array via SHA-256. Use GenerateAes256KeyAndIv() to produce a proper random key.

SHA-256 Hash

1 2 string hash = aes.SHA256Hash(“hello world”); // Returns lowercase hex string, e.g. “b94d27b9…”

Key and IV Generation

1 2 3 var (keyBase64, ivBase64) = aes.GenerateAes256KeyAndIv(); // keyBase64 — 256-bit key (32 bytes), Base64-encoded // ivBase64 — 128-bit IV (16 bytes), Base64-encoded

HMAC Signature Provider

Creating a Signature

1 2 3 var secretKey = new HmacSecretKey(Base64: mySecretBase64); string signature = hmac.CreateSignature(requestPayloadJson, secretKey); // Returns a Base64-encoded HMAC-SHA256 signature string

The payload must be valid JSON. The library wraps it with the current UTC Unix timestamp before signing:

1 2 3 4 { “TimeStamp”: 1700000000, “RequestPayLoad”: { } }

Validating a Signature

1 2 3 4 5 6 7 8 var request = new HmacSignedRequest( Payload: requestPayloadJson, Signature: receivedSignature, TimeStamp: new UnixTimeSeconds(receivedTimestamp), SecretKey: new HmacSecretKey(mySecretBase64)); bool valid = hmac.ValidateSignature(request); // Returns true on success; throws on failure

ValidateSignature steps:

  1. Recomputes the expected HMAC using the payload and timestamp.
  2. Compares using constant-time comparison (CryptographicOperations.FixedTimeEquals).
  3. Validates the timestamp is within the allowed clock skew and expiration window.

Default timing windows: AllowedClockSkewInMinutes = 5, AllowedExpirationInMinutes = 5.


Password Provider

Hashing a Password

1 2 PasswordHash hash = passwordProvider.SecurePassword(“my-password”); // Store hash.HashBase64, hash.SaltBase64, hash.Iterations in your database

Validating a Password

1 2 3 4 5 6 7 var stored = new PasswordHash( HashBase64: storedHashFromDb, SaltBase64: storedSaltFromDb, Iterations: storedIterationsFromDb); bool isValid = passwordProvider.IsValidPassword(“my-password”, stored);

Models Reference

EncryptionFormats

1 2 3 4 5 6 7 8 9 public enum EncryptionFormats { GCM = 1, GCMFormatted = 2, CBC = 3, CTR = 4, [Obsolete] ECB = 5, CFB = 6 }

HmacSecretKey

Member Type Description
Base64 string Base64-encoded HMAC secret key bytes
ToBytes() byte[] Decodes the key to a raw byte array; throws if blank

HmacSignedRequest

Member Type Description
Payload string The original JSON request payload
Signature string Base64-encoded HMAC-SHA256 signature to validate
TimeStamp UnixTimeSeconds Unix timestamp when the signature was created
SecretKey HmacSecretKey The shared secret key
allowedClockSkewInMinutes int Clock skew tolerance (default: 0, uses config value of 5)
allowedExpirationInMinutes int Max request age in minutes (default: 0, uses config value of 5)

PasswordHash

Member Type Description
HashBase64 string Base64-encoded PBKDF2 hash output
SaltBase64 string Base64-encoded random salt
Iterations int PBKDF2 iteration count used when hashing

UnixTimeSeconds

Member Type Description
Value long Unix timestamp in seconds
Now() UnixTimeSeconds (static) Returns current UTC time as UnixTimeSeconds
ToDateTimeOffset() DateTimeOffset Converts to UTC DateTimeOffset

Exceptions Reference

Exception Thrown When
InvalidSignatureException HMAC signature does not match
ExpiredSignatureException Request timestamp is outside the allowed window
InvalidParameterException A required parameter has an invalid value
ParameterNotFoundException A required parameter is missing
UnknownErrorException An unexpected error occurs
1 2 3 4 5 6 7 8 9 10 11 12 try { bool valid = hmac.ValidateSignature(request); } catch (InvalidSignatureException) { // Signature mismatch — reject the request } catch (ExpiredSignatureException) { // Timestamp too old or too far in the future — reject }

Security Notes

  • AES-256-GCM / GCMFormatted — Use for all new encryption. GCM provides authenticated encryption, detecting tampering automatically.
  • AES-256-CBC / CFB / CTR — Acceptable for interoperability. No built-in authentication; combine with a separate MAC if tamper detection is needed.
  • AES-256-ECB — Do not use. Marked [Obsolete] in the library.

Key and IV Management

  • Never hardcode keys in source code. Store them in environment variables, Azure Key Vault, AWS Secrets Manager, or similar.
  • Use GenerateAes256KeyAndIv() to produce cryptographically secure random keys and IVs.

Signature Security

  • Uses CryptographicOperations.FixedTimeEquals to prevent timing side-channels.
  • The 5-minute expiration window blocks replay attacks. Tune allowedExpirationInMinutes as needed.
  • The secret key should be at least 256 bits (32 bytes) of random data.

Password Hashing

  • Uses PBKDF2 with SHA-256, a random 16-byte salt, and a random iteration count between 5,000 and 10,000.
  • Password comparison uses constant-time comparison to prevent timing attacks.

Full API Reference

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 // AES Provider interface ISecurityAESProvider { string EncryptGcm(string plainText); // Encrypt using injected key/IV string DecryptGcm(string plainText); // Decrypt using injected key string Encrypt(string plainText, string key, EncryptionFormats format = GCMFormatted, string? iv = null); string Decrypt(string encryptedText, string key, EncryptionFormats format = GCMFormatted, string? iv = null); string SHA256Hash(string text); // SHA-256 hex hash (string KeyBase64, string IvBase64) GenerateAes256KeyAndIv(); // Generate random key+IV } // HMAC Signature Provider interface ISecurityHMACSignatureProvider { string CreateSignature(string requestPayload, HmacSecretKey secretKey); // Create HMAC-SHA256 signature bool ValidateSignature(HmacSignedRequest request); // Validate; throws on failure } // Password Provider interface ISecurityPasswordProvider { PasswordHash SecurePassword(string password); // Hash a password bool IsValidPassword(string password, PasswordHash stored); // Verify a password } // DI Registration static IServiceCollection AddMaraiCryptography(this IServiceCollection services, string key = “”, string vector = “”); // Models readonly record struct HmacSecretKey(string Base64) { byte[] ToBytes(); } sealed record HmacSignedRequest(string Payload, string Signature, UnixTimeSeconds TimeStamp, HmacSecretKey SecretKey, int allowedClockSkewInMinutes = 0, int allowedExpirationInMinutes = 0); readonly record struct PasswordHash(string HashBase64, string SaltBase64, int Iterations); readonly record struct UnixTimeSeconds(long Value) { static UnixTimeSeconds Now(); DateTimeOffset ToDateTimeOffset(); } enum EncryptionFormats { GCM = 1, GCMFormatted = 2, CBC = 3, CTR = 4, [Obsolete] ECB = 5, CFB = 6 } // Exceptions (all extend Exception) sealed class InvalidSignatureException : Exception { } sealed class ExpiredSignatureException : Exception { } sealed class InvalidParameterException : Exception { } sealed class ParameterNotFoundException : Exception { } sealed class UnknownErrorException : Exception { } // Configuration constants static class SecurityRSASignatureConfiguration { const int AllowedClockSkewInMinutes = 5; const int AllowedExpirationInMinutes = 5; }

License

This project is licensed under the Marai Proprietary Software License Agreement.

Free for personal and commercial use.

For the complete license agreement and terms, please visit: https://marai.dev/proprietary-software-license-agreement