A professional, production-ready cryptography library for .NET applications providing AES-256-GCM encryption, secure password hashing, and HMAC-SHA256 request signing with a simple, intuitive API.
Table of Contents
- Features
- Installation
- Dependency Injection Setup
- Core Interfaces
- Complete Examples
- Security Best Practices
- Configuration
- Exception Handling
- FAQ
- License
Features
AES-256-GCM Encryption
- Industry-standard authenticated encryption
- Automatic nonce and tag management
- Built-in format validation
- Secure key generation
Secure Password Hashing
- PBKDF2 with SHA-256
- Automatic salt generation
- Configurable iterations (5,000-10,000)
- Constant-time verification
HMAC Request Signing
- HMAC-SHA256 signatures
- Timestamp-based replay protection
- Configurable expiration windows
- Built-in validation
Installation
Using .NET CLI
dotnet add package Marai.CryptographyUsing Package Manager Console
Install-Package Marai.CryptographyUsing PackageReference
Add to your .csproj file:
<PackageReference Include="Marai.Cryptography" Version="1.0.0-Alpha.1" />Requirements
- .NET 10.0 or later
- Microsoft.Extensions.DependencyInjection (for DI support)
Dependency Injection Setup
ASP.NET Core Web API
// Program.cs
using Marai.Cryptography;
var builder = WebApplication.CreateBuilder(args);
// Register Marai.Cryptography services
builder.Services.AddMaraiCryptography();
// Register other services
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();Console Application
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Marai.Cryptography;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
// Register Marai.Cryptography
services.AddMaraiCryptography();
// Register your services
services.AddTransient<MyService>();
})
.Build();
await host.RunAsync();Blazor Application
// Program.cs
using Marai.Cryptography;
var builder = WebApplication.CreateBuilder(args);
// Register services
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddMaraiCryptography();
var app = builder.Build();
app.Run();What Gets Registered:
The AddMaraiCryptography() extension method registers these three services as Singletons:
ISecurityAESProvider→SecurityAESProviderISecurityPasswordProvider→SecurityPasswordProviderISecurityRSASignatureProvider→SecurityRSASignatureProvider
All implementations are thread-safe and suitable for singleton lifetime.
Core Interfaces
ISecurityAESProvider
Provides AES-256-GCM encryption, decryption, and SHA-256 hashing capabilities.
Methods Overview
public interface ISecurityAESProvider
{
// Encryption & Decryption
string EncryptGCM(string plainText, string key, string initVector);
string DecryptGCM(string encryptedText, string key, string initVector);
// Validation
bool IsGcmFormat(string encryptedText);
// Hashing
string SHA256Hash(string text);
// Key Generation
(string KeyBase64, string IvBase64) GenerateAes256KeyAndIv();
}EncryptGCM
Encrypts text using AES-256-GCM authenticated encryption.
Signature:
string EncryptGCM(string plainText, string key, string initVector)Parameters:
plainText– The text to encryptkey– Encryption key (any length, will be derived to 256 bits)initVector– Initialization vector (will be derived to 96-bit nonce)
Returns:
- Base64-encoded encrypted data with GCM header
Example:
public class DataEncryptionService
{
private readonly ISecurityAESProvider _aesProvider;
private readonly IConfiguration _configuration;
public DataEncryptionService(
ISecurityAESProvider aesProvider,
IConfiguration configuration)
{
_aesProvider = aesProvider;
_configuration = configuration;
}
public string EncryptSensitiveData(string data)
{
// Get keys from configuration (Azure Key Vault, etc.)
var key = _configuration["Encryption:Key"];
var iv = _configuration["Encryption:IV"];
// Encrypt the data
var encrypted = _aesProvider.EncryptGCM(data, key, iv);
return encrypted;
// Output: R0NNjY2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3...
}
}DecryptGCM
Decrypts AES-256-GCM encrypted data.
Signature:
string DecryptGCM(string encryptedText, string key, string initVector)Parameters:
encryptedText– Base64-encoded encrypted datakey– Decryption key (must match encryption key)initVector– Initialization vector (must match encryption IV)
Returns:
- Decrypted plaintext
Throws:
CryptographicException– Invalid key, IV, or tampered dataFormatException– Invalid Base64 encoding
Example:
public class DataDecryptionService
{
private readonly ISecurityAESProvider _aesProvider;
private readonly IConfiguration _configuration;
public DataDecryptionService(
ISecurityAESProvider aesProvider,
IConfiguration configuration)
{
_aesProvider = aesProvider;
_configuration = configuration;
}
public string DecryptSensitiveData(string encryptedData)
{
try
{
var key = _configuration["Encryption:Key"];
var iv = _configuration["Encryption:IV"];
// Decrypt the data
var decrypted = _aesProvider.DecryptGCM(encryptedData, key, iv);
return decrypted;
}
catch (CryptographicException ex)
{
// Handle decryption failure (wrong key, tampered data, etc.)
_logger.LogError(ex, "Decryption failed");
throw;
}
}
}GenerateAes256KeyAndIv
Generates cryptographically secure random key and IV for AES-256.
Signature:
(string KeyBase64, string IvBase64) GenerateAes256KeyAndIv()Returns:
- Tuple containing Base64-encoded key (32 bytes) and IV (16 bytes)
Example:
public class KeyGenerationService
{
private readonly ISecurityAESProvider _aesProvider;
public async Task<EncryptionKeys> GenerateAndStoreKeys()
{
// Generate secure keys
var (key, iv) = _aesProvider.GenerateAes256KeyAndIv();
Console.WriteLine($"Generated Key: {key}");
Console.WriteLine($"Generated IV: {iv}");
// Store in Azure Key Vault, AWS Secrets Manager, etc.
await _keyVaultService.SetSecretAsync("AesEncryptionKey", key);
await _keyVaultService.SetSecretAsync("AesEncryptionIV", iv);
return new EncryptionKeys { Key = key, IV = iv };
}
}SHA256Hash
Computes SHA-256 hash of text.
Signature:
string SHA256Hash(string text)Parameters:
text– The text to hash
Returns:
- Hexadecimal hash string (64 characters)
Example:
public class FileIntegrityService
{
private readonly ISecurityAESProvider _aesProvider;
public async Task<string> CalculateFileHash(string filePath)
{
var content = await File.ReadAllTextAsync(filePath);
var hash = _aesProvider.SHA256Hash(content);
Console.WriteLine($"File: {filePath}");
Console.WriteLine($"Hash: {hash}");
return hash;
// Output: a1b2c3d4e5f6789012345678901234567890123456789012345678901234567890
}
public bool VerifyFileIntegrity(string content, string expectedHash)
{
var actualHash = _aesProvider.SHA256Hash(content);
return actualHash.Equals(expectedHash, StringComparison.OrdinalIgnoreCase);
}
}IsGcmFormat
Validates if encrypted text has valid GCM format.
Signature:
bool IsGcmFormat(string encryptedText)Parameters:
encryptedText– Encrypted data to validate
Returns:
trueif valid GCM format,falseotherwise
Example:
public class SecureDataService
{
private readonly ISecurityAESProvider _aesProvider;
public string ProcessEncryptedData(string encryptedData, string key, string iv)
{
// Validate format before attempting decryption
if (!_aesProvider.IsGcmFormat(encryptedData))
{
throw new InvalidDataException("Invalid GCM encrypted format");
}
// Safe to decrypt
return _aesProvider.DecryptGCM(encryptedData, key, iv);
}
}ISecurityPasswordProvider
Provides secure password hashing using PBKDF2 with SHA-256.
Methods Overview
public interface ISecurityPasswordProvider
{
// Hash password with auto-generated salt and iterations
(string HashedPassword, string Salt, int Iterations) SecurePassword(string password);
// Hash password with specific salt and iterations
string SecurePassword(string password, string salt, int iterations);
// Verify password against stored hash
bool IsValidPassword(string hashedPassword, string password, string salt, int iterations);
}SecurePassword (Auto-generated)
Hashes a password with automatically generated salt and random iteration count.
Signature:
(string HashedPassword, string Salt, int Iterations) SecurePassword(string password)Parameters:
password– The password to hash
Returns:
- Tuple containing hashed password, salt, and iteration count
Example – User Registration:
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly ISecurityPasswordProvider _passwordProvider;
private readonly ApplicationDbContext _dbContext;
public AuthController(
ISecurityPasswordProvider passwordProvider,
ApplicationDbContext dbContext)
{
_passwordProvider = passwordProvider;
_dbContext = dbContext;
}
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] RegisterRequest request)
{
// Hash password with auto-generated salt and random iterations
var (hashedPassword, salt, iterations) =
_passwordProvider.SecurePassword(request.Password);
// Create user
var user = new User
{
Email = request.Email,
PasswordHash = hashedPassword,
PasswordSalt = salt,
PasswordIterations = iterations,
CreatedAt = DateTime.UtcNow
};
_dbContext.Users.Add(user);
await _dbContext.SaveChangesAsync();
return Ok(new { message = "User registered successfully" });
}
}SecurePassword (With Parameters)
Hashes a password with specific salt and iteration count.
Signature:
string SecurePassword(string password, string salt, int iterations)Parameters:
password– The password to hashsalt– Base64-encoded saltiterations– PBKDF2 iteration count
Returns:
- Base64-encoded hashed password
Example – Password Verification:
public class PasswordVerificationService
{
private readonly ISecurityPasswordProvider _passwordProvider;
public bool VerifyUserPassword(User user, string providedPassword)
{
// Hash the provided password with stored salt and iterations
var hashedAttempt = _passwordProvider.SecurePassword(
providedPassword,
user.PasswordSalt,
user.PasswordIterations
);
// Compare with stored hash
return hashedAttempt == user.PasswordHash;
}
}IsValidPassword
Verifies a password against a stored hash (recommended method).
Signature:
bool IsValidPassword(string hashedPassword, string password, string salt, int iterations)Parameters:
hashedPassword– The stored password hashpassword– The password to verifysalt– The stored saltiterations– The stored iteration count
Returns:
trueif password matches,falseotherwise
Example – User Login:
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly ISecurityPasswordProvider _passwordProvider;
private readonly ApplicationDbContext _dbContext;
private readonly IJwtTokenService _tokenService;
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
// Find user
var user = await _dbContext.Users
.FirstOrDefaultAsync(u => u.Email == request.Email);
if (user == null)
{
return Unauthorized(new { error = "Invalid credentials" });
}
// Verify password
bool isValid = _passwordProvider.IsValidPassword(
user.PasswordHash,
request.Password,
user.PasswordSalt,
user.PasswordIterations
);
if (!isValid)
{
return Unauthorized(new { error = "Invalid credentials" });
}
// Generate JWT token
var token = _tokenService.GenerateToken(user);
return Ok(new
{
token,
user = new { user.Id, user.Email }
});
}
}ISecurityRSASignatureProvider
Provides HMAC-SHA256 request signing and validation for API authentication.
Methods Overview
public interface ISecurityRSASignatureProvider
{
// Create signature for request
string CreateSignature(string requestPayload, string hmacSecretKey);
// Validate signature (default settings)
void ValidateSignature(string requestSignature, string requestPayload,
string requestHeaderTimeStamp, string hmacSecretKey);
// Validate signature (custom settings)
void ValidateSignature(string requestSignature, string requestPayload,
string requestHeaderTimeStamp, string hmacSecretKey,
int allowedClockSkewInMinutes = 0, int allowedExpirationInMinutes = 0);
}CreateSignature
Creates an HMAC-SHA256 signature for a request payload.
Signature:
string CreateSignature(string requestPayload, string hmacSecretKey)Parameters:
requestPayload– JSON payload to signhmacSecretKey– Secret key for HMAC computation
Returns:
- Base64-encoded HMAC-SHA256 signature
Example – API Client:
public class SecureApiClient
{
private readonly ISecurityRSASignatureProvider _signatureProvider;
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
public SecureApiClient(
ISecurityRSASignatureProvider signatureProvider,
HttpClient httpClient,
IConfiguration configuration)
{
_signatureProvider = signatureProvider;
_httpClient = httpClient;
_configuration = configuration;
}
public async Task<OrderResponse> CreateOrder(CreateOrderRequest order)
{
// Serialize payload to JSON
var payload = JsonSerializer.Serialize(order);
// Get secret key from configuration
var secretKey = _configuration["Api:HmacSecretKey"];
// Create signature
var signature = _signatureProvider.CreateSignature(payload, secretKey);
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
// Build HTTP request with signature headers
var request = new HttpRequestMessage(HttpMethod.Post,
"https://api.example.com/orders");
request.Headers.Add("X-Signature", signature);
request.Headers.Add("X-Timestamp", timestamp);
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
// Send request
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<OrderResponse>(responseContent);
}
}ValidateSignature (Default)
Validates signature using default configuration (5-minute clock skew and expiration).
Signature:
void ValidateSignature(string requestSignature, string requestPayload,
string requestHeaderTimeStamp, string hmacSecretKey)Parameters:
requestSignature– The signature to validaterequestPayload– The JSON payload that was signedrequestHeaderTimeStamp– Unix timestamp (seconds) as stringhmacSecretKey– Secret key for verification
Throws:
InvalidSignatureException– Signature verification failedExpiredSignatureException– Timestamp outside valid windowParameterNotFoundException– Timestamp is missingInvalidParameterException– Timestamp format invalid
Example – API Endpoint:
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly ISecurityRSASignatureProvider _signatureProvider;
private readonly IConfiguration _configuration;
private readonly IOrderService _orderService;
public OrdersController(
ISecurityRSASignatureProvider signatureProvider,
IConfiguration configuration,
IOrderService orderService)
{
_signatureProvider = signatureProvider;
_configuration = configuration;
_orderService = orderService;
}
[HttpPost]
public async Task<IActionResult> CreateOrder([FromBody] JsonElement payload)
{
try
{
// Extract headers
var signature = Request.Headers["X-Signature"].FirstOrDefault();
var timestamp = Request.Headers["X-Timestamp"].FirstOrDefault();
if (string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(timestamp))
{
return BadRequest(new { error = "Missing authentication headers" });
}
// Get secret key
var secretKey = _configuration["Api:HmacSecretKey"];
var payloadJson = payload.GetRawText();
// Validate signature (5 min clock skew, 5 min expiration)
_signatureProvider.ValidateSignature(
signature,
payloadJson,
timestamp,
secretKey
);
// Signature valid - process request
var order = JsonSerializer.Deserialize<CreateOrderRequest>(payloadJson);
var result = await _orderService.CreateOrderAsync(order);
return Ok(result);
}
catch (InvalidSignatureException)
{
return Unauthorized(new { error = "Invalid signature" });
}
catch (ExpiredSignatureException ex)
{
return Unauthorized(new { error = ex.Message });
}
catch (ParameterNotFoundException ex)
{
return BadRequest(new { error = ex.Message });
}
}
}ValidateSignature (Custom)
Validates signature with custom clock skew and expiration settings.
Signature:
void ValidateSignature(string requestSignature, string requestPayload,
string requestHeaderTimeStamp, string hmacSecretKey,
int allowedClockSkewInMinutes = 0, int allowedExpirationInMinutes = 0)Parameters:
- Same as default method, plus:
allowedClockSkewInMinutes– Custom clock skew tolerance (0 = use default 5 min)allowedExpirationInMinutes– Custom expiration window (0 = use default 5 min)
Example – Webhook Handler:
[ApiController]
[Route("api/webhooks")]
public class WebhooksController : ControllerBase
{
private readonly ISecurityRSASignatureProvider _signatureProvider;
private readonly IConfiguration _configuration;
[HttpPost("payment")]
public async Task<IActionResult> HandlePaymentWebhook([FromBody] JsonElement payload)
{
try
{
var signature = Request.Headers["X-Webhook-Signature"].FirstOrDefault();
var timestamp = Request.Headers["X-Webhook-Timestamp"].FirstOrDefault();
var secretKey = _configuration["PaymentProvider:WebhookSecret"];
// Webhooks may be delayed, so allow longer expiration
_signatureProvider.ValidateSignature(
signature,
payload.GetRawText(),
timestamp,
secretKey,
allowedClockSkewInMinutes: 10, // 10 min clock skew
allowedExpirationInMinutes: 60 // 1 hour expiration
);
// Process webhook
await ProcessPaymentWebhook(payload);
return Ok(new { message = "Webhook processed" });
}
catch (InvalidSignatureException)
{
return Unauthorized(new { error = "Invalid webhook signature" });
}
catch (ExpiredSignatureException)
{
return Unauthorized(new { error = "Webhook expired" });
}
}
}Complete Examples
Example 1: Securing Database Fields
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
public string PasswordSalt { get; set; }
public int PasswordIterations { get; set; }
public string SocialSecurityNumber { get; set; } // Encrypted in DB
}
public class UserService
{
private readonly ISecurityAESProvider _aesProvider;
private readonly ISecurityPasswordProvider _passwordProvider;
private readonly ApplicationDbContext _dbContext;
private readonly IConfiguration _configuration;
public UserService(
ISecurityAESProvider aesProvider,
ISecurityPasswordProvider passwordProvider,
ApplicationDbContext dbContext,
IConfiguration configuration)
{
_aesProvider = aesProvider;
_passwordProvider = passwordProvider;
_dbContext = dbContext;
_configuration = configuration;
}
public async Task<User> CreateUser(string email, string password, string ssn)
{
// Hash password
var (passwordHash, salt, iterations) = _passwordProvider.SecurePassword(password);
// Encrypt SSN
var encryptionKey = _configuration["Encryption:Key"];
var encryptionIV = _configuration["Encryption:IV"];
var encryptedSSN = _aesProvider.EncryptGCM(ssn, encryptionKey, encryptionIV);
var user = new User
{
Email = email,
PasswordHash = passwordHash,
PasswordSalt = salt,
PasswordIterations = iterations,
SocialSecurityNumber = encryptedSSN
};
_dbContext.Users.Add(user);
await _dbContext.SaveChangesAsync();
return user;
}
public async Task<User> GetUserWithDecryptedData(int userId)
{
var user = await _dbContext.Users.FindAsync(userId);
if (user != null)
{
// Decrypt SSN
var encryptionKey = _configuration["Encryption:Key"];
var encryptionIV = _configuration["Encryption:IV"];
user.SocialSecurityNumber = _aesProvider.DecryptGCM(
user.SocialSecurityNumber,
encryptionKey,
encryptionIV
);
}
return user;
}
}Example 2: Microservices Authentication
// Service A (Client) - Sending Request
public class OrderServiceClient
{
private readonly ISecurityRSASignatureProvider _signatureProvider;
private readonly HttpClient _httpClient;
public async Task<Order> CreateOrder(CreateOrderRequest orderRequest)
{
var payload = JsonSerializer.Serialize(orderRequest);
var signature = _signatureProvider.CreateSignature(payload, "shared-secret-key");
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
var request = new HttpRequestMessage(HttpMethod.Post,
"https://order-service/api/orders");
request.Headers.Add("X-Signature", signature);
request.Headers.Add("X-Timestamp", timestamp);
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<Order>();
}
}
// Service B (Server) - Receiving Request
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly ISecurityRSASignatureProvider _signatureProvider;
[HttpPost]
public async Task<IActionResult> CreateOrder([FromBody] JsonElement payload)
{
var signature = Request.Headers["X-Signature"].FirstOrDefault();
var timestamp = Request.Headers["X-Timestamp"].FirstOrDefault();
try
{
_signatureProvider.ValidateSignature(
signature,
payload.GetRawText(),
timestamp,
"shared-secret-key"
);
var order = JsonSerializer.Deserialize<CreateOrderRequest>(payload.GetRawText());
// Process order...
return Ok(new { orderId = 123 });
}
catch (InvalidSignatureException)
{
return Unauthorized();
}
}
}Example 3: Secure File Storage
public class SecureFileService
{
private readonly ISecurityAESProvider _aesProvider;
private readonly IConfiguration _configuration;
public SecureFileService(
ISecurityAESProvider aesProvider,
IConfiguration configuration)
{
_aesProvider = aesProvider;
_configuration = configuration;
}
public async Task<string> EncryptAndSaveFile(string content, string fileName)
{
// Encrypt content
var key = _configuration["Encryption:Key"];
var iv = _configuration["Encryption:IV"];
var encrypted = _aesProvider.EncryptGCM(content, key, iv);
// Calculate hash for integrity
var hash = _aesProvider.SHA256Hash(content);
// Save encrypted file
var encryptedPath = $"encrypted/{fileName}.enc";
await File.WriteAllTextAsync(encryptedPath, encrypted);
// Save hash separately
await File.WriteAllTextAsync($"encrypted/{fileName}.hash", hash);
return encryptedPath;
}
public async Task<string> DecryptFile(string encryptedPath)
{
// Read encrypted content
var encrypted = await File.ReadAllTextAsync(encryptedPath);
// Decrypt
var key = _configuration["Encryption:Key"];
var iv = _configuration["Encryption:IV"];
var decrypted = _aesProvider.DecryptGCM(encrypted, key, iv);
// Verify integrity
var hashPath = encryptedPath.Replace(".enc", ".hash");
var expectedHash = await File.ReadAllTextAsync(hashPath);
var actualHash = _aesProvider.SHA256Hash(decrypted);
if (actualHash != expectedHash)
{
throw new InvalidDataException("File integrity check failed");
}
return decrypted;
}
}Security Best Practices
DO
1. Store Keys Securely
// GOOD: Use Azure Key Vault or AWS Secrets Manager
var keyVaultUrl = configuration["KeyVault:Url"];
var secretClient = new SecretClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
var key = await secretClient.GetSecretAsync("AesEncryptionKey");
// GOOD: Use environment variables (for development)
var key = Environment.GetEnvironmentVariable("ENCRYPTION_KEY");2. Use Unique Salts
// GOOD: Auto-generated salt for each user
var (hash, salt, iterations) = _passwordProvider.SecurePassword(password);3. Validate Signatures
// GOOD: Always validate on sensitive endpoints
[HttpPost("transfer")]
public async Task<IActionResult> Transfer([FromBody] JsonElement payload)
{
_signatureProvider.ValidateSignature(/* ... */);
// Process transfer
}4. Use HTTPS
// GOOD: Enforce HTTPS
app.UseHttpsRedirection();
app.UseHsts();DON’T
1. Never Hardcode Keys
// BAD: Hardcoded keys
private const string KEY = "my-secret-key"; // NEVER DO THIS2. Never Reuse Salts
// BAD: Same salt for all users
var salt = "global-salt"; // NEVER DO THIS
var hash = _passwordProvider.SecurePassword(password, salt, 10000);3. Never Expose Crypto Details
// BAD: Exposing cryptographic errors
catch (CryptographicException ex)
{
return BadRequest(ex.Message); // NEVER DO THIS
}
// GOOD: Generic error
catch (CryptographicException ex)
{
_logger.LogError(ex, "Decryption failed");
return StatusCode(500, "Unable to process request");
}4. Never Skip Validation
// BAD: Processing without signature validation
[HttpPost("payment")]
public async Task<IActionResult> ProcessPayment([FromBody] PaymentRequest payment)
{
// Process without validation - NEVER DO THIS
}Configuration
appsettings.json
{
"Encryption": {
"Key": "#{FROM_KEYVAULT}#",
"IV": "#{FROM_KEYVAULT}#"
},
"Api": {
"HmacSecretKey": "#{FROM_KEYVAULT}#"
}
}Azure Key Vault Integration
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add Azure Key Vault
var keyVaultUrl = builder.Configuration["KeyVault:Url"];
builder.Configuration.AddAzureKeyVault(
new Uri(keyVaultUrl),
new DefaultAzureCredential()
);
// Register services
builder.Services.AddMaraiCryptography();Environment Variables
# .env or system variables
export ENCRYPTION_KEY="your-base64-encoded-key"
export ENCRYPTION_IV="your-base64-encoded-iv"
export HMAC_SECRET_KEY="your-secret-key"Exception Handling
Common Exceptions
| Exception | When Thrown | How to Handle |
|---|---|---|
InvalidSignatureException | Signature verification fails | Return 401 Unauthorized |
ExpiredSignatureException | Timestamp outside valid window | Return 401 with error message |
ParameterNotFoundException | Required parameter missing | Return 400 Bad Request |
InvalidParameterException | Parameter format invalid | Return 400 Bad Request |
CryptographicException | Encryption/decryption fails | Log error, return 500 |
Exception Handling Example
public async Task<IActionResult> SecureEndpoint([FromBody] JsonElement payload)
{
try
{
// Validate signature
_signatureProvider.ValidateSignature(
signature,
payload.GetRawText(),
timestamp,
secretKey
);
// Process request
return Ok(await ProcessRequest(payload));
}
catch (InvalidSignatureException)
{
_logger.LogWarning("Invalid signature from {IP}",
HttpContext.Connection.RemoteIpAddress);
return Unauthorized(new { error = "Invalid signature" });
}
catch (ExpiredSignatureException ex)
{
_logger.LogWarning("Expired signature: {Message}", ex.Message);
return Unauthorized(new { error = "Request expired" });
}
catch (ParameterNotFoundException ex)
{
return BadRequest(new { error = ex.Message });
}
catch (CryptographicException ex)
{
_logger.LogError(ex, "Cryptographic operation failed");
return StatusCode(500, new { error = "Unable to process request" });
}
}FAQ
Q: How do I generate encryption keys?
var (key, iv) = _aesProvider.GenerateAes256KeyAndIv();
Console.WriteLine($"Key: {key}");
Console.WriteLine($"IV: {iv}");
// Store these in Azure Key Vault or similarQ: Can I use custom password iteration counts?
Yes, create a custom provider:
var customProvider = new SecurityPasswordProvider(
maxSaltLength: 32,
minimumIterations: 100000,
maximumIterations: 150000,
maximumDataLength: 64
);
services.AddSingleton<ISecurityPasswordProvider>(customProvider);Q: What if signature validation keeps failing?
Check these common issues:
// 1. Verify secret keys match
Console.WriteLine($"Client Key: {clientSecretKey}");
Console.WriteLine($"Server Key: {serverSecretKey}");
// 2. Check timestamp is current
var timestamp = long.Parse(timestampString);
var requestTime = DateTimeOffset.FromUnixTimeSeconds(timestamp);
Console.WriteLine($"Request Time: {requestTime}");
Console.WriteLine($"Server Time: {DateTimeOffset.UtcNow}");
// 3. Use extended validation window for testing
_signatureProvider.ValidateSignature(
signature, payload, timestamp, secretKey,
allowedClockSkewInMinutes: 60,
allowedExpirationInMinutes: 60
);Q: How do I encrypt existing data in my database?
public async Task EncryptExistingUserData()
{
var users = await _dbContext.Users.ToListAsync();
var key = _configuration["Encryption:Key"];
var iv = _configuration["Encryption:IV"];
foreach (var user in users)
{
if (!_aesProvider.IsGcmFormat(user.SocialSecurityNumber))
{
// Data not encrypted yet
user.SocialSecurityNumber = _aesProvider.EncryptGCM(
user.SocialSecurityNumber,
key,
iv
);
}
}
await _dbContext.SaveChangesAsync();
}Q: Is this library thread-safe?
Yes, all providers are thread-safe and registered as singletons.
Q: What algorithms are used?
- Encryption: AES-256-GCM (NIST SP 800-38D)
- Password Hashing: PBKDF2 with SHA-256 (NIST SP 800-132)
- Signatures: HMAC-SHA256 (FIPS 198-1)
- Hashing: SHA-256 (FIPS 180-4)
License
This project is licensed under the Marai Proprietary Software License Agreement.
Free for personal and commercial use.
See LICENSE file or visit:
Proprietary Software License Agreement
Support
- Documentation: https://github.com/maraisystems/Marai.Cryptography
- NuGet: https://www.nuget.org/packages/Marai.Cryptography/
- Email: contact@marai.dev



