Feedemy.KeyManagement 3.2.1

dotnet add package Feedemy.KeyManagement --version 3.2.1
                    
NuGet\Install-Package Feedemy.KeyManagement -Version 3.2.1
                    
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="3.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Feedemy.KeyManagement" Version="3.2.1" />
                    
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 3.2.1
                    
#r "nuget: Feedemy.KeyManagement, 3.2.1"
                    
#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@3.2.1
                    
#: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=3.2.1
                    
Install as a Cake Addin
#tool nuget:?package=Feedemy.KeyManagement&version=3.2.1
                    
Install as a Cake Tool

Feedemy.KeyManagement

NuGet License: MIT .NET

Enterprise-grade key management library for .NET applications.

Features

  • Automatic Key Rotation - Background service with configurable intervals
  • Versioned Encryption - Auto-decryption with version detection
  • Asymmetric Keys - RSA (2048/3072/4096) and ECDSA (P-256/P-384/P-521)
  • Multi-Platform Storage - Windows DPAPI, Linux Keyring, Azure Key Vault
  • Distributed Caching - Redis with pub/sub invalidation
  • Database Persistence - SQL Server, PostgreSQL, and SQLite
  • Health Monitoring - ASP.NET Core health checks
  • Fallback Storage - Multi-provider redundancy
  • Audit Trail - Complete compliance logging
  • Roslyn Analyzers - 48 compile-time security rules

Installation

dotnet add package Feedemy.KeyManagement

# Persistence (choose one)
dotnet add package Feedemy.KeyManagement.Providers.EntityFramework  # SQL Server
dotnet add package Feedemy.KeyManagement.Providers.Npgsql          # PostgreSQL
dotnet add package Feedemy.KeyManagement.Providers.Sqlite          # SQLite (dev/testing)

# Optional
dotnet add package Feedemy.KeyManagement.Analyzers

Quick Start

Development Setup

using Feedemy.KeyManagement.Extensions;

var builder = WebApplication.CreateBuilder(args);

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

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

Production Setup

builder.Services.AddKeyManagement(options =>
{
    options.EnableAutoRotation = true;
    options.RotationCheckInterval = TimeSpan.FromHours(6);

    // Auto-initialization
    options.Initialization.EnableAutoInitialization = true;
    options.Initialization.ExternalKeysJsonPath = "keys.json";
})
.WithAzureKeyStorage("https://your-vault.vault.azure.net/")
.WithRedisCache("localhost:6379")
.WithSqlServerMetadata("Server=localhost;Database=KeyManagement;...");

builder.Services.AddHealthChecks()
    .AddKeyManagementHealthCheck();

Basic Usage

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

    // Create a key
    public async Task CreateKeyAsync()
    {
        await _adminService.CreateKeyAsync(new CreateKeyRequest
        {
            KeyName = "MyEncryptionKey",
            AutoGenerate = true,
            KeySize = 32,
            RotationIntervalDays = 90,
            CreatedBy = "Admin"
        });
    }

    // Retrieve a key (cached - sub-millisecond)
    public async Task<byte[]> GetKeyAsync()
    {
        return await _keyService.RetrieveKeyAsync("MyEncryptionKey");
    }

    // Check health
    public async Task<KeyHealthStatus> GetHealthAsync()
    {
        var health = await _keyService.GetKeyHealthAsync("MyEncryptionKey");
        return health.Status;
    }
}

Key Initialization

Define keys in keys.json for auto-generation on first run:

{
  "Keys": [
    {
      "KeyName": "EncryptionKey",
      "KeyType": "Symmetric",
      "RotationIntervalDays": 90,
      "Category": "MasterKey"
    },
    {
      "KeyName": "JwtSigningKey",
      "KeyType": "RSA",
      "KeySize": 2048,
      "RotationIntervalDays": 180,
      "Category": "Signing"
    }
  ]
}

Asymmetric Keys

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

// Verify signature
var isValid = await _asymmetricOps.VerifyAsync(
    "JwtSigningKey",
    data,
    signature,
    HashAlgorithmName.SHA256);

// Get public key for external use
var publicKeyPem = await _asymmetricOps.GetPublicKeyPemAsync("JwtSigningKey");

Performance

