Serilog.Sinks.File.Encrypt 2.0.0

dotnet add package Serilog.Sinks.File.Encrypt --version 2.0.0
                    
NuGet\Install-Package Serilog.Sinks.File.Encrypt -Version 2.0.0
                    
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="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Serilog.Sinks.File.Encrypt" Version="2.0.0" />
                    
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 2.0.0
                    
#r "nuget: Serilog.Sinks.File.Encrypt, 2.0.0"
                    
#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@2.0.0
                    
#: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=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Serilog.Sinks.File.Encrypt&version=2.0.0
                    
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 encryption. This package provides secure logging by encrypting log data before writing to disk, ensuring sensitive information remains protected.

🚧 Newly Released 🚧 This library is newly released. APIs may change in future versions. Please report any issues you encounter or suggestions for improvement.

Features

  • Hybrid Encryption: Uses RSA encryption for key exchange and AES for efficient data encryption
  • Seamless Integration: Plugs directly into Serilog.File.Sink using file lifecycle hooks
  • Memory-Optimized: Producer-consumer architecture for efficient processing of large files
  • CLI Tool Integration: Companion CLI tool for key generation and log decryption
  • 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:

  • 6-17% time overhead in real-world scenarios (well under typical targets)
  • 200K+ logs/second throughput with buffered writes
  • 1.6-2.2x memory overhead for typical usage patterns
  • 🚀 Buffered writes recommended - Encrypted buffered I/O outperforms non-encrypted unbuffered

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 key management and decryption capabilities, also 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 (default: unbuffered for data safety)
Log.Logger = new LoggerConfiguration()
    .WriteTo.File(
        path: "logs/app.log",
        hooks: new EncryptHooks(publicKeyXml))
    .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 to reduce overhead from 15-17% to 6-8%. See the Advanced Usage section below.

3. Decrypt Logs

Use the CLI tool to decrypt your log files:

serilog-encrypt decrypt --key ./keys/private_key.xml --file logs/app.log --output logs/app-decrypted.log

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,              // Enables high-performance mode
        flushToDiskInterval: TimeSpan.FromSeconds(1),
        hooks: new EncryptHooks(publicKeyXml))
    .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)

Programmatic Key Generation

using Serilog.Sinks.File.Encrypt;

// Generate a new RSA key pair
var (publicKey, privateKey) = EncryptionUtils.GenerateRsaKeyPair(4096);

// Save keys to files
File.WriteAllText("public_key.xml", publicKey);
File.WriteAllText("private_key.xml", privateKey);

Programmatic Decryption

For large files, use the memory-optimized streaming API:

using Serilog.Sinks.File.Encrypt;

// File-to-file decryption
string privateKeyXml = File.ReadAllText("private_key.xml");
await EncryptionUtils.DecryptFileLogAsync(
    "logs/app.log", 
    "logs/decrypted.log", 
    privateKeyXml);

// Stream-to-stream decryption with custom options
var options = new StreamingOptions 
{
    BufferSize = 64 * 1024,  // 64KB chunks
    QueueDepth = 20,         // Queue depth
    ContinueOnError = true   // Continue on corruption
    ErrorHandlingMode = ErrorHandlingMode.WriteToErrorLog, // Log errors to a separate file
    ErrorLogPath = "decryption-errors.log" // Custom error log path
};

using var input = File.OpenRead("large-log.encrypted");
using var output = File.Create("large-log.decrypted");
await EncryptionUtils.DecryptLogFileAsync(input, output, privateKey, options);

Error Handling Modes

Choose how to handle decryption errors:

using Serilog.Sinks.File.Encrypt.Models;

// Skip corrupted sections silently (DEFAULT - ideal for JSON/structured logs)
var skipOptions = new StreamingOptions 
{
    ErrorHandlingMode = ErrorHandlingMode.Skip  // This is the default
};

// Write error messages inline to output (use only for human-readable logs)
var inlineOptions = new StreamingOptions 
{
    ErrorHandlingMode = ErrorHandlingMode.WriteInline
};

// Write errors to separate log file (for troubleshooting)
var errorLogOptions = new StreamingOptions 
{
    ErrorHandlingMode = ErrorHandlingMode.WriteToErrorLog,
    ErrorLogPath = "decryption-errors.log"
};

// Throw exception on first error (strict validation)
var strictOptions = new StreamingOptions 
{
    ErrorHandlingMode = ErrorHandlingMode.ThrowException,
    ContinueOnError = false
};

await EncryptionUtils.DecryptLogFileAsync(input, output, privateKey, skipOptions);

Error Handling Use Cases

