Feedemy.KeyManagement 2.5.2

Additional Details

Memory leaks were detected in these packages and fixed in version 2.5.5. It doesn't affect you if you're not calling more than 10,000 keys per minute. An easier setup has been added to address difficulties with extension registration.

There is a newer version of this package available.
See the version list below for details.
dotnet add package Feedemy.KeyManagement --version 2.5.2
                    
NuGet\Install-Package Feedemy.KeyManagement -Version 2.5.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Feedemy.KeyManagement" Version="2.5.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Feedemy.KeyManagement" Version="2.5.2" />
                    
Directory.Packages.props
<PackageReference Include="Feedemy.KeyManagement" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Feedemy.KeyManagement --version 2.5.2
                    
#r "nuget: Feedemy.KeyManagement, 2.5.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Feedemy.KeyManagement@2.5.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Feedemy.KeyManagement&version=2.5.2
                    
Install as a Cake Addin
#tool nuget:?package=Feedemy.KeyManagement&version=2.5.2
                    
Install as a Cake Tool

Feedemy.KeyManagement

NuGet License .NET

Enterprise-grade key management library for .NET applications with versioned encryption, automatic rotation, distributed caching, and comprehensive audit logging.

Overview

Feedemy.KeyManagement provides a production-ready solution for managing cryptographic keys and sensitive configuration in .NET applications. It combines platform-specific secure storage with advanced features like automatic key rotation, version management, distributed caching, health monitoring, and full audit trails.

Key Features

  • Versioned Encryption Format (v2.3.0+): Auto-decryption with version detection, backward compatibility
  • Generic String-Based Keys: No hardcoded key names - fully application-agnostic
  • Automatic Key Rotation: Background service with configurable intervals and health monitoring
  • Asymmetric Key Support: RSA (2048/3072/4096-bit) and ECDSA (P-256/P-384/P-521) for signing and encryption
  • Multi-Platform Secure Storage: Windows DPAPI, Linux AES-256-GCM, Azure Key Vault, InMemory
  • High-Performance Caching: Redis/Memory cache with multi-layer strategy (< 1ms cache hits)
  • Distributed Cache Invalidation: Pub/sub pattern for multi-server synchronization
  • Database Persistence: SQL Server and PostgreSQL support with automatic migrations
  • Master Key Encryption: Envelope encryption for sensitive keys
  • Health Monitoring: Built-in ASP.NET Core health checks
  • Fallback Storage: Multi-provider redundancy with automatic synchronization
  • Audit Trail: Complete audit logging for compliance
  • Roslyn Analyzers: 48 compile-time rules for security and best practices
  • OpenTelemetry Integration: Metrics, traces, and structured logging

Production-Ready

  • 100% Test Coverage: 450+ unit tests, 120+ integration tests
  • Benchmark Tested: < 1ms cached retrieval, < 100ms rotation operations
  • Battle-Tested: Used in production environments
  • .NET 10.0: Latest framework support

Quick Start

Installation

# Core library
dotnet add package Feedemy.KeyManagement

# Persistence providers (choose one)
dotnet add package Feedemy.KeyManagement.Providers.EntityFramework  # SQL Server
dotnet add package Feedemy.KeyManagement.Providers.Npgsql          # PostgreSQL

# Optional: Analyzers for compile-time checks
dotnet add package Feedemy.KeyManagement.Analyzers

Initial Setup (First Time)

Step 1: Create keys.json (Key Definitions)

Create a keys.json file to define which keys should be auto-generated on first run:

{
  "Keys": [
    {
      "KeyName": "EncryptionKey",
      "KeyType": "Symmetric",
      "KeySize": 256,
      "RotationIntervalDays": 90,
      "Category": "MasterKey",
      "Description": "Master encryption key for envelope encryption",
      "AutoRotationEnabled": true,
      "RequiresMasterKeyEncryption": false
    },
    {
      "KeyName": "JwtSigningKey",
      "KeyType": "RSA",
      "KeySize": 2048,
      "RotationIntervalDays": 180,
      "Category": "Signing",
      "Description": "RSA key for JWT RS256 tokens",
      "AutoRotationEnabled": true,
      "RequiresMasterKeyEncryption": true
    },
    {
      "KeyName": "ApiEncryptionKey",
      "KeyType": "Symmetric",
      "KeySize": 256,
      "RotationIntervalDays": 90,
      "Category": "Encryption",
      "Description": "AES-256 key for API payload encryption",
      "AutoRotationEnabled": true,
      "RequiresMasterKeyEncryption": true
    },
    {
      "KeyName": "DocumentSigningKey",
      "KeyType": "ECDSA",
      "KeySize": 256,
      "RotationIntervalDays": 365,
      "Category": "Signing",
      "Description": "ECDSA P-256 key for document signatures",
      "AutoRotationEnabled": false,
      "RequiresMasterKeyEncryption": true
    }
  ]
}

