Stratara.Security
3.2.2
dotnet add package Stratara.Security --version 3.2.2
NuGet\Install-Package Stratara.Security -Version 3.2.2
<PackageReference Include="Stratara.Security" Version="3.2.2" />
<PackageVersion Include="Stratara.Security" Version="3.2.2" />
<PackageReference Include="Stratara.Security" />
paket add Stratara.Security --version 3.2.2
#r "nuget: Stratara.Security, 3.2.2"
#:package Stratara.Security@3.2.2
#addin nuget:?package=Stratara.Security&version=3.2.2
#tool nuget:?package=Stratara.Security&version=3.2.2
Stratara.Security
License: MIT.
Dependency-light key store and envelope encryption for Stratara. Provides a production
IKeyStore with KEK-wrapped, versioned per-scope data-encryption keys (rotation, revoke, and
crypto-shred), a file-backed master-key provider, and an AES-GCM blob encryptor — referencing only
Stratara.Abstractions + BCL crypto. No EF Core, RabbitMQ, Redis, or cloud SDKs in the graph.
Quick start
// appsettings / secrets:
// "Stratara": { "KeyStore": { "MasterKeyBase64": "<openssl rand -base64 32>", "StorePath": "/var/run/secrets/keystore.json" } }
builder.Services.AddStrataraFileKeyStore(builder.Configuration);
// Encrypt a blob bound to a tenant scope + purpose:
var scope = new KeyScope(DataSensitivityLevel.TenantScoped, TenantId: "acme-corp");
await using var encrypted = await encryptor.EncryptAsync(plainStream, scope, purpose: "attachment");
await using var plain = await encryptor.DecryptAsync(encrypted, scope);
What's inside
EnvelopeFileKeyStore(IKeyStore) — random 32-byte DEK per scope/version, KEK-wrapped with AES-256-GCM (wrap AAD bound to the key id, so a wrapped DEK can't be moved to another scope). The store file holds only wrapped DEKs + metadata, never plaintext.RotateAsyncadds a version;RevokeAsyncmakes one version undecryptable;EraseScopeAsyncdeletes all versions for a scope (GDPR Art. 17 crypto-shred). DEKs are zeroed after use; the store file is written0600on Unix.FileMasterKeyProvider(IMasterKeyProvider) — KEK fromMasterKeyBase64, validated to decode to exactly 32 bytes (AES-256) at startup. The custody seam: swap for an HSM / KMS / vault provider later without touching the stored data.AesGcmSecureBlobEncryptor(ISecureBlobEncryptor) — AES-GCM stream encryption with apurpose-bound AAD ({tenant}||{purpose}) and a versioned, self-describing format (v2 leading byte). Reads legacy streams without the version byte; setStratara:BlobEncryption:LegacyBlobsCarryPurposeto match the legacy layout.DummyKeyStore— Development-only deterministic fallback (throws outsideDevelopment).
Key id schema
{level}:{tenant}:{user}:v{N} — e.g. TenantScoped:acme-corp::v1. GetOrCreateCurrentKeyAsync
returns the highest non-revoked version (creating v1 if none); RotateAsync creates v{N+1}.
Dependencies
Stratara.AbstractionsStratara.DiagnosticsMicrosoft.Extensions.{Configuration,DependencyInjection,Hosting,Logging}.AbstractionsMicrosoft.Extensions.Options(+Options.ConfigurationExtensions)
| 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
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.8)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.8)
- Stratara.Abstractions (>= 3.2.2)
- Stratara.Diagnostics (>= 3.2.2)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Stratara.Security:
| Package | Downloads |
|---|---|
|
Stratara.Infrastructure
Infrastructure glue for the Stratara framework — authorization decorators, configuration providers, and DI composition helpers that wire Mediator, Outbox, Identity, and EF Core into a hosted app. |
|
|
Stratara.Testing
Test doubles and assertion helpers for applications built on the Stratara framework — an in-memory IKeyStore, an in-memory IMessageBus, in-memory membership/setting/API-key stores, a preset ISessionContextProvider, deterministic tenant ids, and a given/when/then aggregate rehydration harness. Drop the Postgres/RabbitMQ testcontainers for unit tests. |
GitHub repositories
This package is not used by any popular GitHub repositories.
API keys can now be bootstrapped. Issuance always generated the key itself, which does not fit the
setups where server and caller have to share a key *before* either starts — container orchestration,
CI provisioning, self-hosted bundles, end-to-end test hosts. This release adds an import path for a
key the caller already holds, plus the canonical key format as public API so callers generate values
instead of inventing them.
### Added
- **`IApiKeyStore.ImportAsync` — store a machine key whose raw value the caller already holds.**
Issuance generates the key itself, which is the wrong shape when server and caller must share it
*before* either boots: container orchestration, CI provisioning, self-hosted bundles, end-to-end
test hosts. Without this path, consumers bypassed the store and reimplemented its internals (the
digest format and the machine-key membership row) — a copy that keeps compiling after the store
changes and fails as a runtime 401. Import is idempotent, so it can run unconditionally on every
boot: a value that is already stored returns the existing descriptor and the stored key is never
mutated, so a changed configuration cannot escalate a key's roles or extend its expiry unnoticed.
Revoked, expired, and foreign-tenant values are rejected rather than silently adopted, and
concurrent replicas racing the same first import converge on one key. Machine keys only —
`ApiKeyImportRequest` carries no `UserId`.
- **`ApiKeyFormat` (`Stratara.Abstractions.ApiKeys`) — the canonical raw-key format as public API.**
`CreateRawKey()` generates `stk_` plus the Base64Url encoding of 32 CSPRNG bytes; `IsWellFormed()`
checks that shape. `ImportAsync` accepts only well-formed values: the store keeps its stored
digest unsalted because a generated key carries 256 bits of entropy, and a hand-picked value would
quietly invalidate that. Generate keys out of band with `CreateRawKey()` and keep them in a secret
store — the type lives in the abstractions package so host-builder and orchestration projects can
reach it without referencing the storage implementation.
- **`InMemoryApiKeyStore` (`Stratara.Testing`) — the drop-in double for the API-key store**,
mirroring issuance, import, fail-closed validation, revocation, and the erasure sweeps, and
materializing machine keys into a membership store you can share and inspect.
### Changed
- **API-key lifecycle events are now logged** (`Stratara.Diagnostics` event-ID range 116_000):
import, idempotent repeat, and a repeat that supplied different parameters. Key ids and tenant
ids only — never the raw key or its digest.
- **For implementers of `IApiKeyStore`:** the interface gained `ImportAsync`. Custom implementations
need the new member; callers are unaffected.