Skip Mode - For Structured Logging (JSON, Compact JSON):

// Prevents corrupted error messages from breaking JSON parsing
var options = new StreamingOptions { ErrorHandlingMode = ErrorHandlingMode.Skip };
await EncryptionUtils.DecryptLogFileAsync(input, output, privateKey, options);

WriteToErrorLog Mode - For Production Troubleshooting:

// Clean output + separate error tracking
var options = new StreamingOptions 
{ 
    ErrorHandlingMode = ErrorHandlingMode.WriteToErrorLog,
    ErrorLogPath = "errors.log"  // Optional, auto-generated if omitted
};
await EncryptionUtils.DecryptLogFileAsync(input, output, privateKey, options);

ThrowException Mode - For Data Integrity Validation:

// Fail fast on any corruption
var options = new StreamingOptions 
{ 
    ErrorHandlingMode = ErrorHandlingMode.ThrowException,
    ContinueOnError = false
};

try 
{
    await EncryptionUtils.DecryptLogFileAsync(input, output, privateKey, options);
}
catch (CryptographicException ex)
{
    Console.WriteLine($"Decryption failed: {ex.Message}");
}

Web Application Example

var builder = WebApplication.CreateBuilder(args);

// Load public key from configuration
string publicKeyXml = builder.Configuration["Logging:PublicKeyXml"];

builder.Host.UseSerilog((context, configuration) =>
    configuration
        .ReadFrom.Configuration(context.Configuration)
        .WriteTo.File(
            path: "logs/webapp-.log",
            rollingInterval: RollingInterval.Day,
            hooks: new EncryptHooks(publicKeyXml)));

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

API Reference

Key Management

(string publicKey, string privateKey) EncryptionUtils.GenerateRsaKeyPair(int keySize = 2048)

Decryption

// File-to-file async decryption
Task EncryptionUtils.DecryptLogFileAsync(string encryptedFilePath, string outputFilePath, string rsaPrivateKey, StreamingOptions? options = null, CancellationToken cancellationToken = default)

// Stream-to-stream async decryption  
Task EncryptionUtils.DecryptLogFileAsync(Stream inputStream, Stream outputStream, string rsaPrivateKey, StreamingOptions? options = null, CancellationToken cancellationToken = default)

StreamingOptions

public class StreamingOptions
{
    public int BufferSize { get; init; } = 16 * 1024;                   // 16KB default
    public int QueueDepth { get; init; } = 10;                          // Queue depth
    public bool ContinueOnError { get; init; } = true;                  // Error handling
    public ErrorHandlingMode ErrorHandlingMode { get; init; } = Skip;   // Error mode (default: Skip)
    public string? ErrorLogPath { get; init; }                          // Error log file path
}

ErrorHandlingMode

public enum ErrorHandlingMode
{
    Skip = 0,              // Skip errors silently (DEFAULT - safe for all log formats)
    WriteInline = 1,       // Write error messages inline (use only for human-readable logs)
    WriteToErrorLog = 2,   // Write errors to separate log file
    ThrowException = 3     // Throw exception on first error
}

Security Considerations

  • Keep private keys secure and never include them in your application deployment
  • Store private keys in secure key management systems in production
  • Use 2048-bit RSA keys minimum (4096-bit for enhanced security)
  • Restrict access to encrypted log files and private keys

CLI Tool

The companion CLI tool provides key management and decryption with full error handling control:

# Generate keys
serilog-encrypt generate --output /path/to/keys

# Decrypt with default settings
serilog-encrypt decrypt --key private_key.xml --file log.txt --output decrypted.txt

# Decrypt with error handling options
serilog-encrypt decrypt -k key.xml -f log.txt -o out.txt -e Skip                    # Skip errors
serilog-encrypt decrypt -k key.xml -f log.txt -o out.txt -e WriteToErrorLog --error-log errors.log  # Log errors
serilog-encrypt decrypt -k key.xml -f log.txt -o out.txt -e ThrowException          # Fail on errors

For detailed CLI documentation, see the CLI tool documentation.

Requirements

  • .NET 8.0 or higher
  • A project using Serilog.Sinks.File
  • RSA key pair for encryption/decryption in XML format (generated via CLI tool or programmatically)
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
2.0.0 657 12/2/2025
1.0.0 405 12/1/2025
0.50.1 171 11/27/2025
0.0.0-alpha.0.27 126 11/27/2025
0.0.0-alpha.0.17 135 11/25/2025
0.0.0-alpha.0.14 130 11/24/2025
0.0.0-alpha.0.13 126 11/24/2025
0.0.0-alpha.0.12 133 11/24/2025