Operation Mean Notes
RetrieveKey (cached) < 1 μs L1 cache hit
RetrieveKey (cache miss) ~12 ms Database + storage
Sign (RSA-2048) ~1.2 ms
Verify (RSA-2048) ~0.15 ms
Sign (ECDSA P-256) ~0.35 ms
Verify (ECDSA P-256) ~0.12 ms

Packages

Package Description
Feedemy.KeyManagement Core library
Feedemy.KeyManagement.Providers.EntityFramework SQL Server persistence
Feedemy.KeyManagement.Providers.Npgsql PostgreSQL persistence
Feedemy.KeyManagement.Providers.Sqlite SQLite persistence (dev/testing)
Feedemy.KeyManagement.Analyzers Roslyn analyzers

Documentation

Detailed documentation available in docs/partial/:

  • README_CORE.md - API reference
  • README_CONFIG.md - Configuration options
  • README_STORAGE.md - Storage providers
  • README_PERSISTENCE.md - Database setup
  • README_CACHE.md - Caching architecture
  • README_INIT.md - Initialization guide
  • README_BACKGROUND.md - Background services
  • README_EXTENSIONS.md - DI setup

License

This project is dual-licensed:

Use Case License Cost
Personal projects MIT Free
Open source projects MIT Free
Commercial products MIT Free
Enterprise support & SLA Commercial Paid

Commercial inquiries: licensing@feedemy.com

Support


Copyright (c) 2025 Feedemy

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.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.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.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.

Version Downloads Last Updated
3.2.1 116 4/24/2026
3.2.0 102 4/24/2026
3.1.0 160 3/9/2026
3.0.6 114 3/9/2026
3.0.5 114 3/9/2026
3.0.4 108 3/9/2026
3.0.3 126 3/9/2026
3.0.2 116 3/9/2026
3.0.1 120 2/27/2026
2.5.10 268 1/21/2026
2.5.9 161 1/10/2026
2.5.8 112 1/10/2026
2.5.7 109 1/9/2026
2.5.6 107 1/9/2026
2.5.5 138 1/6/2026
2.5.4 141 1/4/2026 2.5.4 is deprecated because it has critical bugs.
2.5.3 246 1/3/2026 2.5.3 is deprecated because it has critical bugs.
2.5.2 1,428 12/1/2025 2.5.2 is deprecated because it has critical bugs.
2.5.1 1,055 12/1/2025 2.5.1 is deprecated because it is no longer maintained and has critical bugs.
2.5.0 1,047 12/1/2025 2.5.0 is deprecated because it is no longer maintained and has critical bugs.

v3.2.1 — Hotfix: Private L1 cache (do not configure shared IMemoryCache)

v3.2.0 called services.AddMemoryCache(opts => { opts.SizeLimit = ... })
which reconfigured the SHARED DI-wide IMemoryCache singleton. Any host
application that also uses IMemoryCache for its own purposes was forced
to call SetSize(...) on every Set() call (otherwise InvalidOperationException).
This was a real breaking change that slipped through "zero breaking" testing.

Fix in 3.2.1:
- Introduced FeedemyKeyManagementMemoryCache (sealed, inherits MemoryCache).
 Library now owns its own private cache instance registered in DI by its
 concrete type.
- MemoryCacheProvider and KeyCacheService are registered via factory methods
 that inject the private cache, NOT the shared IMemoryCache.
- services.AddMemoryCache(...) is no longer called by the library; the host
 application's shared IMemoryCache registration (if any) is left untouched.

Upgrade from 3.2.0: drop-in. If you applied a PostConfigure<MemoryCacheOptions>
workaround to null out SizeLimit, you can remove it.

-----

v3.2.0 — Bench-Driven Reliability & Scalability Release

Produced by a 90-scenario benchmark harness (bench/ + BenchNode) and 3 h
of sustained endurance load. 31 findings documented, 21 fixed in 10 PRs,
zero breaking changes.