Key Definition Fields:

Field Type Description
KeyName string Unique key identifier
KeyType string Symmetric, RSA, or ECDSA
KeySize int 256 (AES), 2048/3072/4096 (RSA), 256/384/521 (ECDSA)
RotationIntervalDays int Days between automatic rotations
Category string Encryption, Signing, ApiKey, Secret, MasterKey
SourceType string Generated (auto), External (provide content)
InitialContent string For external keys: plain text, hex, or base64:...
RequiresMasterKeyEncryption bool Encrypt with master key (recommended for sensitive keys)

Step 2: Create azure-credentials.json (Azure Key Vault)

For Azure Key Vault storage, create azure-credentials.json:

{
  "TenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "ClientSecret": "your-client-secret-here"
}

How to get these values:

  1. Go to Azure Portal → Azure Active Directory → App registrations
  2. Create new registration or use existing
  3. Copy Application (client) IDClientId
  4. Copy Directory (tenant) IDTenantId
  5. Go to Certificates & secrets → New client secret → Copy value → ClientSecret
  6. Go to your Key Vault → Access policies → Add access policy for this app

Security Note: On first run, credentials are imported to platform-secure storage:

  • Windows: Credential Manager (DPAPI protected)
  • Linux: Secret Service / Keyring

After successful import, delete azure-credentials.json for security.

Step 3: Configure appsettings.json

{
  "KeyManagement": {
    "EnableAutoRotation": true,
    "RotationCheckInterval": "06:00:00",
    "DefaultRotationDays": 90,
    "MasterEncryptionKeyName": "EncryptionKey",

    "Storage": {
      "ProviderType": "AzureKeyVault",
      "AzureKeyVaultUrl": "https://your-vault.vault.azure.net/",
      "AzureCredentialsJsonPath": "azure-credentials.json"
    },

    "Cache": {
      "ProviderType": "Redis",
      "RedisConnectionString": "localhost:6379"
    },

    "Initialization": {
      "EnableAutoInitialization": true,
      "ExternalKeysJsonPath": "keys.json"
    }
  },

  "ConnectionStrings": {
    "KeyManagement": "Server=localhost;Database=MyApp;Trusted_Connection=true;"
  }
}

Basic Setup (Development)

using Feedemy.KeyManagement.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add KeyManagement with in-memory providers (development only)
builder.Services.AddKeyManagement(options =>
{
    options.EnableAutoRotation = true;
    options.RotationCheckInterval = TimeSpan.FromHours(6);
    options.DefaultRotationDays = 90;
});

var app = builder.Build();
app.Run();

Production Setup (SQL Server + Redis + Azure Key Vault)

using Feedemy.KeyManagement.Extensions;
using StackExchange.Redis;

var builder = WebApplication.CreateBuilder(args);

// Configure Redis
builder.Services.AddSingleton<IConnectionMultiplexer>(
    ConnectionMultiplexer.Connect("localhost:6379"));

// Add KeyManagement with production providers
builder.Services.AddKeyManagement(options =>
{
    options.EnableAutoRotation = true;
    options.RotationCheckInterval = TimeSpan.FromHours(6);
    options.DefaultRotationDays = 90;

    // Storage: Azure Key Vault
    options.Storage.ProviderType = StorageProviderType.AzureKeyVault;
    options.Storage.AzureKeyVaultUrl = "https://your-vault.vault.azure.net/";

    // Cache: Redis with distributed invalidation
    options.Cache.ProviderType = CacheProviderType.Redis;
    options.Cache.RedisConnectionString = "localhost:6379";
})
.UseEntityFramework()  // SQL Server persistence
.AddHealthChecks();     // ASP.NET Core health checks

