Davasorus.Utility.DotNet.Encryption
2026.2.1.6
See the version list below for details.
dotnet add package Davasorus.Utility.DotNet.Encryption --version 2026.2.1.6
NuGet\Install-Package Davasorus.Utility.DotNet.Encryption -Version 2026.2.1.6
<PackageReference Include="Davasorus.Utility.DotNet.Encryption" Version="2026.2.1.6" />
<PackageVersion Include="Davasorus.Utility.DotNet.Encryption" Version="2026.2.1.6" />
<PackageReference Include="Davasorus.Utility.DotNet.Encryption" />
paket add Davasorus.Utility.DotNet.Encryption --version 2026.2.1.6
#r "nuget: Davasorus.Utility.DotNet.Encryption, 2026.2.1.6"
#:package Davasorus.Utility.DotNet.Encryption@2026.2.1.6
#addin nuget:?package=Davasorus.Utility.DotNet.Encryption&version=2026.2.1.6
#tool nuget:?package=Davasorus.Utility.DotNet.Encryption&version=2026.2.1.6
Davasorus.Utility.DotNet.Encryption
⚠️ Security Notice
V1 and V2 are retained for backward compatibility with existing ciphertext. Both use unauthenticated AES-CBC with fixed key-derivation parameters and should be considered obfuscation, not encryption, for new code. Use V3 for any new encryption needs.
V3 uses AES-256-GCM with a random IV per message, an authenticated envelope, and caller-supplied key material — no hardcoded secrets, no network dependencies.
Overview
Davasorus.Utility.DotNet.Encryption provides encryption and decryption utilities for .NET applications. The package ships three generations side by side:
- V1 — legacy sync API, AES-CBC, PBKDF2-HMAC-SHA1 (
[Obsolete], retained for backward compatibility). - V2 — async API, AES-CBC, PBKDF2-HMAC-SHA512 (
[Obsolete], retained for backward compatibility). - V3 — AES-256-GCM, authenticated envelope, random IV per message, caller-supplied keys,
string/byte[]/Streamoverloads. This is the recommended API for new code.
Features
- V3: AES-256-GCM authenticated encryption, random IV per message, embedded key-name for rotation
- V3:
string,byte[]/ReadOnlyMemory<byte>, andStreamoverloads - V3:
DecryptionResultfor expected-failure outcomes; throws only on programmer errors - V3: zero-on-dispose Client buffers (best-effort in-memory exposure mitigation)
- V1/V2: legacy AES-CBC with PBKDF2, retained for wire compatibility
- .NET 8 compatible
- OpenTelemetry traces + metrics for all three generations
V3 Quickstart
using Microsoft.Extensions.DependencyInjection;
using Tyler.Utility.DotNet.Encryption.V3.Configuration;
using Tyler.Utility.DotNet.Encryption.V3.Service;
// At startup:
var key = Convert.FromBase64String(builder.Configuration["Encryption:Keys:Primary"]!); // 32 bytes
builder.Services.AddEncryptionServicesV3(opts => opts.AddKey("primary", key));
// In a scoped handler:
public class Handler(IEncryptionServiceV3 enc, IDecryptionServiceV3 dec)
{
public async Task<string> WrapAsync(string secret, CancellationToken ct)
=> await enc.EncryptAsync(secret, "primary", ct);
public async Task<string?> UnwrapAsync(string cipher, CancellationToken ct)
{
var result = await dec.DecryptAsync(cipher, ct);
return result.Success ? result.Value : null;
}
}
Lifecycles are fixed (not configurable): IEncryptionServiceV3/IDecryptionServiceV3 are Scoped, IEncryptionClientV3/IDecryptionClientV3 are Transient. The Service resolves a fresh Client per call and disposes it immediately — the Client zeros its key/plaintext buffers on dispose as a best-effort in-memory exposure mitigation.
V3 Envelope Format
Each V3 ciphertext is a base64url-encoded envelope:
| Offset | Size | Field |
|---|---|---|
| 0 | 1 byte | Version (0x01) |
| 1 | 1 byte | Key-name length N |
| 2 | N bytes | Key name (ASCII, [A-Za-z0-9._-], 1..64 chars) |
| 2+N | 12 bytes | Random IV |
| 14+N | L bytes | AES-GCM ciphertext |
| 14+N+L | 16 bytes | GCM auth tag |
The key name is embedded so that decrypt can look up the right key from the registered map. This enables key rotation: register the new key under a new name, start writing with it, and old data continues to decrypt as long as the old key is still registered.
V3 Key Rotation
builder.Services.AddEncryptionServicesV3(opts =>
{
opts.AddKey("2026-Q1", oldKeyBytes); // still decrypts old data
opts.AddKey("2026-Q2", newKeyBytes); // new data is encrypted under this
});
// App code chooses "2026-Q2" for new writes:
var cipher = await enc.EncryptAsync(value, "2026-Q2");
// Decrypt picks the right key based on the envelope:
var result = await dec.DecryptAsync(cipher); // works for both 2026-Q1 and 2026-Q2 ciphertext
When you're confident no 2026-Q1 ciphertext remains in the wild, remove it from the registration.
V3 Error Model
- Throws
ArgumentNullException,EncryptionKeyNotFoundException,ObjectDisposedException,OperationCanceledException— for programmer errors and cancellation. - Returns
DecryptionResultwithSuccess = falseand aDecryptionErrorfor expected-failure decrypt outcomes:InvalidEnvelope,CiphertextTooShort,UnsupportedVersion,UnknownKey,AuthenticationFailed.
Encrypt returns a plain string or byte[] on success (no result-wrapper — encrypt has no expected-failure mode once inputs validate).
Known Limitations
- V3 Stream overloads are buffered, not streaming. AES-GCM's auth tag is computed over the whole message, so the Stream overloads read the full input into memory before encrypting. For payloads larger than tens of megabytes, split or encrypt at the record level.
- Zero-on-dispose is best-effort. V3 Clients zero their local key/plaintext buffers when disposed, but GC compaction may have copied the buffer in managed memory before we zero it. True secret-in-memory safety requires OS-level locked memory, which this package does not provide.
- No forward secrecy. AES-GCM is symmetric — compromise of a key compromises all ciphertext ever produced under that key.
- No replay protection. Ciphertext is a value; same plaintext re-encrypted produces different ciphertext, but the original ciphertext can be replayed. Applications requiring replay protection must handle it at the application layer (timestamps, nonces).
- Offline only. V3 does not integrate with KMS, Key Vault, or any remote key provider. Keys must be supplied locally at DI registration.
OpenTelemetry Integration
This package includes comprehensive OpenTelemetry instrumentation for distributed tracing, metrics, and observability across all encryption and decryption operations (V1, V2, and V3).
Telemetry Data Emitted
Activities (Spans):
Encryption.EncryptValue- Service-level encryption operations (V2)Encryption.Encrypt- Client-level encryption operations (V2)Encryption.V1.Encrypt- Legacy encryption operations (V1)Decryption.DecryptValue- Service-level decryption operations (V2)Decryption.Decrypt- Client-level decryption operations (V2)Decryption.V1.Decrypt- Legacy decryption operations (V1)
Tags (Attributes):
encryption.operation/decryption.operation- Operation type (encrypt/decrypt)encryption.input_length/decryption.input_length- Input data length in charactersencryption.output_length/decryption.output_length- Output data length in charactersencryption.valid_usage/decryption.valid_usage- Whether the usage type is validencryption.success/decryption.success- Whether the operation succeededencryption.version/decryption.version- API version (v1 for legacy APIs)crypto.algorithm- Encryption algorithm used (AES)crypto.key_derivation- Key derivation function (PBKDF2-HMAC-SHA512 for V2, PBKDF2-HMAC-SHA1 for V1)crypto.iterations- PBKDF2 iteration count (10,000)
Note: The
encryption.usage/decryption.usagetags were removed. Because theUsageenum values (Static,Dynamic,api,web,desktop) are effectively the passwords used for V1/V2 PBKDF2 derivation, emitting them as trace tags would broadcast the secret. V3 has no such concern — keys are supplied at DI registration, and only the key name is exposed in traces.
Metrics
Three instruments are emitted (via the shared Telemetry package's MeterHelper):
| Instrument | Kind | Unit | Dimensions |
|---|---|---|---|
encryption.operations.total |
Counter | {operation} |
version ∈ {v1,v2,v3}, operation ∈ {encrypt,decrypt}, success |
encryption.duration |
Histogram | ms | same dimensions |
encryption.input_bytes |
Histogram | bytes | same dimensions |
Events:
encoding.completed- Unicode encoding phase completed (encryption) with bytes_lengthdecoding.completed- Base64 decoding phase completed (decryption) with bytes_lengthcrypto.started- Cryptographic operation startedcrypto.completed- Cryptographic operation completedexception- Exception occurred with full exception details (type, message, stacktrace)
Configuring Telemetry
The package uses Tyler.Utility.Encryption as the ActivitySource name. To enable telemetry collection, configure OpenTelemetry in your application startup:
using OpenTelemetry.Trace;
// In Program.cs or Startup.cs
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("Tyler.Utility.Encryption") // Enable encryption telemetry
.AddAspNetCoreInstrumentation() // Optional: ASP.NET Core tracing
.AddHttpClientInstrumentation() // Optional: HTTP client tracing
.AddConsoleExporter() // For development
// OR for production:
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("http://your-otel-collector:4317");
})
);
Trace Context Integration
All encryption and decryption operations automatically include trace context (TraceId, SpanId, ParentSpanId) in structured log messages. This enables seamless correlation between:
- Distributed traces across microservices
- Application logs
- Performance metrics
- Error tracking
Example log output with trace context:
{
"timestamp": "2025-01-15T10:30:45.123Z",
"level": "Information",
"message": "Encryption complete",
"TraceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"SpanId": "00f067aa0ba902b7",
"ParentSpanId": "b7ad6b7169203331",
"InputLength": 256
}
Performance Analysis
The granular events enable detailed performance analysis:
Encoding Phase - Time spent converting data to bytes
- Encryption: Unicode encoding (
encoding.completedevent) - Decryption: Base64 decoding (
decoding.completedevent)
- Encryption: Unicode encoding (
Cryptographic Phase - Time spent in AES operations
- Between
crypto.startedandcrypto.completedevents - Includes key derivation (PBKDF2) and encryption/decryption
- Between
Use these events in your APM tool to identify bottlenecks and optimize performance.
Security Considerations
No sensitive data is exposed in telemetry:
- ✅ Only metadata is logged (lengths, usage types, algorithms)
- ❌ Plaintext values are never included in traces
- ❌ Encrypted values are never included in traces
- ❌ Encryption keys are never included in traces
- ❌ Salts are never included in traces
Exception messages are captured for debugging, but they do not contain sensitive cryptographic material.
Example: Full Trace
Span: Encryption.EncryptValue [Service]
├─ Tags: encryption.operation=encrypt, encryption.input_length=256
├─ Child Span: Encryption.Encrypt [Client]
│ ├─ Tags: crypto.algorithm=AES, crypto.key_derivation=PBKDF2-HMAC-SHA512, crypto.iterations=10000
│ ├─ Event: encoding.completed (bytes_length=512)
│ ├─ Event: crypto.started
│ ├─ Event: crypto.completed
│ └─ Tags: encryption.output_length=344, encryption.success=true
└─ Status: Ok
Getting Started
- Reference the library in your .NET project.
- Register the services and clients with your DI container (see below).
- Only interact with the service interfaces (
IEncryptionService,IDecryptionService) in your application code. The service manages all communication with the client internally.
Note: Do not use the client classes directly in your application code. The service layer encapsulates all client logic, error handling, and logging.
Usage Example
V1 (Synchronous, legacy):
var encrypt = new Encrypt(logger);
var encrypted = encrypt.EncryptValue(model);
var decrypt = new Decrypt(logger);
var decrypted = decrypt.DecryptValue(model);
V2 (Asynchronous, recommended):
// Register services and clients in DI (see below)
// In your consuming code, inject IEncryptionService and IDecryptionService
public class MyClass
{
private readonly IEncryptionService _encryptionService;
private readonly IDecryptionService _decryptionService;
public MyClass(IEncryptionService encryptionService, IDecryptionService decryptionService)
{
_encryptionService = encryptionService;
_decryptionService = decryptionService;
}
public async Task<string> EncryptAndDecrypt(UtilitySettingsModel model)
{
var encrypted = await _encryptionService.EncryptValue(model);
var decrypted = await _decryptionService.DecryptValue(model);
return decrypted;
}
}
API Overview
- V1
EncryptValue(UtilitySettingsModel model): string
Synchronously encrypts the providedUtilitySettingsModeland returns the encrypted string. This method is part of the legacy V1 API and is intended for use in synchronous workflows.DecryptValue(UtilitySettingsModel model): string
Synchronously decrypts the providedUtilitySettingsModeland returns the decrypted string. Use this method when asynchronous processing is not required.Usage enum:
api
For API-based scenarios where encryption/decryption is performed in service endpoints.web
For web application scenarios, such as encrypting data in web forms or cookies.desktop
For desktop application scenarios, such as encrypting configuration files or user data.
- V2
EncryptValueAsync(UtilitySettingsModel model): Task<string>
Asynchronously encrypts the providedUtilitySettingsModel. This method is internal to the service and not intended for direct use.DecryptValueAsync(UtilitySettingsModel model): Task<string>
Asynchronously decrypts the providedUtilitySettingsModel. This method is internal to the service and not intended for direct use.Service API:
EncryptValue(UtilitySettingsModel model): Task<string>
Public asynchronous method to encrypt aUtilitySettingsModelvia the service interface.DecryptValue(UtilitySettingsModel model): Task<string>
Public asynchronous method to decrypt aUtilitySettingsModelvia the service interface.
Usage enum:
Static
For scenarios where encryption parameters remain constant.Dynamic
For scenarios where encryption parameters may change per operation.
Service/Client Pattern
- Do not use
IEncryptionClientorIDecryptionClientdirectly. - Always use
IEncryptionServiceandIDecryptionServicein your application code. - The service handles all error handling, logging, and client interaction.
Key Differences: V1 vs V2
| Feature/Behavior | V1 (Legacy) | V2 (Current) |
|---|---|---|
| API Design | Synchronous methods | Asynchronous (Task-based, service-oriented) |
| Usage Enum | api, web, desktop | Static, Dynamic |
| Key Derivation | PBKDF2, static salt, SHA1 | PBKDF2, named salt ("Ivan Medvedev"), SHA512 |
| Error Handling | Returns empty string on error | Returns empty string, logs error, throws for invalid usage |
| Logging | ILogger<T> | ILogger<T> |
| .NET Support | .NET 8 | .NET 8 |
| Extensibility | Limited | Improved, interface-based, testable |
| Test Coverage | Unit tests for all usage types | Unit tests for all usage types, async tests |
Summary of Improvements in V2
- Fully async/await support for better scalability
- Stronger key derivation (SHA512, named salt)
- Usage enum is more generic (Static/Dynamic)
- Improved error handling and logging
- Interface-based design for easier testing and extension
- Consistent base64 encoding/decoding (replaces '/' with '_' for URLs)
- Strict service/client separation for DI
Dependency Injection (DI) Usage
V2 is designed for modern .NET dependency injection. Use the built-in extension methods from Tyler.Utility.DotNet.Encryption.Configuration:
Recommended: Extension methods (registers both encryption and decryption)
using Tyler.Utility.DotNet.Encryption.Configuration;
// Default (Scoped lifetime)
services.AddEncryptionServices();
// With custom lifetime
services.AddEncryptionServices(config => config.UseTransientLifetime());
services.AddEncryptionServices(config => config.UseSingletonLifetime());
Register only encryption or decryption:
services.AddEncryptionOnly();
services.AddDecryptionOnly();
Manual registration (alternative):
services.AddScoped<IEncryptionService, EncryptionService>();
services.AddScoped<IEncryptionClient, EncryptionClient>();
services.AddScoped<IDecryptionService, DecryptionService>();
services.AddScoped<IDecryptionClient, DecryptionClient>();
Note: Only inject and use
IEncryptionServiceandIDecryptionServicein your application code. The service manages all client interactions.
Example Test Cases
- Encrypt and decrypt with all supported usage types
- Handles invalid usage gracefully (returns empty string)
- Logs errors on exceptions
- Async tests for V2
Dependencies
- Davasorus.Utility.Dotnet.Contracts.Collections
- Davasorus.Utility.DotNet.Contracts.Types
- Davasorus.Utility.DotNet.Telemetry
- Microsoft.Extensions.DependencyInjection
- Microsoft.Extensions.Logging.Abstractions
License
MIT License
Contributing
Contributions are welcome! Please submit issues or pull requests for improvements.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- Davasorus.Utility.Dotnet.Contracts.Collections (>= 2026.2.1.5)
- Davasorus.Utility.DotNet.Contracts.Types (>= 2026.2.1.5)
- Davasorus.Utility.DotNet.Telemetry (>= 2026.2.1.3)
- Microsoft.Extensions.DependencyInjection (>= 10.0.7)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.7)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on Davasorus.Utility.DotNet.Encryption:
| Package | Downloads |
|---|---|
|
Davasorus.Utility.DotNet.SQS
Amazon SQS interaction for TEPS Utilities |
|
|
Davasorus.Utility.DotNet.Auth
Handles Authentication for TEPS Utilities |
|
|
Davasorus.Utility.DotNet.Api
API Interaction for TEPS Utilities with generic deserialization, configurable error reporting, and improved DI configuration. Supports REST, GraphQL, gRPC, WebSocket, SignalR, and SSE protocols. |
|
|
SA.OpenSearchTool.Business
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2026.2.1.7 | 0 | 4/23/2026 |
| 2026.2.1.6 | 0 | 4/23/2026 |
| 2026.2.1.5 | 36 | 4/22/2026 |
| 2026.2.1.4 | 137 | 4/16/2026 |
| 2026.2.1.3 | 1,902 | 4/9/2026 |
| 2026.2.1.2 | 106 | 4/9/2026 |
| 2026.2.1.1 | 1,349 | 4/1/2026 |
| 2026.1.3.5 | 743 | 3/29/2026 |
| 2026.1.3.4 | 635 | 3/24/2026 |
| 2026.1.3.3 | 2,027 | 3/12/2026 |
| 2026.1.3.2 | 134 | 3/12/2026 |
| 2026.1.3.1 | 632 | 3/10/2026 |
| 2026.1.2.6 | 1,743 | 2/17/2026 |
| 2026.1.2.4 | 809 | 2/12/2026 |
| 2026.1.2.3 | 279 | 2/9/2026 |
| 2026.1.2.2 | 113 | 2/9/2026 |
| 2026.1.2.1 | 666 | 2/7/2026 |
| 2026.1.1.4 | 674 | 1/30/2026 |
| 2026.1.1.3 | 1,309 | 1/15/2026 |
| 2026.1.1.2 | 112 | 1/14/2026 |