Feedemy.KeyManagement
2.5.2
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.
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
<PackageReference Include="Feedemy.KeyManagement" Version="2.5.2" />
<PackageVersion Include="Feedemy.KeyManagement" Version="2.5.2" />
<PackageReference Include="Feedemy.KeyManagement" />
paket add Feedemy.KeyManagement --version 2.5.2
#r "nuget: Feedemy.KeyManagement, 2.5.2"
#:package Feedemy.KeyManagement@2.5.2
#addin nuget:?package=Feedemy.KeyManagement&version=2.5.2
#tool nuget:?package=Feedemy.KeyManagement&version=2.5.2
Feedemy.KeyManagement
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:
- Go to Azure Portal → Azure Active Directory → App registrations
- Create new registration or use existing
- Copy Application (client) ID →
ClientId - Copy Directory (tenant) ID →
TenantId - Go to Certificates & secrets → New client secret → Copy value →
ClientSecret - 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+):
0x01prefix + 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:
- Background service checks every
RotationCheckInterval - Identifies keys where
NextRotationDue <= Now - Generates new key content
- Saves to storage and database
- Invalidates all cache layers
- Triggers cache warming
- 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:
- Content Cache:
key:{KeyName}:content:v{Version}(1-hour TTL) - Metadata Cache:
key:{KeyName}:metadata(5-min TTL) - Version Cache:
key:{KeyName}:versions(5-min TTL)
Invalidation Flow:
- Server 1 rotates key
- Publishes invalidation message to Redis pub/sub
- All servers receive notification
- Each server clears local cache
- 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 versionKeyManagement.KeyVersions: Version history with content hashesKeyManagement.KeyAuditLogs: Complete audit trailKeyManagement.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
KeyNameenum - use strings instead - Removed
ToKeyString()andToKeyName()extension methods WarmCriticalKeysAsync()now requiresstring[]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
- Architecture Guide - System architecture and data flows
- Migration Guide - Version migration instructions
- Analyzer Rules - Complete list of Roslyn analyzer rules
- Telemetry - OpenTelemetry integration guide
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:
- Fork the repository
- Create a feature branch
- Write tests for new features
- Ensure all tests pass:
dotnet test - 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
- Documentation: GitHub Wiki
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Acknowledgments
Built with:
| Product | Versions 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. |
-
net10.0
- Azure.Identity (>= 1.17.1)
- Azure.Security.KeyVault.Secrets (>= 4.8.0)
- Microsoft.Data.SqlClient (>= 6.1.3)
- Microsoft.EntityFrameworkCore.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Caching.Memory (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Options (>= 10.0.0)
- OpenTelemetry.Api (>= 1.14.0)
- Polly (>= 8.6.5)
- StackExchange.Redis (>= 2.10.1)
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.