// Add health check endpoint
builder.Services.AddHealthChecks()
    .AddKeyManagementHealthCheck(
        name: "key_management",
        failureStatus: HealthStatus.Unhealthy,
        tags: new[] { "security", "keys" });

var app = builder.Build();
app.MapHealthChecks("/health");
app.Run();

Basic Usage

public class MyService
{
    private readonly IKeyManagementService _keyService;
    private readonly IKeyManagementAdminService _adminService;

    public MyService(
        IKeyManagementService keyService,
        IKeyManagementAdminService adminService)
    {
        _keyService = keyService;
        _adminService = adminService;
    }

    // Create a new encryption key (admin operation)
    public async Task CreateEncryptionKeyAsync()
    {
        var result = await _adminService.CreateKeyAsync(new CreateKeyRequest
        {
            KeyName = "MyEncryptionKey",
            Description = "AES-256 encryption key for sensitive data",
            AutoGenerate = true,
            KeySize = 32, // 256 bits
            RotationIntervalDays = 90,
            AutoRotationEnabled = true,
            MaxVersionsToRetain = 5,
            Category = KeyCategory.Encryption,
            SourceType = KeySourceType.Generated,
            CreatedBy = "Admin"
        });

        if (result.Success)
        {
            Console.WriteLine($"Key created: {result.Data.KeyName} v{result.Data.CurrentVersion}");
        }
    }

    // Retrieve key (cache-first, extremely fast)
    public async Task<byte[]> GetEncryptionKeyAsync()
    {
        return await _keyService.RetrieveKeyAsync("MyEncryptionKey");
    }

    // Get key metadata
    public async Task<KeyMetadata> GetKeyInfoAsync()
    {
        var metadata = await _keyService.GetKeyMetadataAsync("MyEncryptionKey");
        Console.WriteLine($"Current Version: {metadata.CurrentVersion}");
        Console.WriteLine($"Next Rotation: {metadata.NextRotationDue}");
        Console.WriteLine($"Auto Rotation: {metadata.AutoRotationEnabled}");
        return metadata;
    }

    // Check key health
    public async Task MonitorKeyHealthAsync()
    {
        var health = await _keyService.GetKeyHealthAsync("MyEncryptionKey");

        if (health.Status == KeyHealthStatus.NeedsRotation)
        {
            Console.WriteLine("WARNING: Key needs immediate rotation!");
        }
        else if (health.Status == KeyHealthStatus.Warning)
        {
            Console.WriteLine($"Key approaching rotation: {health.DaysUntilRotation} days remaining");
        }
    }
}

Core Features

1. Generic String-Based Keys (v3.0+)

The library is fully generic - no hardcoded key names. Applications define their own key names as strings.

// Create any key with any name
await adminService.CreateKeyAsync(new CreateKeyRequest
{
    KeyName = "MyCustomKey",  // Any string you want
    Description = "Application-specific key",
    AutoGenerate = true,
    KeySize = 32,
    RotationIntervalDays = 90,
    CreatedBy = "Admin"
});

// Retrieve by name
var key = await keyService.RetrieveKeyAsync("MyCustomKey");

Migration from v2.x: If you used hardcoded KeyName enums, replace them with strings:

  • KeyName.EncryptionKey"EncryptionKey"
  • keyName.ToKeyString() → Direct string usage

2. Versioned Encryption Format

Auto-decryption with version detection for backward compatibility.

// Encrypt data (automatically adds version header)
var encryptedData = await encryptionService.EncryptAsync(plaintext, "MyKey");

// Decrypt (auto-detects version and decrypts accordingly)
var decryptedData = await encryptionService.DecryptAsync(encryptedData, "MyKey");
// Works even if key has been rotated - automatically uses correct version

Format: [VERSION_BYTE][ENCRYPTED_CONTENT]

  • Version 1 (legacy): No version byte
  • Version 2 (v2.3.0+): 0x01 prefix + versioned encryption

3. Asymmetric Key Support (RSA & ECDSA)

Full support for digital signatures, JWT signing, and public key encryption.

Creating Asymmetric Keys
// RSA key for JWT signing
var rsaResult = await adminService.CreateKeyAsync(new CreateKeyRequest
{
    KeyName = "JwtSigningKey",
    KeyType = KeyType.AsymmetricRSA,
    RsaKeySize = RsaKeySize.Rsa2048,
    Category = KeyCategory.Signing,
    Description = "RSA key for JWT RS256 tokens",
    AutoRotationEnabled = true,
    RotationIntervalDays = 180,
    CreatedBy = "System"
});

