Install age (brew install age on macOS, apt install age on Linux), generate recipient keys with age-keygen, then encrypt files using age -r [public-key] file.txt > file.txt.age and decrypt with age -d file.txt.age. For best results, share your public key through a trusted channel, have recipients encrypt their files using your key, and decrypt using your private key. Age is simpler than GPG, works with modern cryptography (ChaCha20-Poly1305), and requires no key servers or complex configuration.
Installing AGE
The installation process varies by operating system, but most developers can install age with a single command. On macOS, use Homebrew:
brew install age
On Linux, age is available through most package managers. For Debian-based distributions:
sudo apt install age
For other platforms, download pre-built binaries from the GitHub releases page. Verify the installation by running:
age --version
You should see version information confirming a successful installation.
Generating Encryption Keys
Before encrypting files, you need to generate a keypair. AGE supports two types of keys: identity files (password-protected) and SSH keys. For most use cases, generating a dedicated identity file provides the best balance of security and convenience.
Generate a new identity file with:
age-keygen -o age-identity.txt
This command creates two outputs: a private key saved to age-identity.txt and a public key displayed in the terminal. The public key looks something like:
age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5eu9rq
Protect your identity file. Anyone with access to this file can decrypt messages encrypted for your public key. Store it in a secure location, such as a password manager or encrypted directory.
If you prefer using existing SSH keys, age can generate the corresponding age public key from your SSH key:
age-keygen -y ~/.ssh/id_ed25519
This outputs the age-format public key derived from your SSH private key, allowing you to use existing credentials for age encryption.
Encrypting Files
With your keypair ready, encrypting files becomes straightforward. The basic syntax uses age with the -p flag for password-based encryption, or the -r flag for recipient-based encryption using public keys.
For password-based encryption:
age -p -o sensitive-data.tar.gz.age sensitive-data.tar.gz
This prompts for a passphrase and outputs the encrypted file. The -p flag adds password-based key derivation using Argon2, making the encryption resistant to brute-force attacks.
For recipient-based encryption using your public key:
age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5eu9rq -o document.pdf.age document.pdf
Replace the public key with your generated or derived key. You can specify multiple recipients by adding additional -r flags:
age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5eu9rq \
-r age1anotherpublickeyhere -o file.tar.gz.age file.tar.gz
This allows multiple people to decrypt the same file using their respective private keys.
For encrypting directories, combine age with tar:
tar czf - /path/to/directory | age -p -o backup.tar.gz.age
Decrypting Files
Decryption requires the corresponding private key or the correct passphrase. The age-decrypt command handles both recipient-based and password-based encrypted files.
Using your identity file:
age-decrypt -i age-identity.txt document.pdf.age
This outputs the decrypted content to stdout. To save to a file, use the -d flag or redirect output:
age-decrypt -i age-identity.txt -o document.pdf document.pdf.age
For password-encrypted files:
age-decrypt -p -o original-file.txt encrypted-file.txt.age
The -p flag prompts for the passphrase used during encryption.
Automation Patterns
Integrating age into scripts and workflows requires understanding how to pass keys securely. Avoid hardcoding keys in scripts. Instead, use environment variables or file references with appropriate permissions.
Passing a key from an environment variable:
export AGE_PRIVATE_KEY=$(cat age-identity.txt)
age-decrypt -i /dev/stdin encrypted-file.age
For automated backups with age:
#!/bin/bash
BACKUP_FILE="backup-$(date +%Y%m%d).tar.gz"
tar czf "$BACKUP_FILE" /important/data
age -r age1publickey -o "$BACKUP_FILE.age" "$BACKUP_FILE"
rm "$BACKUP_FILE"
This script creates a compressed backup, encrypts it for a specific recipient, and removes the unencrypted original.
Combine age with scp or rsync for secure file transfer:
tar czf - /local/data | age -p | ssh user@server "cat > remote-data.tar.gz.age"
The receiving end then decrypts with the appropriate key or passphrase.
Security Considerations
Age implements modern cryptographic primitives by default. The symmetric encryption uses ChaCha20-Poly1305, providing authenticated encryption that detects tampering. Key derivation uses Argon2id, resistant to both GPU and ASIC-based attacks when password-protected.
A few best practices improve your security posture. Never commit identity files to version control. Add them to .gitignore or use a separate secrets management approach. Rotate keys periodically, especially for shared or team encryption. When sharing files with others, verify public keys through a separate channel to prevent man-in-the-middle attacks.
For team usage, consider a key management strategy where each team member has their own key, and encrypted files list all team members as recipients. This maintains individual key control while enabling collaborative access.
Comparison with GPG
Developers familiar with GPG might wonder why age exists. Age prioritizes simplicity and modern defaults over broad compatibility. GPG supports numerous algorithms, some outdated, and carries historical complexity from decades of development. Age chooses sane defaults—modern algorithms, no configuration required—and focuses on the most common use case: encrypting files for yourself or specific recipients.
For teams already using SSH, age’s SSH key compatibility reduces the credential management burden. You can encrypt files using keys you already use for server authentication.
Age Cryptography Deep-Dive
Understanding age’s cryptographic foundation ensures proper security assumptions:
ChaCha20-Poly1305 Algorithm
Age uses ChaCha20-Poly1305, a modern AEAD cipher providing both confidentiality and authenticity:
ChaCha20: Stream cipher providing confidentiality
- 256-bit key
- 96-bit nonce (unique per encryption)
- Faster on CPUs without AES hardware acceleration
- No known practical cryptanalysis attacks
Poly1305: Polynomial authentication
- 128-bit authentication tag
- Detects any bit manipulation of ciphertext
- Timing-safe implementation (resistant to timing attacks)
This combination ensures ciphertext cannot be decrypted incorrectly without detection.
Key Derivation Details
For password-based encryption, age uses Argon2id:
Parameters (default):
- Time cost: 3 iterations
- Memory cost: 64 MB
- Parallelism: 4 threads
- Output: 256-bit derived key
These defaults resist GPU attacks while remaining fast on modern hardware.
For public-key encryption, age uses Curve25519 for key agreement:
X25519: Elliptic curve Diffie-Hellman
- 256-bit security level
- Montgomery form (efficient point operations)
- No cofactor issues
- Widely considered cryptographically sound
Batch Encryption Operations
For processing many files:
#!/bin/bash
# Batch encrypt directory structure
RECIPIENT_KEY="age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5eu9rq"
SOURCE_DIR="./sensitive-data"
OUTPUT_DIR="./encrypted-backup"
mkdir -p "$OUTPUT_DIR"
# Encrypt each file, preserving directory structure
find "$SOURCE_DIR" -type f | while read -r file; do
# Calculate output path
relative_path="${file#$SOURCE_DIR/}"
output_path="$OUTPUT_DIR/$relative_path.age"
# Create output directory
mkdir -p "$(dirname "$output_path")"
# Encrypt file
age -r "$RECIPIENT_KEY" -o "$output_path" "$file"
# Print progress
echo "Encrypted: $relative_path"
done
# Create manifest of encrypted files
find "$OUTPUT_DIR" -type f -exec sha256sum {} \; > "$OUTPUT_DIR/manifest.sha256"
Integration with Backup Tools
Age integrates with backup workflows:
Restic Backup with Age
# Setup restic with age encryption
restic init -r /mnt/backups -e age
# Create age key for backup
age-keygen -o restic-key.txt
# Set environment variable
export RESTIC_PASSWORD_COMMAND="age -d -i restic-key.txt < /tmp/restic.age"
# Backup with encryption
restic -r /mnt/backups backup /important/data
# Verify backup
restic -r /mnt/backups check
# Restore when needed
restic -r /mnt/backups restore latest --target /restore/location
Tar + Age for Versioned Backups
#!/bin/bash
# Daily incremental backup with age
BACKUP_DATE=$(date +%Y%m%d-%H%M%S)
RECIPIENT="age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5eu9rq"
BACKUP_ROOT="/backups"
# Create tarball with modification time delta
find /data -type f -newer /tmp/last-backup-marker 2>/dev/null | \
tar czf - -T - | \
age -r "$RECIPIENT" -o "$BACKUP_ROOT/incremental-$BACKUP_DATE.tar.gz.age"
# Update marker for next run
touch /tmp/last-backup-marker
# List recent backups
ls -lh "$BACKUP_ROOT"/incremental-*.age | tail -5
Secure Key Sharing Protocols
Distributing keys securely is critical:
Out-of-Band Verification
#!/bin/bash
# Share key through multiple channels
# Primary: encrypted email
echo "Your age public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5eu9rq" | \
mail -s "Your encryption key" recipient@example.com
# Secondary: SMS with fingerprint (short form)
FINGERPRINT=$(echo "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5eu9rq" | \
sha256sum | cut -c1-16)
echo "Key fingerprint: $FINGERPRINT" | sms recipient
# Verify with voice call
# "For security, read back the fingerprint you received in SMS"
Shamir’s Secret Sharing for Keys
For high-security scenarios, split keys across trustees:
# Install ssss (Shamir's Secret Sharing)
brew install ssss
# Create key with 3-of-5 splitting
# Any 3 shares can recover the key
cat age-identity.txt | \
ssss-split -t 3 -n 5 > key-shares.txt
# Distribute each share to separate trustee
split -n l/5 key-shares.txt share_
# To recover
cat share_00 share_01 share_02 | \
ssss-combine > recovered-key.txt
Performance and Scalability
Age handles large-scale encryption efficiently:
# Performance testing
# Age on 1GB file (modern CPU)
time age -r "$KEY" -o file.1gb.age file.1gb
# Typical: 0.5-1.5 seconds
# Parallel encryption of many files
find data -type f | \
parallel age -r "$KEY" -o {}.age {}
# Memory usage: Minimal (~10MB regardless of file size)
# This is because age streams data rather than loading entirely
Decryption in Restricted Environments
Recovering files when tools are limited:
# On system without age installed:
# Compile minimal age decoder or use reference implementation
# Python implementation (partial, for testing)
import os
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
# This would require implementing full age format parsing
# For production, always compile/install proper age binary
Integration with Git for Encrypted Repositories
Store sensitive config in git with age encryption:
# Setup encrypted git attributes
echo "*.secret.txt diff=age" > .gitattributes
# Configure git filter
git config filter.age.clean "age -e -r $AGE_PUBLIC_KEY"
git config filter.age.smudge "age -d -i $AGE_IDENTITY_FILE"
# Track encrypted secrets
git add config.secret.txt
git commit -m "Add encrypted configuration"
# Developers with key can decrypt
git smudge .git/config.secret.txt > config.txt
Threat Model Analysis
Age provides protection against specific threats:
Protected against:
- Network eavesdropping during file transfer
- Cloud storage provider accessing your files
- Attacker who gains access to encrypted files
- Key recovery without passphrase (password-based)
NOT protected against:
- Attacker with your private key
- Malware on your system during encryption/decryption
- Brute-force of weak passphrases
- Side-channel attacks on implementation
Related Articles
- Best Secure File Sharing Tools for Teams Handling.
- How to Set Up Secure File Sharing for Sensitive Documents
- Onionshare Secure File Sharing Over Tor Network Setup And Us
- Secure File Sharing Tools Comparison: E2E Encrypted.
- Age Encryption Tool Tutorial for Developers
Built by theluckystrike — More at zovo.one