age или GPG — это выбор между маленьким инструментом шифрования с единственным назначением и полной реализацией OpenPGP. age (формат age-encryption.org/v1, спецификация опубликована в C2SP) шифрует файл для одного или нескольких получателей. Каждый получатель — короткая строка вида age1…, открытый ключ SSH или постквантовый ключ age1pq1…; для инкапсуляции ключа используется X25519 или гибридный ML-KEM, а для полезной нагрузки — ChaCha20-Poly1305. GnuPG реализует OpenPGP: шифрование, подписи, сертификацию ключей, связки ключей, серверы ключей, смарт-карты и интеграцию с электронной почтой, и за ним стоят десятилетия совместимости.
Коротко
| age | GnuPG (OpenPGP) | |
|---|---|---|
| Назначение | Только шифрование файлов | Шифрование, подпись, сертификация, управление ключами |
| Спецификация | age v1 (C2SP): один формат, никаких версий для согласования | RFC 9580 (2024) и RFC 9980 (2026); GnuPG следует LibrePGP |
| Открытый ключ | 62 символа (age1…); постквантовый — 1959 символов (age1pq1…) | Двоичный ключ с идентификаторами пользователя и самоподписями; 473 байта для пары Ed25519 и Cv25519 (измерено) |
| Алгоритмы | X25519, гибрид ML-KEM-768 + X25519, scrypt для парольных фраз, ChaCha20-Poly1305, HKDF и HMAC-SHA-256 | Множество: RSA, ECC, AES, Camellia, устаревшие шифры для старых данных; ML-KEM-768 + X25519 в GnuPG 2.5 |
| Настройки | Нет | Предпочтения, модели доверия, настройки алгоритмов и сжатия |
| Измеренный шифртекст для файла размером 26 байт | 226 байт (X25519); 1685 байт (постквантовый) | 203 байта (Cv25519); 1288 байт (подключ ML-KEM-768 + X25519) |
| Ключи SSH как получатели | Да (ssh-ed25519, ssh-rsa) | Нет |
| Аппаратные ключи | Через плагины: YubiKey в режиме PIV, TPM, Secure Enclave | Смарт-карты OpenPGP и ключи безопасности |
| Подписи | Нет, так задумано | Да |
| Поиск ключей | Нет; ключи получателей передаются напрямую | Серверы ключей, WKD, связки ключей |
| Большие файлы | Потоковая обработка фрагментами по 64 КиБ с аутентификацией; API расшифрования с произвольным доступом начиная с версии 1.3 | Потоковая обработка; один AEAD-пакет с разбиением на фрагменты |
Шифруем один и тот же файл обоими способами
Скрипт ниже шифрует секрет размером 26 байт для получателя age X25519, постквантового получателя age, подключа GnuPG Cv25519 и подключа GnuPG ML-KEM-768 + X25519, расшифровывает каждый результат и сравнивает размеры (age 1.3.2, GnuPG 2.5.21). Всё происходит во временном каталоге.
#!/bin/sh
dir=$(mktemp -d /tmp/agegpg.XXXXXX) # short path: gpg-agent sockets need one
cd "$dir" || exit 1
export GNUPGHOME="$dir/g"
mkdir -m 700 g
printf 'db_password=correct-horse\n' > secret.env # 26 bytes
echo "== age"
age-keygen -o key.txt 2> /dev/null
recipient=$(age-keygen -y key.txt)
echo "recipient: ${#recipient} characters"
age -r "$recipient" -o secret.env.age secret.env
age -d -i key.txt secret.env.age | cmp -s - secret.env && echo "decrypted: identical"
echo "ciphertext: $(wc -c < secret.env.age | tr -d ' ') bytes"
echo "== age, post-quantum recipient"
age-keygen -pq -o pq.txt 2> /dev/null
pqrecipient=$(age-keygen -y pq.txt)
echo "recipient: ${#pqrecipient} characters"
age -r "$pqrecipient" -o secret.env.pq.age secret.env
age -d -i pq.txt secret.env.pq.age | cmp -s - secret.env && echo "decrypted: identical"
echo "ciphertext: $(wc -c < secret.env.pq.age | tr -d ' ') bytes"
echo "== GnuPG"
gpg --batch --quiet --passphrase '' --quick-gen-key "Ops <ops@example.org>" ed25519 cert 1y
fpr=$(gpg --list-keys --with-colons ops@example.org 2> /dev/null | awk -F: '/^fpr/ { print $10; exit }')
gpg --batch --quiet --passphrase '' --quick-add-key "$fpr" cv25519 encr 1y
gpg --batch --quiet --trust-model always -r ops@example.org -o secret.env.gpg -e secret.env
gpg --batch --quiet -d secret.env.gpg 2> /dev/null | cmp -s - secret.env && echo "decrypted: identical"
echo "ciphertext: $(wc -c < secret.env.gpg | tr -d ' ') bytes"
echo "public key export: $(gpg --export ops@example.org | wc -c | tr -d ' ') bytes binary"
echo "== GnuPG, ML-KEM-768 + X25519 subkey"
gpg --batch --quiet --passphrase '' --quick-gen-key "Ops PQ <ops-pq@example.org>" ed25519 cert 1y
fpr=$(gpg --list-keys --with-colons ops-pq@example.org 2> /dev/null | awk -F: '/^fpr/ { print $10; exit }')
gpg --batch --quiet --passphrase '' --quick-add-key "$fpr" ky768_cv25519 encr 1y
gpg --batch --quiet --trust-model always -r ops-pq@example.org -o secret.env.pq.gpg -e secret.env
gpg --batch --quiet -d secret.env.pq.gpg 2> /dev/null | cmp -s - secret.env && echo "decrypted: identical"
echo "ciphertext: $(wc -c < secret.env.pq.gpg | tr -d ' ') bytes"
gpgconf --kill gpg-agent
cd / && rm -rf "$dir"
== age
recipient: 62 characters
decrypted: identical
ciphertext: 226 bytes
== age, post-quantum recipient
recipient: 1959 characters
decrypted: identical
ciphertext: 1685 bytes
== GnuPG
decrypted: identical
ciphertext: 203 bytes
public key export: 473 bytes binary
== GnuPG, ML-KEM-768 + X25519 subkey
decrypted: identical
ciphertext: 1288 bytes
Оба инструмента добавляют фиксированный заголовок на каждого получателя и небольшие накладные расходы на аутентификацию, поэтому для реальных файлов разница пренебрежимо мала. Постквантовые накладные расходы — это шифртекст ML-KEM-768 (1088 байт), который передаётся в блоке каждого получателя; у age они больше в основном потому, что его заголовок текстовый, а двоичные значения записаны в base64. Скрипт использует --trust-model always только потому, что ключ создан секундой раньше; в реальной работе вы проверили бы отпечаток ключа получателя.
Важные детали совместимости
gpg --list-packets для файлов выше показывает две вещи, которые стоит знать. Для ключа Cv25519 GnuPG 2.5 записывает пакет OCB «AEAD encrypted packet» в формате LibrePGP, а не пакет зашифрованных данных версии 2, который определён в RFC 9580, потому что собственные ключи GnuPG объявляют о поддержке такого пакета; реализации, которые следуют только RFC 9580, могут не прочитать такие файлы. Для постквантового подключа GnuPG использует алгоритм открытого ключа 8 — свою составную схему Kyber из LibrePGP, тогда как RFC 9980 отводит для ML-KEM-768 + X25519 номер 35, поэтому постквантовые ключи GnuPG и ключи по RFC 9980 несовместимы. У age один формат и одна постквантовая конструкция (HPKE с гибридом ML-KEM-768 и X25519, известным также как X-Wing), и для неё опубликованы тестовые векторы.
Почему age намеренно маленький
В age нет всего того, из-за чего OpenPGP трудно использовать безопасно: нет согласования алгоритмов, нет сжатия (которое может раскрывать сведения об открытом тексте), нет режима без аутентификации, нет сервера ключей и нет сети доверия. Получатель — это строка, которую можно вставить в файл конфигурации, а в файле, зашифрованном для нескольких получателей, на каждого получателя приходится один блок. Цена — узкий круг задач: age не умеет подписывать, поэтому не доказывает, кто зашифровал файл, а также не управляет ключами и не распространяет их. Эти пробелы закрывают инструменты вокруг него: SOPS шифрует значения в YAML и JSON для получателей age, а плагины позволяют хранить закрытые ключи на YubiKey, в TPM или в Secure Enclave macOS.
Широта GnuPG ценна там, где OpenPGP — общий язык: подписанная и зашифрованная почта с теми, с кем вы уже переписываетесь, репозитории пакетов, подписи релизов, которые уже проверяют потребители ниже по цепочке, и смарт-карты OpenPGP, на которых вместе хранятся ключи подписи, шифрования и аутентификации.
Что когда выбирать
- Секреты в Git (SOPS), переменные CI, резервные копии, dotfiles: age, по одному получателю на каждого человека или машину плюс аппаратный или офлайн-получатель для восстановления.
- Шифрование для уже имеющегося у вас ключа SSH другого человека: age.
- Долгосрочные конфиденциальные архивы: age с постквантовыми получателями
age1pq1, чтобы противостоять стратегии «собери сейчас, расшифруй потом». - Зашифрованная почта: OpenPGP (или S/MIME, см. S/MIME или OpenPGP).
- Шифрование и подпись за один шаг для получателя, который пользуется OpenPGP: GnuPG.
- Подписи релизов и пакетов: ни age, ни GPG в роли инструмента шифрования; см. Sigstore или GPG.
Заметки о миграции
- Сгенерируйте закрытые ключи age для каждого человека или машины (
age-keygenилиage-keygen -pq, когда все, кто шифрует или расшифровывает, перейдут на age 1.3 или новее) и храните закрытый ключ для восстановления офлайн. - Перешифровывайте, а не конвертируйте: расшифруйте каждый файл GPG старым ключом и зашифруйте для новых получателей; преобразования формата не существует. Для SOPS добавьте получателей age в правила создания (creation rules) в
.sops.yamlи выполнитеsops updatekeysдля каждого файла; когда позже будете удалять ключи PGP, выполните после этогоsops rotate, чтобы сменился и ключ данных. - Сохраните ключ GPG, пока все старые файлы и резервные копии не будут перешифрованы или пока не истечёт срок их хранения.
- Замените подписи GPG отдельно подписями SSH, minisign или Sigstore: age подписей не создаёт.
- Проверьте размеры и инструменты с помощью инструмента шифрования age и изучите старые файлы в инспекторе OpenPGP, чтобы узнать, какие ключи и алгоритмы в них используются.