// ECDSA key for document signing
var ecdsaResult = await adminService.CreateKeyAsync(new CreateKeyRequest
{
    KeyName = "DocumentSigningKey",
    KeyType = KeyType.AsymmetricECDSA,
    EcdsaCurve = EcdsaCurve.P256,
    Category = KeyCategory.Signing,
    Description = "ECDSA key for document signatures",
    CreatedBy = "Admin"
});
Using Asymmetric Keys
public class JwtService
{
    private readonly IAsymmetricKeyOperations _asymmetricOps;

    public JwtService(IAsymmetricKeyOperations asymmetricOps)
    {
        _asymmetricOps = asymmetricOps;
    }

    // Sign JWT token
    public async Task<string> CreateJwtTokenAsync(Dictionary<string, object> claims)
    {
        var payload = JsonSerializer.SerializeToUtf8Bytes(claims);

        // Sign with RS256
        var signature = await _asymmetricOps.SignAsync(
            "JwtSigningKey",
            payload,
            HashAlgorithmName.SHA256);

        return EncodeJwt(payload, signature);
    }

    // Verify JWT token
    public async Task<bool> VerifyJwtTokenAsync(string token)
    {
        var (payload, signature) = DecodeJwt(token);

        // Verify using cached public key (very fast - ~0.15ms)
        return await _asymmetricOps.VerifyAsync(
            "JwtSigningKey",
            payload,
            signature,
            HashAlgorithmName.SHA256);
    }

    // Export public key for clients
    public async Task<string> GetPublicKeyPemAsync()
    {
        // Safe to expose publicly
        return await _asymmetricOps.GetPublicKeyPemAsync("JwtSigningKey");
    }
}

Supported Algorithms:

Algorithm Key Sizes Use Case Performance
RSA 2048, 3072, 4096-bit Signing, Encryption Sign: ~1.2ms, Verify: ~0.15ms
ECDSA P-256, P-384, P-521 Signing (compact) Sign: ~0.35ms, Verify: ~0.12ms

4. Automatic Key Rotation

Background service automatically rotates keys based on configured intervals.

// Enable in configuration
builder.Services.AddKeyManagement(options =>
{
    options.EnableAutoRotation = true;
    options.RotationCheckInterval = TimeSpan.FromHours(6);
    options.DefaultRotationDays = 90;
});

// Per-key configuration
await adminService.CreateKeyAsync(new CreateKeyRequest
{
    KeyName = "ApiKey",
    RotationIntervalDays = 90,      // Rotate every 90 days
    AutoRotationEnabled = true,     // Enable auto-rotation
    MaxVersionsToRetain = 5,        // Keep last 5 versions
    // ...
});

// Monitor rotation status
var status = await adminService.GetRotationServiceStatusAsync();
Console.WriteLine($"Keys Pending Rotation: {status.KeysPendingRotation.Count}");

Rotation Flow:

  1. Background service checks every RotationCheckInterval
  2. Identifies keys where NextRotationDue <= Now
  3. Generates new key content
  4. Saves to storage and database
  5. Invalidates all cache layers
  6. Triggers cache warming
  7. Logs audit entry

5. Health Monitoring

Built-in ASP.NET Core health checks for monitoring system health.

// Add health checks
builder.Services.AddHealthChecks()
    .AddKeyManagementHealthCheck(
        name: "key_management",
        failureStatus: HealthStatus.Unhealthy,
        tags: new[] { "security", "keys" });

// Map endpoint
app.MapHealthChecks("/health");

Health Status Levels:

  • Healthy: All keys operational, > 30 days until rotation
  • Degraded: Warning keys detected (7-30 days until rotation)
  • Unhealthy: Critical keys (rotation overdue) or system failure

Response Example:

{
  "status": "Healthy",
  "totalKeys": 15,
  "healthyKeys": 13,
  "warningKeys": 2,
  "criticalKeys": 0,
  "storageProvider": "Azure Key Vault",
  "storageAvailable": true,
  "checks": {
    "key_management": {
      "status": "Healthy",
      "description": "Key management system is healthy"
    }
  }
}

6. Distributed Caching with Redis

