AgeSharp 0.2.0-preview.2

This is a prerelease version of AgeSharp.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package AgeSharp --version 0.2.0-preview.2
                    
NuGet\Install-Package AgeSharp -Version 0.2.0-preview.2
                    
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="AgeSharp" Version="0.2.0-preview.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AgeSharp" Version="0.2.0-preview.2" />
                    
Directory.Packages.props
<PackageReference Include="AgeSharp" />
                    
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 AgeSharp --version 0.2.0-preview.2
                    
#r "nuget: AgeSharp, 0.2.0-preview.2"
                    
#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 AgeSharp@0.2.0-preview.2
                    
#: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=AgeSharp&version=0.2.0-preview.2&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=AgeSharp&version=0.2.0-preview.2&prerelease
                    
Install as a Cake Tool

<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://github.com/FiloSottile/age/blob/main/logo/logo_white.svg"> <source media="(prefers-color-scheme: light)" srcset="https://github.com/FiloSottile/age/blob/main/logo/logo.svg"> <img alt="The age logo, a wireframe of St. Peters dome in Rome, with the text: age, file encryption" width="600" src="https://github.com/FiloSottile/age/blob/main/logo/logo.svg"> </picture> </p>

NuGet codecov

AgeSharp is a C# implementation of the age file encryption format, fully interoperable with the reference Go implementation and other age-compatible tools.

It depends only on BouncyCastle.Cryptography and targets .NET 10.

Features

  • All standard recipient types: X25519, scrypt/passphrase, SSH-Ed25519, SSH-RSA
  • Post-quantum ML-KEM-768-X25519 hybrid encryption
  • Plugin protocol — interoperates with age-plugin-* binaries
  • Encrypt to multiple recipients
  • ASCII armor support
  • Streaming encryption and decryption across all APIs — memory is bounded by a single 64 KiB chunk buffer regardless of input size (1 GiB file uses the same working set as a 1 MB file)
  • Pull-based streaming (EncryptReader / DecryptReader) returns a readable Stream for pipe-and-forget use cases
  • Detached header APIs (EncryptDetached / DecryptDetached)
  • Random-access decryption (AgeRandomAccess) — seek into encrypted files
  • Header inspection without decryption (AgeHeader.Parse)
  • Encrypted identity files (passphrase-protected)
  • Recipients file parsing (-R style files with comments)
  • Fully interoperable — files produced by AgeSharp decrypt with age, rage, and vice versa

Installation

dotnet add package AgeSharp

Usage

Encrypt and decrypt

using Age;
using Age.Recipients;

using var identity = X25519Identity.Generate();
var recipient = identity.Recipient;

using var input = new MemoryStream("Hello, age!"u8.ToArray());
using var encrypted = new MemoryStream();
AgeEncrypt.Encrypt(input, encrypted, recipient);

encrypted.Position = 0;
using var decrypted = new MemoryStream();
AgeEncrypt.Decrypt(encrypted, decrypted, identity);

Passphrase encryption

var passphrase = new ScryptRecipient("correct-horse-battery-staple");

using var input = new MemoryStream("Hello, age!"u8.ToArray());
using var encrypted = new MemoryStream();
AgeEncrypt.Encrypt(input, encrypted, passphrase);

encrypted.Position = 0;
using var decrypted = new MemoryStream();
AgeEncrypt.Decrypt(encrypted, decrypted, passphrase);

ASCII armor

AgeEncrypt.Encrypt(input, encrypted, armor: true, recipient);

// -----BEGIN AGE ENCRYPTED FILE-----
// YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA...
// -----END AGE ENCRYPTED FILE-----

Multiple recipients

using var alice = X25519Identity.Generate();
using var bob = X25519Identity.Generate();

AgeEncrypt.Encrypt(input, encrypted, alice.Recipient, bob.Recipient);

// Either identity can decrypt
AgeEncrypt.Decrypt(encrypted, decrypted, bob);

SSH keys

var recipient = SshEd25519Recipient.Parse("ssh-ed25519 AAAA...");
var identity = SshEd25519Identity.CreateFromFile("/path/to/id_ed25519");

AgeEncrypt.Encrypt(input, encrypted, recipient);

encrypted.Position = 0;
AgeEncrypt.Decrypt(encrypted, decrypted, identity);

Post-quantum (ML-KEM-768-X25519)

using var identity = MlKem768X25519Identity.Generate();
var recipient = identity.Recipient;

