Serilog.Sinks.File.Encrypt 6.0.0-preview.1

This is a prerelease version of Serilog.Sinks.File.Encrypt.
dotnet add package Serilog.Sinks.File.Encrypt --version 6.0.0-preview.1
                    
NuGet\Install-Package Serilog.Sinks.File.Encrypt -Version 6.0.0-preview.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="Serilog.Sinks.File.Encrypt" Version="6.0.0-preview.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Serilog.Sinks.File.Encrypt" Version="6.0.0-preview.1" />
                    
Directory.Packages.props
<PackageReference Include="Serilog.Sinks.File.Encrypt" />
                    
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 Serilog.Sinks.File.Encrypt --version 6.0.0-preview.1
                    
#r "nuget: Serilog.Sinks.File.Encrypt, 6.0.0-preview.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 Serilog.Sinks.File.Encrypt@6.0.0-preview.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=Serilog.Sinks.File.Encrypt&version=6.0.0-preview.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Serilog.Sinks.File.Encrypt&version=6.0.0-preview.1&prerelease
                    
Install as a Cake Tool

Serilog.Sinks.File.Encrypt

Build Status codecov NuGet NuGet Downloads License: MIT

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.

v6.0.0 Breaking Changes The writer now emits the v2 on-disk format: every frame binds its session and position into the AES-GCM authenticated data, and a cleanly closed log ends with an authenticated end-of-log seal, making truncation, reordering, and splicing detectable (#83).

  • v6 readers still decrypt v1-format files produced by v3.x–v5.x; v5.x and earlier cannot read v6 files.
  • The ignored version parameter was removed from EncryptHooks and EncryptionOptions.
  • See the CHANGELOG for the full migration guide.

Features

  • Hybrid Encryption: Uses RSA for key exchange and AES-GCM for efficient, authenticated data encryption
  • Tamper-Evident Framing (v2): Frame order, session identity, and header metadata are bound into the AES-GCM authenticated data; a sealed log's truncation is cryptographically detectable
  • 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 (v6.0.0 benchmark run, including the v2 format's per-frame authenticated data and end-of-log seal):

  • ~5-13% time overhead in real-world unbuffered scenarios (well under typical targets)
  • ~1.00x memory — allocations are effectively identical to unencrypted logging
  • Integrity effectively free — the v2 tamper-evidence features add no measurable cost over plain AES-GCM
  • 🚀 Buffered mode is dramatically faster than non-encrypted unbuffered — up to ~5x at high volume
  • 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, and without the end-of-log seal. 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. The decryptor reports such sessions as unsealed (see SealStatus in the Decrypt package) rather than silently treating them as complete.
  • 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 ""
new EncryptHooks(string publicKey, string keyId = "")

On-disk format (v2)

Each file open starts a session: a header (magic(8) + version(1) + keyId(32) + RSA-OAEP-SHA256(AES key ‖ nonce)), followed by self-framing AES-256-GCM frames (length(4, big-endian) + ciphertext + tag(16)). In v2, every frame authenticates 41 bytes of associated data — the SHA-256 hash of the session header, the frame's sequence number, and a frame-type byte — so dropped, reordered, duplicated, or cross-session-spliced frames fail authentication. On clean close (Dispose/Log.CloseAndFlush()) the writer appends a 28-byte authenticated seal record carrying the final frame count, encrypted under a reserved nonce so it stays verifiable even if trailing frames are missing.

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 keyId parameter to track which key encrypted which files

Threat model & known limitations

This package protects the confidentiality and integrity of your log data, and — as of the v2 format — makes tampering within a session cryptographically detectable. Understand what it does and does not defend against before relying on it for security/audit purposes.

What is protected (v2 format, v6.0.0+)

  • 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.
  • Frame ordering and completeness within a session — each frame's associated data binds the session header (version, keyId, wrapped key) and the frame's sequence number. Dropping, reordering, duplicating, or splicing frames between sessions fails authentication.
  • Truncation of a cleanly closed log — a sealed session's authenticated frame count exposes removed trailing frames (SealCountMismatch). A session with no seal is reported as unsealed: either the application crashed or the tail was truncated — the two are byte-for-byte indistinguishable by design, so the decryptor reports the ambiguity instead of silently claiming completeness.

What is not protected

  • Fabricated sessions. 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 inject frames into your existing sessions — the AAD binding prevents that). They cannot read or alter the contents of your existing sessions (that requires the private key). Preventing whole-session fabrication 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.
  • Deletion of entire sessions. Sessions are independently keyed; nothing chains one session to the next, so removing a whole session (e.g. one rolled file's worth) from the middle of a multi-session file is not detectable by the format itself.

If your use case needs full tamper-evidence against a writer with the public key (for example security or audit logs), pair the encrypted file with an external integrity mechanism such as append-only/WORM storage, remote log shipping, or signing.

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.

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
6.0.0-preview.1 0 7/6/2026
5.1.0-preview.1 37 7/2/2026
5.0.0 95 6/29/2026
5.0.0-preview.4 52 6/29/2026
4.0.0 3,538 4/13/2026
4.0.0-preview.1 95 4/8/2026
3.0.0 1,080 3/23/2026
2.0.1-alpha.0.16 65 3/23/2026
2.0.0 4,153 12/2/2025
1.0.0 468 12/1/2025
0.50.1 237 11/27/2025
0.0.0-alpha.0.27 177 11/27/2025
0.0.0-alpha.0.17 185 11/25/2025
0.0.0-alpha.0.14 167 11/24/2025
0.0.0-alpha.0.13 174 11/24/2025
0.0.0-alpha.0.12 184 11/24/2025