Multi-layer caching with distributed invalidation for multi-server deployments.

// Configure Redis
builder.Services.AddSingleton<IConnectionMultiplexer>(
    ConnectionMultiplexer.Connect("localhost:6379"));

builder.Services.AddKeyManagement(options =>
{
    options.Cache.ProviderType = CacheProviderType.Redis;
    options.Cache.RedisConnectionString = "localhost:6379";
});

// Configure cache invalidation options
builder.Services.Configure<KeyManagementCacheOptions>(options =>
{
    options.EnableDistributedInvalidation = true;
    options.InvalidationChannel = "keymanagement:invalidation";
    options.EnableCacheWarming = true;
    options.MaxConcurrentWarmingTasks = 5;
});

Cache Layers:

  1. Content Cache: key:{KeyName}:content:v{Version} (1-hour TTL)
  2. Metadata Cache: key:{KeyName}:metadata (5-min TTL)
  3. Version Cache: key:{KeyName}:versions (5-min TTL)

Invalidation Flow:

  1. Server 1 rotates key
  2. Publishes invalidation message to Redis pub/sub
  3. All servers receive notification
  4. Each server clears local cache
  5. Cache warming preloads critical keys

7. Database Persistence

SQL Server and PostgreSQL support with automatic migrations.

SQL Server (EntityFramework)
builder.Services.AddKeyManagement(options => { /* ... */ })
    .UseEntityFramework();

// Migrations are applied automatically on startup
// Tables created: KeyMetadata, KeyVersions, KeyAuditLogs, SchemaVersion
PostgreSQL (Npgsql)
builder.Services.AddKeyManagement(options => { /* ... */ })
    .UseNpgsql("Host=localhost;Database=mydb;Username=user;Password=pass");

// Automatic migrations for PostgreSQL

Database Tables:

  • KeyManagement.KeyMetadata: Key configuration and current version
  • KeyManagement.KeyVersions: Version history with content hashes
  • KeyManagement.KeyAuditLogs: Complete audit trail
  • KeyManagement.SchemaVersion: Migration tracking

8. Platform-Specific Storage

Automatic selection of the best storage provider for your platform.

Windows DPAPI
builder.Services.AddKeyManagement(options =>
{
    options.Storage.ProviderType = StorageProviderType.WindowsCredentialManager;
});
  • Location: %LOCALAPPDATA%\Feedemy\Keys
  • Encryption: Windows Data Protection API (DPAPI)
  • Scope: CurrentUser
  • Security: Protected by Windows ACLs + DPAPI encryption
Linux AES-256-GCM
builder.Services.AddKeyManagement(options =>
{
    options.Storage.ProviderType = StorageProviderType.LinuxKeyring;
});
  • Location: ~/.feedemy-keys/
  • Encryption: AES-256-GCM with machine-specific key
  • Key Derivation: PBKDF2-SHA256 (100,000 iterations) from /etc/machine-id
  • File Permissions: 600 (owner read/write only)
Azure Key Vault
builder.Services.AddKeyManagement(options =>
{
    options.Storage.ProviderType = StorageProviderType.AzureKeyVault;
    options.Storage.AzureKeyVaultUrl = "https://your-vault.vault.azure.net/";
});
  • Authentication: DefaultAzureCredential (Managed Identity or Client Secret)
  • Retry Policy: 3 attempts with exponential backoff
  • Security: Managed by Azure, hardware security modules (HSM) optional
Auto-Detection
builder.Services.AddKeyManagement(options =>
{
    options.Storage.ProviderType = StorageProviderType.Auto; // Default
});

Automatically selects Windows DPAPI on Windows, Linux AES-GCM on Linux.

9. Fallback Storage

Multi-provider redundancy with automatic synchronization.

builder.Services.AddKeyManagement(options =>
{
    options.Storage.ProviderType = StorageProviderType.Fallback;
    options.Storage.Fallback.PrimaryProvider = StorageProviderType.AzureKeyVault;
    options.Storage.Fallback.FallbackProvider = StorageProviderType.WindowsCredentialManager;
    options.Storage.Fallback.EnableAutoSync = true;
    options.Storage.Fallback.SyncOnStartup = true;
});

Benefits:

  • Automatic failover if primary storage fails
  • Background synchronization between providers
  • Zero-downtime migrations

10. Master Key Encryption

