Marai Cryptography

NuGet License License

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

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

Bash
dotnet add package Marai.Cryptography

Using Package Manager Console

Bash
Install-Package Marai.Cryptography

Using PackageReference

Add to your .csproj file:

XML
<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
C#
// 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
C#
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
C#
// 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:

  • ISecurityAESProviderSecurityAESProvider
  • ISecurityPasswordProviderSecurityPasswordProvider
  • ISecurityRSASignatureProviderSecurityRSASignatureProvider

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
C#
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:

C#
string EncryptGCM(string plainText, string key, string initVector)

Parameters:

  • plainText – The text to encrypt
  • key – 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:

C#
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:

C#
string DecryptGCM(string encryptedText, string key, string initVector)

Parameters:

  • encryptedText – Base64-encoded encrypted data
  • key – Decryption key (must match encryption key)
  • initVector – Initialization vector (must match encryption IV)

Returns:

  • Decrypted plaintext

Throws:

  • CryptographicException – Invalid key, IV, or tampered data
  • FormatException – Invalid Base64 encoding

Example:

C#
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:

C#
(string KeyBase64, string IvBase64) GenerateAes256KeyAndIv()

Returns:

  • Tuple containing Base64-encoded key (32 bytes) and IV (16 bytes)

Example:

C#
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:

C#
string SHA256Hash(string text)

Parameters:

  • text – The text to hash

Returns:

  • Hexadecimal hash string (64 characters)

Example:

C#
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:

C#
bool IsGcmFormat(string encryptedText)

Parameters:

  • encryptedText – Encrypted data to validate

Returns:

  • true if valid GCM format, false otherwise

Example:

C#
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
C#
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:

C#
(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:

C#
[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:

C#
string SecurePassword(string password, string salt, int iterations)

Parameters:

  • password – The password to hash
  • salt – Base64-encoded salt
  • iterations – PBKDF2 iteration count

Returns:

  • Base64-encoded hashed password

Example – Password Verification:

C#
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:

C#
bool IsValidPassword(string hashedPassword, string password, string salt, int iterations)

Parameters:

  • hashedPassword – The stored password hash
  • password – The password to verify
  • salt – The stored salt
  • iterations – The stored iteration count

Returns:

  • true if password matches, false otherwise

Example – User Login:

C#
[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
C#
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:

C#
string CreateSignature(string requestPayload, string hmacSecretKey)

Parameters:

  • requestPayload – JSON payload to sign
  • hmacSecretKey – Secret key for HMAC computation

Returns:

  • Base64-encoded HMAC-SHA256 signature

Example – API Client:

C#
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:

C#
void ValidateSignature(string requestSignature, string requestPayload, 
    string requestHeaderTimeStamp, string hmacSecretKey)

Parameters:

  • requestSignature – The signature to validate
  • requestPayload – The JSON payload that was signed
  • requestHeaderTimeStamp – Unix timestamp (seconds) as string
  • hmacSecretKey – Secret key for verification

Throws:

  • InvalidSignatureException – Signature verification failed
  • ExpiredSignatureException – Timestamp outside valid window
  • ParameterNotFoundException – Timestamp is missing
  • InvalidParameterException – Timestamp format invalid

Example – API Endpoint:

C#
[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:

C#
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:

C#
[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
C#
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
C#
// 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
C#
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

C#
// 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

C#
// GOOD: Auto-generated salt for each user
var (hash, salt, iterations) = _passwordProvider.SecurePassword(password);

3. Validate Signatures

C#
// GOOD: Always validate on sensitive endpoints
[HttpPost("transfer")]
public async Task<IActionResult> Transfer([FromBody] JsonElement payload)
{
    _signatureProvider.ValidateSignature(/* ... */);
    // Process transfer
}

4. Use HTTPS

C#
// GOOD: Enforce HTTPS
app.UseHttpsRedirection();
app.UseHsts();
DON’T

1. Never Hardcode Keys

C#
// BAD: Hardcoded keys
private const string KEY = "my-secret-key";  // NEVER DO THIS

2. Never Reuse Salts

C#
// 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

C#
// 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

C#
// BAD: Processing without signature validation
[HttpPost("payment")]
public async Task<IActionResult> ProcessPayment([FromBody] PaymentRequest payment)
{
    // Process without validation - NEVER DO THIS
}

Configuration

appsettings.json
JSON
{
  "Encryption": {
    "Key": "#{FROM_KEYVAULT}#",
    "IV": "#{FROM_KEYVAULT}#"
  },
  "Api": {
    "HmacSecretKey": "#{FROM_KEYVAULT}#"
  }
}
Azure Key Vault Integration
C#
// 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
Bash
# .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
ExceptionWhen ThrownHow to Handle
InvalidSignatureExceptionSignature verification failsReturn 401 Unauthorized
ExpiredSignatureExceptionTimestamp outside valid windowReturn 401 with error message
ParameterNotFoundExceptionRequired parameter missingReturn 400 Bad Request
InvalidParameterExceptionParameter format invalidReturn 400 Bad Request
CryptographicExceptionEncryption/decryption failsLog error, return 500
Exception Handling Example
Bash
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?
Bash
var (key, iv) = _aesProvider.GenerateAes256KeyAndIv();
Console.WriteLine($"Key: {key}");
Console.WriteLine($"IV: {iv}");
// Store these in Azure Key Vault or similar
Q: Can I use custom password iteration counts?

Yes, create a custom provider:

Bash
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:

Bash
// 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?
Bash
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