AgeEncrypt.Encrypt(input, encrypted, recipient);

Pull-based streaming

Returns a readable Stream — header and key setup is eager, payload encryption/decryption is lazy (chunk-by-chunk on Read()).

// Encrypt: returns a Stream you read ciphertext from
using var encryptedStream = AgeEncrypt.EncryptReader(plaintext, recipient);
encryptedStream.CopyTo(networkStream);

// Decrypt: returns a Stream you read plaintext from
using var decryptedStream = AgeEncrypt.DecryptReader(ciphertext, identity);
decryptedStream.CopyTo(outputStream);

Detached headers

Splits the age header and payload into separate streams — useful for storing the header and payload in different locations.

// Encrypt with separate header and payload
AgeEncrypt.EncryptDetached(input, headerOutput, payloadOutput, recipient);

// Decrypt from separate streams
AgeEncrypt.DecryptDetached(headerInput, payloadInput, output, identity);

Random-access decryption

Seek into an encrypted file and decrypt individual chunks without reading the whole file — useful for encrypted archives, databases, and large files.

using var ra = new AgeRandomAccess(ciphertext, identity);

Console.WriteLine($"Plaintext length: {ra.PlaintextLength}");

// Read 100 bytes at offset 50000
var buf = new byte[100];
ra.ReadAt(50000, buf);

// Or get a seekable Stream
using var stream = ra.GetStream();
stream.Seek(50000, SeekOrigin.Begin);
stream.Read(buf);

Header inspection

Parse the header of an encrypted file without decrypting it.

var header = AgeHeader.Parse(stream);

Console.WriteLine($"Recipients: {header.RecipientCount}");
Console.WriteLine($"Armored: {header.IsArmored}");
Console.WriteLine($"Payload offset: {header.PayloadOffset}");

foreach (var stanza in header.Recipients)
    Console.WriteLine($"  {stanza.Type}: {stanza.Args[0]}");

Parse existing keys

using var identity = AgeKeygen.ParseIdentity("AGE-SECRET-KEY-1...");
var recipient = AgeKeygen.ParseRecipient("age1...");

Custom recipients and identities

Implement IRecipient and IIdentity to integrate custom key types, remote secrets managers, or age plugins.

public class MyRecipient : IRecipient
{
    public string? Label => null; // or a security label to prevent mixing

    public Stanza Wrap(ReadOnlySpan<byte> fileKey)
    {
        // Wrap the file key using your custom scheme
        return new Stanza("MyType", ["arg1"], wrappedKey);
    }
}

public class MyIdentity : IIdentity
{
    public byte[]? Unwrap(Stanza stanza)
    {
        // Called once per stanza in the file's header. Return the file
        // key if this identity matches the stanza, null if not.
    }
}

CLI

AgeSharp ships a CLI compatible with the age command.

# Encrypt
age -r age1... -o encrypted.age plaintext.txt

# Decrypt
age -d -i key.txt -o plaintext.txt encrypted.age

# Generate a key pair
age-keygen -o key.txt

# Inspect an encrypted file (no decryption needed)
age inspect encrypted.age

Development

make            # Build universal macOS binary (AOT)
make build      # Framework-dependent build
make test       # Run all tests
make bench      # Run BenchmarkDotNet benchmarks
make interop    # Interoperability tests vs Go age CLI

Benchmarks

See docs/BENCHMARKS.md for detailed benchmark results comparing AgeSharp with the Go and Rust implementations.

Feature comparison

See docs/FEATURE_COMPARISON.md for a detailed comparison with the Go reference implementation and Rust's rage.

See also

  • age-encryption.org/v1 — the age format specification
  • age — the reference Go implementation and CLI
  • rage — a Rust implementation of age
  • awesome-age — age plugins, tools, and integrations
Product Compatible and additional computed target framework versions.
.NET 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 (1)

Showing the top 1 NuGet packages that depend on AgeSharp:

Package Downloads
Nops.Core

In-process SOPS decryption for .NET - format parsing, MAC verification, and age/PGP master keys with zero native dependencies.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0-preview.4 683 7/28/2026
0.2.0-preview.3 625 7/23/2026
0.2.0-preview.2 365 4/20/2026
0.2.0-preview.1 75 4/20/2026
0.1.0-preview.4 93 2/25/2026
0.1.0-preview.3 86 2/25/2026
0.1.0-preview.2 88 2/25/2026
0.1.0-preview.1 85 2/24/2026