Envelope encryption for sensitive keys.

// Create master encryption key
await adminService.CreateKeyAsync(new CreateKeyRequest
{
    KeyName = "EncryptionKey",  // Master key
    AutoGenerate = true,
    KeySize = 32,
    RotationIntervalDays = 90,
    CreatedBy = "System"
});

// Create encrypted key
await adminService.CreateKeyAsync(new CreateKeyRequest
{
    KeyName = "DatabaseEncryptionKey",
    RequiresMasterKeyEncryption = true,  // Encrypted with master key
    AutoGenerate = true,
    KeySize = 32,
    CreatedBy = "System"
});

Re-encryption after master key rotation:

// Detect version mismatches
var mismatches = await adminService.GetEnvKeysWithVersionMismatchAsync();

// Re-encrypt automatically handled by rotation service

Advanced Features

Version Management

// Get all versions
var versions = await keyService.GetKeyVersionsAsync("ApiKey");
foreach (var version in versions)
{
    Console.WriteLine($"Version {version.Version}: {version.Status}");
}

// Retrieve specific version
var oldKey = await keyService.RetrieveKeyByVersionAsync("ApiKey", version: 3);

// Rollback to previous version
var rollbackResult = await adminService.RollbackToVersionAsync(
    keyName: "ApiKey",
    targetVersion: 3,
    reason: "Revert after incident",
    performedBy: "Admin");

Audit Logging

// Get audit history
var logs = await adminService.GetAuditLogsAsync(
    keyName: "ApiKey",
    fromDate: DateTime.UtcNow.AddMonths(-1));

foreach (var log in logs)
{
    Console.WriteLine($"{log.Timestamp}: {log.Action} by {log.PerformedBy}");
    Console.WriteLine($"  Reason: {log.Reason}");
}

Audit Operations Tracked:

  • KeyCreated
  • KeyUpdated
  • KeyRotated
  • KeyDeactivated
  • KeyReactivated
  • VersionDeactivated
  • VersionReactivated
  • RollbackPerformed
  • ConfigurationUpdated

Cache Warming

// Configure cache warming
builder.Services.Configure<KeyManagementCacheOptions>(options =>
{
    options.EnableCacheWarming = true;
    options.MaxConcurrentWarmingTasks = 5;
});

// Manually warm cache for specific keys
await keyService.WarmCacheAsync(new[] { "ApiKey", "JwtSigningKey" });

Roslyn Analyzers

48 compile-time rules for security and best practices.

dotnet add package Feedemy.KeyManagement.Analyzers

Example Rules:

  • KM0001: Empty key name (Error)
  • KM0002: Invalid rotation interval (Warning)
  • KM0101: Hardcoded key content (Warning)
  • KM0103: Key exposure in logs (Warning)
  • KM0104: Insecure storage in production (Warning)
  • KM0203: Key retrieval in loop (Warning)

Disable specific rules:

#pragma warning disable KM0101
var key = new byte[] { ... };
#pragma warning restore KM0101

See ANALYZER_RULES.md for complete list.

Configuration

KeyManagementOptions

builder.Services.AddKeyManagement(options =>
{
    // Auto-rotation
    options.EnableAutoRotation = true;
    options.RotationCheckInterval = TimeSpan.FromHours(6);
    options.DefaultRotationDays = 90;

    // Master key name (default: "EncryptionKey")
    options.MasterEncryptionKeyName = "EncryptionKey";

    // Storage
    options.Storage.ProviderType = StorageProviderType.Auto;
    options.Storage.AzureKeyVaultUrl = "https://your-vault.vault.azure.net/";

    // Cache
    options.Cache.ProviderType = CacheProviderType.Redis;
    options.Cache.RedisConnectionString = "localhost:6379";

    // Persistence
    options.Persistence.ProviderType = PersistenceProviderType.EntityFramework;

    // Initialization
    options.Initialization.EnableAutoInitialization = true;
    options.Initialization.ExternalKeysJsonPath = "keys.json";
});

KeyManagementCacheOptions

builder.Services.Configure<KeyManagementCacheOptions>(options =>
{
    // Distributed invalidation
    options.EnableDistributedInvalidation = true;
    options.InvalidationChannel = "keymanagement:invalidation";
    options.PubSubTimeoutSeconds = 5;

    // Cache warming
    options.EnableCacheWarming = true;
    options.MaxConcurrentWarmingTasks = 5;

    // Retry
    options.MaxInvalidationRetries = 3;
    options.RetryDelayMilliseconds = 100;
});

