Serilog.Sinks.File.Encrypt
5.1.0-preview.1
dotnet add package Serilog.Sinks.File.Encrypt --version 5.1.0-preview.1
NuGet\Install-Package Serilog.Sinks.File.Encrypt -Version 5.1.0-preview.1
<PackageReference Include="Serilog.Sinks.File.Encrypt" Version="5.1.0-preview.1" />
<PackageVersion Include="Serilog.Sinks.File.Encrypt" Version="5.1.0-preview.1" />
<PackageReference Include="Serilog.Sinks.File.Encrypt" />
paket add Serilog.Sinks.File.Encrypt --version 5.1.0-preview.1
#r "nuget: Serilog.Sinks.File.Encrypt, 5.1.0-preview.1"
#:package Serilog.Sinks.File.Encrypt@5.1.0-preview.1
#addin nuget:?package=Serilog.Sinks.File.Encrypt&version=5.1.0-preview.1&prerelease
#tool nuget:?package=Serilog.Sinks.File.Encrypt&version=5.1.0-preview.1&prerelease
Serilog.Sinks.File.Encrypt
A Serilog.File.Sink hook that encrypts log files using RSA and AES-GCM hybrid encryption. This package provides secure logging by encrypting log data before writing to disk, ensuring sensitive information remains protected.
v5.0.0 Breaking Changes
Split the NuGet package Serilog.Sinks.File.Encrypt into 3 separate packages:
Serilog.Sinks.File.Encrypt(this package — the file hook for encryption only)Serilog.Sinks.File.Decrypt(decryption library —LogReader,LocalKeyProvider,DecryptionOptions,DecryptionUtils,IKeyProvider)Serilog.Sinks.File.Encrypt.Core(shared cryptographic primitives — transitive dependency, no direct reference needed)
Features
- Hybrid Encryption: Uses RSA for key exchange and AES-GCM for efficient, authenticated data encryption
- Key Rotation: Assign a key ID to
EncryptHooks; the decryption layer selects the correct private key automatically - Seamless Integration: Plugs directly into Serilog.File.Sink using file lifecycle hooks
- Optimal Performance: Optimized encryption performance using hybrid encryption
Use Cases
- Secure logging of sensitive application data, especially in desktop applications
- Compliance with data protection regulations by encrypting log files
- Protection against unauthorized access to log files in shared or cloud environments
Performance
Production-ready performance with minimal overhead:
- ✅ 8-12% time overhead in real-world unbuffered scenarios (well under typical targets)
- ✅ 300K+ logs/second throughput with buffered writes (vs. ~174K baseline)
- ✅ AES-GCM: ~1.03–1.07x memory overhead — near-baseline
- ✅ ~5-16% throughput reduction with unbuffered encryption (148K–153K logs/sec)
- 🚀 Buffered mode outperforms non-encrypted unbuffered by ~66% — encrypted buffered I/O is faster than plain unbuffered
- ✅ Zero lock contentions — safe for multithreaded applications handled by Serilog.File.Sink
Buffering Trade-off: While buffered writes provide excellent performance, they carry a risk of data loss if the application crashes before flushing. Always call Log.CloseAndFlush() on application shutdown.
For detailed benchmarks and analysis, see the Benchmark Documentation.
Installation
Install the package via NuGet:
dotnet add package Serilog.Sinks.File.Encrypt
For decryption in your application, install the Decrypt package:
dotnet add package Serilog.Sinks.File.Decrypt
For key generation and ad-hoc log decryption, install the CLI tool:
dotnet tool install --global Serilog.Sinks.File.Encrypt.Cli
Quick Start
1. Generate RSA Key Pair
Generate an RSA key pair using the CLI tool:
serilog-encrypt generate --output ./keys
This creates:
public_key.xml: Used for encryption (safe to include with your application)private_key.xml: Used for decryption (keep secure, do not distribute)
2. Configure Serilog with Encryption
using Serilog;
using Serilog.Sinks.File.Encrypt;
// Load your public key
string publicKeyXml = File.ReadAllText("./keys/public_key.xml");
// Configure Serilog with encryption.
// Assign a key ID to support future key rotation (recommended).
Log.Logger = new LoggerConfiguration()
.WriteTo.File(
path: "logs/app.log",
hooks: new EncryptHooks(publicKeyXml, keyId: "my-app-key-2026"))
.CreateLogger();
// Log as usual
Log.Information("This message will be encrypted!");
// Always flush on shutdown
Log.CloseAndFlush();
💡 Performance Tip: For high-volume scenarios where you can tolerate potential data loss on crashes, use
buffered: true. See the Advanced Usage section below.
3. Decrypt Logs
To decrypt log files, see the Serilog.Sinks.File.Decrypt package for programmatic decryption, or the Serilog.Sinks.File.Encrypt.Cli tool for ad-hoc decryption.
Advanced Usage
High-Performance Configuration (Buffered Mode)
For high-volume logging scenarios where you can tolerate potential data loss on crashes:
using Serilog;
using Serilog.Sinks.File.Encrypt;
string publicKeyXml = File.ReadAllText("./keys/public_key.xml");
Log.Logger = new LoggerConfiguration()
.WriteTo.File(
path: "logs/app.log",
buffered: true, // buffered writes
flushToDiskInterval: TimeSpan.FromSeconds(5), // flush every X seconds (adjust as needed)
hooks: new EncryptHooks(publicKeyXml, keyId: "my-app-key-2026"))
.CreateLogger();
// CRITICAL: Always flush on shutdown
Log.CloseAndFlush();
⚠️ Warning: Buffered writes risk data loss on crashes. Only use when:
- Application has reliable shutdown handling
- You can tolerate loss of recent logs (up to
flushToDiskInterval) - Performance is critical (background workers, high-volume systems)
⚠️ Minor Risks
- On a crash, buffered (unflushed) entries are lost and the file may end with a partially written session or frame. This is a completeness/data-loss concern rather than a confidentiality one: because each session generates a fresh random AES key and nonce, and the decryptor skips incomplete trailing data, a crash does not cause key or nonce reuse.
- Nonce-counter wrapping within a single session is not explicitly handled. Each session uses a 96-bit AES-GCM nonce — a 32-bit random prefix plus a 64-bit counter — so wrapping would require 2^64 encryptions in one continuous session before the counter cycles.
- At 1 million logs/second, that is roughly 585,000 years.
Key Rotation
Assign a unique keyId to each key generation cycle. The ID is embedded in every session header
so the decryption layer knows which private key to use without any manual lookup.
// Old deployment — key from 2025
hooks: new EncryptHooks(oldPublicKey, keyId: "my-app-key-2025")
// New deployment — key from 2026
hooks: new EncryptHooks(newPublicKey, keyId: "my-app-key-2026")
For the decryption side of key rotation, see the Serilog.Sinks.File.Decrypt documentation.
Programmatic Key Generation
using Serilog.Sinks.File.Encrypt;
// Generate a new RSA key pair
var (publicKey, privateKey) = CryptographicUtils.GenerateRsaKeyPair();
// Save keys to files
File.WriteAllText("public_key.xml", publicKey);
File.WriteAllText("private_key.xml", privateKey);
Web Application Example
var builder = WebApplication.CreateBuilder(args);
// Load public key from configuration
string publicKeyXml = builder.Configuration["Logging:PublicKeyXml"]
?? throw new InvalidOperationException("Logging:PublicKeyXml is required");
builder.Host.UseSerilog((context, configuration) =>
configuration
.ReadFrom.Configuration(context.Configuration)
.WriteTo.File(
path: "logs/webapp-.log",
rollingInterval: RollingInterval.Day,
hooks: new EncryptHooks(publicKeyXml, keyId: "webapp-key-2026")));
var app = builder.Build();
app.Run();
API Reference
Key Management
// Generates a new RSA key pair with the specified key size and format (XML or PEM).
(string publicKey, string privateKey) CryptographicUtils.GenerateRsaKeyPair(int keySize = 2048, KeyFormat format = KeyFormat.Xml)
Encryption Hook
// publicKey — RSA public key in XML or PEM format
// keyId — optional identifier embedded in every session header (max 32 bytes UTF-8); default ""
// version — header format version; default 1 - Obsolete and has no effect anymore.
new EncryptHooks(string publicKey, string keyId = "", int version = 1)
Security Considerations
- Keep private keys secure and never include them in your application deployment
- Store private keys in secure key management systems in production (Azure Key Vault, AWS Secrets Manager, etc.)
- Use 2048-bit RSA keys minimum (4096-bit for enhanced security)
- Restrict filesystem access to encrypted log files and private keys
- Rotate keys periodically and use the
keyIdparameter to track which key encrypted which files
Threat model & known limitations
This package protects the confidentiality and per-frame integrity of your log data. It is not a tamper-evident or append-only log. Understand what it does and does not defend against before relying on it for security/audit purposes.
What is protected
- ✅ Confidentiality — log contents are encrypted with AES-256-GCM and the per-session key is wrapped with RSA-OAEP-SHA256. Reading the logs requires the private key.
- ✅ Per-frame integrity — every encrypted frame carries a 128-bit GCM authentication tag, so modifying the bytes of an existing frame is detected during decryption.
What is not protected (current format)
- ❌ Silent truncation, deletion, and reordering. The format provides no cryptographically verifiable completeness or ordering guarantee: frame ordering and the framing metadata are not covered by the per-frame authentication, and there is no end-of-log marker. An attacker with write access to a log file can drop trailing frames, or delete/reorder whole sessions, and decryption still succeeds on what remains — with no indication that anything is missing. Tampering by omission is invisible.
- ❌ Fabricated log entries. Encryption only requires the public key, which ships with your application. Anyone who has that public key and can write to the log file can generate their own AES session key, wrap it with the public key, and append entirely fabricated sessions. They cannot read or alter the contents of your existing sessions (that requires the private key), but they can add convincing-looking new ones. Preventing this requires a secret the attacker does not have — for example a symmetric MAC or a producer-side signing key kept off the public distribution — which this package does not currently provide.
If your use case needs tamper-evidence (for example security or audit logs), treat the encrypted file as confidential but not authoritative on completeness, and pair it with an external integrity mechanism such as append-only/WORM storage, remote log shipping, or signing.
A future major version will add a versioned format that binds frame ordering into the authenticated data and adds an optional end-of-log seal, making truncation and reordering detectable. See issue #83 for progress.
Migration
For step-by-step migration guides, see the CHANGELOG.md:
Versioning
All packages in this repository (Serilog.Sinks.File.Encrypt, Serilog.Sinks.File.Decrypt, Serilog.Sinks.File.Encrypt.Cli, Serilog.Sinks.File.Encrypt.Core) are released in lockstep. Every package is versioned and published together on every release, even when a change only affects one of them. Always use the same version across all packages you reference.
Requirements
- .NET 8.0 (LTS) or .NET 10.0 (LTS), or a compatible higher runtime
- A project using Serilog.Sinks.File
- RSA key pair in XML or PEM format (generated via CLI tool or programmatically)
Support policy: This library targets .NET Long-Term Support (LTS) releases only. A new LTS TFM is added when it ships; the oldest LTS TFM is dropped when Microsoft ends support for it. Users on STS or EOL runtimes can pin an older package version that targets a compatible LTS TFM.
Related Packages
| Package | Purpose |
|---|---|
Serilog.Sinks.File.Decrypt |
Programmatic decryption of encrypted log files |
Serilog.Sinks.File.Encrypt.Cli |
CLI tool for key generation and ad-hoc log decryption |
Serilog.Sinks.File.Encrypt.Core |
Shared cryptographic primitives (transitive dependency) |
| 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 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
- Serilog.Sinks.File (>= 7.0.0)
- Serilog.Sinks.File.Encrypt.Core (>= 5.1.0-preview.1)
-
net8.0
- Serilog.Sinks.File (>= 7.0.0)
- Serilog.Sinks.File.Encrypt.Core (>= 5.1.0-preview.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.1.0-preview.1 | 37 | 7/2/2026 |
| 5.0.0 | 94 | 6/29/2026 |
| 5.0.0-preview.4 | 50 | 6/29/2026 |
| 4.0.0 | 3,519 | 4/13/2026 |
| 4.0.0-preview.1 | 94 | 4/8/2026 |
| 3.0.0 | 1,079 | 3/23/2026 |
| 2.0.1-alpha.0.16 | 64 | 3/23/2026 |
| 2.0.0 | 4,146 | 12/2/2025 |
| 1.0.0 | 467 | 12/1/2025 |
| 0.50.1 | 236 | 11/27/2025 |
| 0.0.0-alpha.0.27 | 176 | 11/27/2025 |
| 0.0.0-alpha.0.17 | 184 | 11/25/2025 |
| 0.0.0-alpha.0.14 | 166 | 11/24/2025 |
| 0.0.0-alpha.0.13 | 173 | 11/24/2025 |
| 0.0.0-alpha.0.12 | 183 | 11/24/2025 |