SharpAESCrypt: The .NET 8 Library That Encrypts Files Without Breaking the Past
A modern AESCrypt implementation for C# that balances strict security, legacy compatibility, and streaming performance.
- SharpAESCrypt treats compatibility as a security problem, not a convenience feature, so strict validation is the default and legacy padding tolerance is an explicit escape hatch.
- The library’s real strength is not the AES primitive itself, but the way it streams, buffers, and seals data without loading entire files into memory.
- Its .NET 8 implementation uses modern runtime features to make an old file format cleaner, faster, and easier to maintain.
- Inside Duplicati, SharpAESCrypt turns encryption into a portable building block rather than a product feature, which is why format fidelity matters so much.
The strange job of an encryption library is that it has to protect data and preserve history at the same time. SharpAESCrypt does that by enforcing a strict reading of the AESCrypt file format, while still offering a compatibility path for old files that were written with malformed padding.
That makes it more interesting than a straight crypto wrapper. It is a modern .NET 8 implementation that acts like a translator between clean security rules and the messy reality of legacy encrypted archives.
The AES header IV generation was slow as it queried the operating system for a MAC address on every call (and it had an error, resulting in a default value being used). Fixing the error and caching the MAC address improved the performance by up to 1.85 times.
What AESCrypt files actually look like
SharpAESCrypt does not invent a new encryption format. It implements a known one: AESCrypt, a portable file format built around AES-256, versioned headers, streamed payloads, and an integrity seal at the end. The format matters because interoperability only works when every implementation agrees on where the header ends, where the blocks begin, and how the file proves it has not been tampered with.
Why strict mode matters
The project’s most revealing switch is IgnorePaddingBytes. In strict mode, SharpAESCrypt behaves like a security gatekeeper. It refuses ambiguity, because padding mistakes can hide bugs, create malformed files, and weaken confidence in what a successful decrypt actually means.
But old files exist, and some were written by clients that did not behave cleanly. The compatibility flag is there to read those files without making the whole library permissive by default. That is a sensible trade-off: keep the safest path as the normal one, and make the exception explicit.
| Mode | What it optimizes for | Trade-off |
|---|---|---|
| Strict validation | Security and clear file semantics | Breaks malformed legacy files instead of guessing |
| Legacy compatibility | Interoperability with older archives | Accepts files that would otherwise fail validation |
| Default behavior | Safe operation for new data | Requires users to opt in when they need historical tolerance |
That is the real pattern in the repo. The library is not saying legacy files are fine. It is saying interoperability sometimes requires carefully bounded forgiveness.
How SharpAESCrypt streams data instead of loading it all at once
This is where the implementation earns its keep. The public API wraps stream objects, so encryption and decryption fit naturally into backup workflows, file pipelines, and other long-running I/O jobs. Internally, the code processes data in fixed-size blocks, builds up the final file incrementally, and verifies integrity at the end with HMAC-SHA256.
The important detail is not just that it streams. It streams in a way that keeps the file format honest. Padding is explicit. Block boundaries matter. Verification happens after the whole transfer, not as a side effect of writing bytes to disk.
// Conceptual shape of the API
using var output = File.Create("backup.aes");
using var encrypting = SharpAESCrypt.AESCrypt.Encrypt(output, password);
source.CopyTo(encrypting);
using var input = File.OpenRead("backup.aes");
using var decrypting = SharpAESCrypt.AESCrypt.Decrypt(input, password);
decrypting.CopyTo(destination);
That pattern is why the library works for backups instead of toy examples. Backups are about pipes, not blobs. A good encryption library for that world has to behave like a stream processor first and a serializer second.
Why the .NET 8 rewrite feels modern
The .NET 8 version reads like a deliberate cleanup of an older problem space. It leans on modern runtime features such as Span<T>, memory-conscious buffering, and narrow internal helpers that keep cryptographic state separate from the public surface area.
That separation matters. It makes the library easier to reason about, easier to test, and less likely to leak implementation details into every consumer’s code. In crypto, boring structure is a feature.
SharpAESCrypt vs the rest of the world
The project is best understood in contrast. It is not trying to outdo every encryption tool. It is trying to be the safest, most interoperable implementation of a specific file format inside the .NET ecosystem.
| Option | What it gives you | What you give up |
|---|---|---|
| SharpAESCrypt | Standard AESCrypt interoperability in modern C# | You stay inside a specific file format |
| Original AES Crypt | The canonical reference for the format | Older runtime and implementation style |
| Hand-rolled .NET crypto | Full control over primitives and layout | You must design and validate the format yourself |
| Restic or Borg | Whole-backup encryption and orchestration | You are choosing a different product model, not the same file-format problem |
That is the right framing. The real comparison is not just against other libraries. It is against the temptation to invent one more custom encryption scheme when a portable format already exists and already has a history.
Why this library matters inside Duplicati
Inside Duplicati, SharpAESCrypt is infrastructure. It helps turn user-controlled encryption into a portable guarantee, which is central to a zero-trust backup model. If the encrypted file cannot be read later, or by another implementation, then the promise of portability starts to fray.
That is why the library’s strictness, compatibility switch, and streaming design all point in the same direction. They are not separate concerns. They are the mechanics of making encryption reliable over time, across platforms, and across imperfect history.