NEW APIS (additive):
- SecretFileKeyStorageProvider: raw-key storage for Docker Swarm / K8s
 secret mounts. Fixes the multi-node /etc/machine-id derivation trap
 (finding #3). Fluent: WithSecretFileKeyStorage("/run/secrets").
- LinuxStorageOptions: KeyringPath, SaltPath, MachineKeySource (MachineId|
 Hostname|Environment|File), EnvironmentVariableName, FilePath.
- WithLinuxKeyStorage(Action<LinuxStorageOptions>?) overload;
 parameterless overload still resolves to default.
- ExternalKeyPairDto: public record for external asymmetric key import
 (finding #17 — schema was undocumented).
- Paged repository APIs on all three repositories:
 GetPagedAsync(skip,take), GetDueForRotationPagedAsync(threshold),
 GetActiveCountAsync, GetByKeyNamePagedAsync (audit),
 GetActiveVersionCountsAsync (batched GROUP BY — kills N+1).
- PagedResult<T> model.
- KeyManagementOptions.AutoBootstrapMasterKey (default true): startup
 now creates the master EncryptionKey if missing; consumers no longer
 have to manually seed before first asymmetric create.
- KeyManagementOptions.Cache.L1MaxSize (default 100_000 units) +
 L1CompactionPercentage (default 0.25): bounded L1 IMemoryCache.
- 2 new telemetry histograms:
 keymanagement.cache.invalidation.publish.duration (ms)
 keymanagement.cache.invalidation.receive.duration (ms)
- ErrorCodes.MASTER_KEY_MISSING.

CRITICAL FIXES:
- #28 MasterKeyProvider cache stampede: per-version SemaphoreSlim +
 atomic InvalidateCache + deferred ZeroMemory. Closes the 1 h-endurance
 race that silently returned null for ~2767 reads per run.
- #29 Unbounded IMemoryCache: SizeLimit + SetSize across all five entry
 types. Working set dropped from 6.1 GB to bounded after sustained load.
- #11 Admin API O(N) scan: pagination + SQL-level filtering throughout.
 SystemHealthAsync goes from 20 k SQL queries (N+1) to 2 (single
 GROUP BY). At 5 k keys, p99 drops from 2-4 s to <100 ms.
- #1 Asymmetric silent master key dependency: auto-bootstrap hosted
 service + clean Result.Fail(MASTER_KEY_MISSING) precondition.
 Fresh deployments no longer crash on first RSA/ECDSA create.
- #3 Multi-node /etc/machine-id trap: addressed via SecretFileKeyStorage
 (primary recommendation) or LinuxStorageOptions.MachineKeySource
 override.
- #4 PostgreSQL schema init race: DatabaseMigrator now acquires
 pg_advisory_xact_lock("KMMG") on the migration transaction. Three
 replicas converge cleanly instead of racing on CREATE TABLE.
- #31 Rotation-retrieve race (regression surfaced by #28 fix):
 RetrieveKeyAsync falls back to version-1 when the new version is
 committed to DB but not yet written to storage. Closes the
 "DB-ahead-of-storage" window (~6.4 % gap under concurrent retrieve +
 rotate) transparently.

CONTRACT & BEHAVIOR FIXES:
- #16 RSA oversized plaintext: ArgumentException → CryptographicException
 (honors IAsymmetricKeyOperations XML doc).
- #25 Public key re-fetch write amplification: dedicated 15-min L1
 cache replaces per-read metadata.UpdateAsync (eliminated
 DbUpdateConcurrencyException under concurrent reads).
- #23 GetECDsaAsync raw accessor: ImportPkcs8PrivateKey errors wrapped
 with InvalidOperationException + meaningful context.
- #27 Fresh asymmetric GetCurrentVersionAsync: bypass metadata cache
 (L2 round-trip was corrupting large PublicKeyPem fields).
- #9 WithRedisCache implicitly enables EnableCacheInvalidationListener
 (opt-out still possible by explicit override).
- #12 EncryptionTotal Prometheus counter: now emits on both symmetric
 and asymmetric success paths (was a dead counter).

NEW DOCS:
- docs/claude/telemetry.md — OTel ↔ Prometheus naming reference, meter
 inventory, PromQL + Grafana snippets.
- docs/claude/security-model.md — threat matrix, per-provider guarantees
 (cold disk, container escape, host root, memory dump, network), and
 mitigation roadmap (Vault Transit, TPM sealed key, PKCS#11 softHSM).
- Expanded XML docs: LinuxKeyringStorageProvider misnomer warning,
 CreateKeyAsync concurrent-duplicate contract, RotationCheckInterval
 validation message, CreateKeyRequest.ExternalAsymmetricKeyPair schema
 (with JSON example).

UPGRADE:
Drop-in: all public APIs are additive; no signature changes. Consumers
that relied on the dead EncryptionTotal counter will now see non-zero
values. Consumers configuring Redis via WithRedisCache() will have
the cache invalidation listener enabled automatically — set
EnableCacheInvalidationListener=false explicitly to preserve the
pre-3.2 behavior.

VERIFICATION:
- 54+ unit tests pass (solution-wide 0 errors).
- Bench 69/69 scenarios green, 0 violations (was 11+ violations in
 pre-fix runs). S34 master-key-race endurance 0 bad (was 2767).
- See docs/claude/bench-findings-verified-2026-04-24.md for the full
 verification matrix.
----

v3.0.0 - Major Security, Performance & Stability Release

SECURITY (CRITICAL):
- Fixed SQL injection in PostgreSQL DatabaseMigrator (parameterized queries via NpgsqlParameter)
- Fixed plaintext Azure credential storage on Linux (AES-256-GCM encryption with machine-derived key)
- AES-GCM AAD bypass mitigation (AllowLegacyNoAadDecryption option, default false)
- DPAPI application-specific entropy ("Feedemy.KeyManagement.v1") with legacy auto-migration
- Linux per-installation random salt with .salt.bak backup/recovery chain
- Linux key re-encryption safety: write-read-verify with FixedTimeEquals, .migrating transactional backup
- Linux RetrieveKeyAsync TOCTOU fix (File.Exists moved inside lock)
- Credential JSON secure deletion after import (random-overwrite + File.Delete)
- CryptographicOperations.ZeroMemory throughout all sensitive byte array paths (12+ locations)
- IDisposable for WindowsDpapiStorageProvider, FallbackStorageProvider, MasterKeyProvider

PERFORMANCE:
- MasterKeyProvider in-memory cache with 5-min TTL (eliminates 2 IO round-trips per decryption)
- MasterKeyProvider.InvalidateCache() called after master key rotation for cache coherence
- Source-generated logging ([LoggerMessage]) for retrieval hot path
- TagList zero-allocation telemetry, Span-based IsEncryptedFormat detection
- Prefix-based cache pattern removal fast path (bypasses regex for simple patterns)
- Health check response caching (30s TTL with SemaphoreSlim double-check)
- RandomNumberGenerator.Fill / SHA256.HashData static APIs (no instance allocation)
- RegexOptions.None instead of Compiled (prevents assembly leak)
- Volatile.Read for FallbackStorageProvider._consecutiveFailures (prevents torn reads)
- Atomic RetrievalLockEntry.Dispose via Interlocked.CompareExchange
- Static eviction callback in KeyCacheService (eliminates closure allocation)

FIXED:
- Startup crash risks shifted behind DI build (ConfigurationValidationHostedService)
- Background services opt-in by default (EnableAutoRotation, EnableCacheInvalidationListener, EnableFallbackSync)
- CacheInvalidationListener resiliency with bounded retry and exponential backoff
- Double pub/sub removed from InvalidateAllVersionsAsync
- Startup ordering fixed for ServicesStartConcurrently=true
- UseAzureDefaults production safety: metadata provider validation enforced
- Rollback log level: successful rollback now logs Warning instead of Critical
- ConfigurationEnumMappings default cases now throw ArgumentOutOfRangeException
- Fire-and-forget defensive copy in FallbackStorageProvider.SyncToFallbackAsync

BREAKING CHANGES:
- OperationResult<T> now immutable (init-only properties), Warnings is IReadOnlyList<string>
- IKeyMetadataRepository has new required members (ExistsIncludingInactiveAsync, GetByKeyNameAsync)
- IMasterKeyProvider has new InvalidateCache() method (custom implementations must add it)
- MasterKeyProvider now implements IDisposable
- Background services opt-in by default
- UseAzureDefaults() requires persistent metadata provider
- GetByNameAsync/ExistsAsync targets active records by default
- See CHANGELOG.md and docs/MIGRATION_GUIDE.md for full migration guide