Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Repository files navigation
* AgeSharp C# implementation of the [[https://github.com/FiloSottile/age][Age]] file encryption format. =Age= is a simple, modern and secure file encryption tool. *Note: This is not affiliated with the official Age project.* ** Known Limitations Some features from the Age specification are not yet implemented: - Post-quantum encryption (ML-KEM/ML-DSA) ** Prerequisites - .NET 10 SDK ** Dependencies - [[https://www.nuget.org/packages/NSec.Cryptography/][NSec.Cryptography]] - X25519, ChaCha20-Poly1305, HKDF - [[https://www.nuget.org/packages/Norgerman.Cryptography.Scrypt/][Norgerman.Cryptography.Scrypt]] - Scrypt for passphrases ** Library Usage AgeSharp provides a complete C# library for encrypting and decrypting data. #+begin_src csharp using AgeSharp.Core; // Generate a new identity var identity = AgeKeyGenerator.GenerateX25519Key(); var recipient = identity.ToRecipient(); // Encrypt data var data = System.Text.Encoding.UTF8.GetBytes("Hello, World!"); var encrypted = await Age.EncryptAsync(data, new[] { recipient }); // Decrypt data var decrypted = await Age.DecryptAsync(encrypted, new[] { identity }); var message = System.Text.Encoding.UTF8.GetString(decrypted); #+end_src *** Using Passphrase #+begin_src csharp using AgeSharp.Core; // Create a passphrase recipient var passphrase = "your-passphrase"; var recipient = AgeParser.ParseRecipient(passphrase); // Encrypt data with passphrase var data = System.Text.Encoding.UTF8.GetBytes("Hello, World!"); var encrypted = await Age.EncryptAsync(data, new[] { recipient }); // Decrypt using passphrase identity var identity = AgeParser.ParseIdentity(passphrase); var decrypted = await Age.DecryptAsync(encrypted, new[] { identity }); var message = System.Text.Encoding.UTF8.GetString(decrypted); #+end_src ** Security Notes *** Memory Security For security, private keys and passphrases are stored as byte arrays and are zeroed in memory immediately after each encryption/decryption operation. This prevents sensitive key material from persisting in memory longer than necessary. - X25519 private keys are zeroed after each ~Unwrap()~ call - Scrypt passphrases are zeroed after each ~CreateStanza()~ or ~Unwrap()~ call - To decrypt multiple files, re-read the key file or re-enter the passphrase *** Passphrase Identities Passphrase identities cannot be serialized to a string. This prevents accidental leakage of passphrases in logs or debug output. Use ~AgeParser.ParseIdentity()~ with the original passphrase to decrypt. *** Using ASCII Armor #+begin_src csharp using AgeSharp.Core; // Encrypt with armor var options = new EncryptionOptions { Armor = true }; using var output = new MemoryStream(); await Age.EncryptAsync(input, output, recipients, options); var armored = System.Text.Encoding.ASCII.GetString(output.ToArray()); // Output: -----BEGIN AGE ENCRYPTED FILE----- // ... // -----END AGE ENCRYPTED FILE----- #+end_src *** Parsing Keys #+begin_src csharp using AgeSharp.Core; // Parse a recipient from a string var recipient = AgeParser.ParseRecipient("age1n0szz7y4u757g66s2qkmv7tmrtpq55h6ve9tvugutwttwtcs99jsqlrx6j"); // Parse an identity from a string var identity = AgeParser.ParseIdentity("AGE-SECRET-KEY-1RJD99JTCZLF60ACFAH9T34FF7XPFH5JF79VJNL8X3GA856KMDU5SZ0UU85"); // Parse recipients from a file var recipients = AgeParser.ParseRecipientsFile("recipients.txt"); // Parse identities from a file var identities = AgeParser.ParseIdentitiesFile("identities.txt"); #+end_src *** Inspecting Files #+begin_src csharp using AgeSharp.Core; // Inspect an encrypted file var info = AgeInspector.Inspect("file.age"); Console.WriteLine($"Version: {info.Version}"); Console.WriteLine($"Armor: {info.IsArmor}"); Console.WriteLine($"Post-quantum: {info.PostQuantum}"); Console.WriteLine($"Stanza types: {string.Join(", ", info.StanzaTypes)}"); Console.WriteLine($"Header size: {info.HeaderSize}"); Console.WriteLine($"Payload size: {info.PayloadSize}"); #+end_src ** Build #+begin_src shell dotnet build #+end_src ** Test #+begin_src shell dotnet test #+end_src ** CLI Usage *** age-keygen Generate a new X25519 identity key pair. #+begin_src shell # Generate key to stdout dotnet run --project src/AgeSharp.CLI.KeyGen # Generate key to file dotnet run --project src/AgeSharp.CLI.KeyGen -- -o key.txt # Convert identity to recipient (print public key) dotnet run --project src/AgeSharp.CLI.KeyGen -- -y -i key.txt #+end_src Output format: #+begin_example # created: 2026-02-27T00:30:33+02:00 # public key: age1n0szz7y4u757g66s2qkmv7tmrtpq55h6ve9tvugutwttwtcs99jsqlrx6j AGE-SECRET-KEY-1RJD99JTCZLF60ACFAH9T34FF7XPFH5JF79VJNL8X3GA856KMDU5SZ0UU85 #+end_example *** age (main CLI) Encrypt or decrypt files using X25519 keys or passphrases. #+begin_src shell # Encrypt a file to a recipient dotnet run --project src/AgeSharp.CLI.Age -- -r age1n0szz7y4u757g66s2qkmv7tmrtpq55h6ve9tvugutwttwtcs99jsqlrx6j -o output.txt.age input.txt # Encrypt with ASCII armor dotnet run --project src/AgeSharp.CLI.Age -- -r age1n0szz7y4u757g66s2qkmv7tmrtpq55h6ve9tvugutwttwtcs99jsqlrx6j -a -o output.txt.age input.txt # Decrypt a file dotnet run --project src/AgeSharp.CLI.Age -- --decrypt -i key.txt -o output.txt input.txt.age # Encrypt to multiple recipients dotnet run --project src/AgeSharp.CLI.Age -- -r recipient1 -r recipient2 -o output.txt.age input.txt #+end_src **** Options | Option | Description | |--------|-------------| | =-e=, =--encrypt= | Encrypt mode (default) | | =-d=, =--decrypt= | Decrypt mode | | =-r=, =--recipient= | Recipient public key or passphrase (can repeat) | | =-R=, =--recipients-file= | File containing recipients (can repeat) | | =-i=, =--identity= | Identity file for decryption (can repeat) | | =-a=, =--armor= | Use ASCII armor (PEM encoding) | | =-p=, =--passphrase= | Prompt for passphrase to encrypt | | =-o=, =--output= | Output file path | | =-h=, =--help= | Show help | *** age-inspect Inspect an age encrypted file to view its metadata. #+begin_src shell # Inspect a file dotnet run --project src/AgeSharp.CLI.Inspect -- file.age # Inspect and output as JSON dotnet run --project src/AgeSharp.CLI.Inspect -- --json file.age #+end_src ** Project Structure #+begin_example AgeSharp/ ├── src/ │ ├── AgeSharp.Core/ # Core library │ │ ├── Encoding/ # Base64, Bech32 utilities │ │ ├── Headers/ # Age header parsing/generation │ │ ├── Keys/ # Key types (X25519, etc.) │ │ └── Exceptions/ # Custom exceptions │ ├── AgeSharp.CommandLine/ # Command line parsing │ ├── AgeSharp.CLI.Age/ # Main CLI (age) │ ├── AgeSharp.CLI.KeyGen/ # Key generator (age-keygen) │ └── AgeSharp.CLI.Inspect/ # File inspector (age-inspect) ├── tests/ │ └── AgeSharp.Tests/ # Unit tests ├── doc/ # Documentation │ └── requirements.org # Detailed requirements ├── README.org # This file └── LICENSE # Apache 2.0 license #+end_example ** Specification See the [[https://age-encryption.org/v1][Age specification]] for detailed format information. ** License Licensed under the Apache License, Version 2.0. See [[file:LICENSE][LICENSE]] for details.