Environment Variables

# Core settings
KeyManagement__EnableAutoRotation=true
KeyManagement__RotationCheckInterval=06:00:00
KeyManagement__DefaultRotationDays=90

# Storage
KeyManagement__Storage__ProviderType=AzureKeyVault
KeyManagement__Storage__AzureKeyVaultUrl=https://your-vault.vault.azure.net/

# Cache
KeyManagement__Cache__ProviderType=Redis
KeyManagement__Cache__RedisConnectionString=localhost:6379

# Cache options
KeyManagementCache__EnableDistributedInvalidation=true
KeyManagementCache__InvalidationChannel=keymanagement:invalidation

Performance

Benchmark Results (BenchmarkDotNet)

| Method                     | Mean       | Allocated |
|---------------------------|------------|-----------|
| RetrieveKey_Cached        | 0.85 μs    | 96 B      |
| RetrieveKey_CacheMiss     | 12.3 ms    | 1.2 KB    |
| GetMetadata_Cached        | 0.62 μs    | 48 B      |
| GetCurrentVersion_Cached  | 0.58 μs    | 32 B      |
| CreateKey                 | 85.2 ms    | 4.5 KB    |
| RotateKey                 | 95.7 ms    | 5.2 KB    |
| SignData_RSA2048          | 1.2 ms     | 512 B     |
| VerifySignature_RSA2048   | 0.15 ms    | 256 B     |
| SignData_ECDSA_P256       | 0.35 ms    | 384 B     |
| VerifySignature_ECDSA_P256| 0.12 ms    | 192 B     |

Run benchmarks:

cd benchmarks/Feedemy.KeyManagement.Benchmarks
dotnet run -c Release

Testing

Unit Tests (450+)

cd tests/Feedemy.KeyManagement.Tests
dotnet test

Coverage:

  • Core services (KeyManagementService, KeyManagementAdminService)
  • Domain services (Health, Validation, Versioning, Encryption)
  • Cache services (Multi-layer, Invalidation, Warming)
  • Storage coordination
  • Repository patterns
  • Background services

Integration Tests (120+)

cd tests/Feedemy.KeyManagement.IntegrationTests
dotnet test

Coverage:

  • End-to-end workflows
  • Cache invalidation propagation
  • Automatic rotation
  • Rollback scenarios
  • Provider integration (SQL Server, PostgreSQL, Redis, Azure KV)
  • Asymmetric key operations (RSA, ECDSA)

Architecture

See ARCHITECTURE.md for detailed architecture diagrams, component descriptions, and data flow.

High-Level Architecture

┌─────────────────────────────────────────────────────────┐
│                   APPLICATION LAYER                     │
│         Controllers / Services / Background Jobs        │
└──────────────────────┬──────────────────────────────────┘
                       │
        ┌──────────────┴──────────────┐
        │                             │
┌───────▼─────────┐         ┌─────────▼────────────┐
│ IKeyManagement  │         │ IKeyManagementAdmin  │
│ Service         │         │ Service              │
│ (Read Ops)      │         │ (Write Ops)          │
└───────┬─────────┘         └─────────┬────────────┘
        │                             │
        └──────────────┬──────────────┘
                       │
┌──────────────────────▼──────────────────────────────────┐
│              DOMAIN SERVICES LAYER                      │
│  ┌────────────┐ ┌────────────┐ ┌────────────┐          │
│  │ Health     │ │ Validation │ │ Versioning │          │
│  │ Calculator │ │ Service    │ │ Service    │          │
│  └────────────┘ └────────────┘ └────────────┘          │
│  ┌────────────┐ ┌────────────┐ ┌────────────┐          │
│  │ Encryption │ │ Rotation   │ │ Storage    │          │
│  │Orchestrator│ │ Service    │ │Coordinator │          │
│  └────────────┘ └────────────┘ └────────────┘          │
└──────────────────────┬──────────────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────────────┐
│         INFRASTRUCTURE SERVICES LAYER                   │
│  ┌────────────┬────────────┬────────────┐              │
│  │ Cache      │ Storage    │ Persistence│              │
│  │ - Redis    │ - DPAPI    │ - SQL      │              │
│  │ - Memory   │ - Azure KV │ - Postgres │              │
│  │ - Invalidation│ - Linux AES│ - InMemory│            │
│  └────────────┴────────────┴────────────┘              │
└─────────────────────────────────────────────────────────┘

Migration Guides

From v2.x to v3.0

Breaking Changes:

  • Removed KeyName enum - use strings instead
  • Removed ToKeyString() and ToKeyName() extension methods
  • WarmCriticalKeysAsync() now requires string[] parameter

Migration Steps:

// Before (v2.x)
await keyService.RetrieveKeyAsync(KeyName.EncryptionKey);
var keyString = KeyName.EncryptionKey.ToKeyString();

// After (v3.0)
await keyService.RetrieveKeyAsync("EncryptionKey");
var keyString = "EncryptionKey"; // Direct string usage

See MIGRATION_GUIDE.md for complete migration instructions.

Documentation

NuGet Packages

Package Version Description
Feedemy.KeyManagement 2.5.2 Core library
Feedemy.KeyManagement.Providers.EntityFramework 2.5.2 SQL Server persistence
Feedemy.KeyManagement.Providers.Npgsql 2.5.2 PostgreSQL persistence
Feedemy.KeyManagement.Analyzers 2.5.2 Roslyn analyzers

Roadmap

  • HashiCorp Vault provider
  • AWS Secrets Manager provider
  • Multi-region replication
  • Key import/export utilities
  • Grafana dashboard templates
  • Performance profiling tools

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for new features
  4. Ensure all tests pass: dotnet test
  5. Submit a pull request

Code Style:

  • Follow .NET coding conventions
  • Use nullable reference types
  • Add XML documentation for public APIs
  • Write unit tests for business logic
  • Write integration tests for workflows

License

Dual License - see LICENSE file for details.

  • Personal & Educational Use: FREE
  • Commercial Use: Requires paid license

Copyright (c) 2025 Feedemy

For commercial licensing inquiries: licensing@feedemy.com

Support

Acknowledgments

Built with:

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Feedemy.KeyManagement:

Package Downloads
Feedemy.KeyManagement.Providers.Npgsql

PostgreSQL (Npgsql) persistence provider for Feedemy.KeyManagement. Provides PostgreSQL storage for key metadata, versions, and audit logs with migrations support. Platform-independent alternative to SQL Server.

Feedemy.KeyManagement.Providers.EntityFramework

Entity Framework Core persistence provider for Feedemy.KeyManagement. Provides SQL Server storage for key metadata, versions, and audit logs with migrations support. Fully tested with 56/56 integration tests passing.

Feedemy.KeyManagement.Providers.Sqlite

SQLite persistence provider for Feedemy.KeyManagement. Provides lightweight, file-based storage for key metadata, versions, and audit logs. Ideal for development, testing, and single-server deployments.

GitHub repositories

This package is not used by any popular GitHub repositories.

v3.0.0 - Generic Library Refactor (BREAKING CHANGES)

BREAKING CHANGES:
- Removed KeyName enum - library is now fully generic and string-based
- Removed all KeyName enum overloads from interfaces (IKeyManagementService, IKeyManagementAdminService, IKeyRotationService)
- Removed KeyNameExtensions (ToKeyString(), ToKeyName())
- Removed KeyConfigurationExtensions with application-specific key configurations
- Applications must now provide their own key names as strings
- WarmCriticalKeysAsync() now requires string[] parameter (no default hardcoded keys)

MIGRATION GUIDE:
- Replace: KeyName.AKey → "AKey" (or your custom key name)
- Replace: keyName.ToKeyString() → keyName (direct string usage)
- Replace: KeyName.EncryptionKey → "EncryptionKey"
- Provide your own KeyConfiguration for each key instead of using GetKeyConfiguration()
- Pass critical key names to WarmCriticalKeysAsync(string[] criticalKeyNames)

WHY THIS CHANGE:
- Library was not truly generic - contained application-specific business logic
- Hardcoded key names (AKey, CKey, OKey, LidioDeliverySecrets, NetGSM_AppKey, etc.)
- Applications can now use ANY key names without library modifications
- Follows true library design principles - zero business logic coupling

IMPORTANT: This is a major version bump due to breaking API changes. Review migration guide before upgrading.