Что есть OpenPGP
OpenPGP (RFC 4880), стандарт асимметричной криптографии для data в покое: подписи и шифрование. GPG (GnuPG), main FOSS-имплементация на Linux/macOS. Альтернативы: Sequoia-PGP (Rust, новый), RNP (для Thunderbird).
Назначение исторически:
- Шифрованная email-переписка между людьми без CA
- Подпись release-артефактов (tar.gz, deb, rpm)
- Web of trust, доверие через подписанные публичные ключи
Сегодня:
- Git-commit signing (
git commit -S) - Package signing: APT-репо, RPM-репо, npm-publish
- secrets-tool (pass) для хранения паролей
- Sign tarball в release notes (исторически, для верификации)
Email-encryption практически вымерло, заменили Signal/Matrix.
Pair ключей
GPG-ключ, это связка: master-key (для подписи sub-keys) + N sub-keys (encryption, signing, authentication).
Алгоритмы:
- RSA 4096, классика, совместимость со всеми
- EdDSA Ed25519 для подписи, современный, faster, smaller
- Curve25519 (ECDH) для encryption, современный
Default в новых GnuPG, Ed25519 + Curve25519.
Создать:
gpg --full-generate-key
# interactive: выбрать тип, размер, expiry, name+email
Или non-interactive:
gpg --batch --gen-key <<EOF
Key-Type: EDDSA
Key-Curve: ed25519
Subkey-Type: ECDH
Subkey-Curve: cv25519
Name-Real: Alice Example
Name-Email: alice@example.com
Expire-Date: 2y
Passphrase: ...
EOF
Просмотр:
gpg --list-keys # публичные
gpg --list-secret-keys # с приватными
gpg --list-keys --keyid-format LONG # 16-hex fingerprint
Fingerprint и keyID
Каждый ключ имеет:
- Full fingerprint, 40 hex-символов (SHA1, 20 байт; или SHA256 в новых):
EB42 3B25 14F1 6F5C ... - Long key ID, last 16 hex characters:
EB423B2514F16F5C - Short key ID, last 8:
14F16F5C(опасно, легко сколлайдить в spoofing, НЕ использовать)
При импорте/верификации сравнивай по full fingerprint, а не по name/email/short-id.
Подпись (sign)
# Detached-signature (отдельный файл .sig)
gpg --detach-sign release-1.2.tar.gz
▸release-1.2.tar.gz.sig
# Embedded
gpg --sign message.txt # бинарный + signature внутри
gpg --clearsign message.txt # ASCII-armor (читаемый)
Verify:
gpg --verify release-1.2.tar.gz.sig release-1.2.tar.gz
# gpg: Good signature from "Alice <alice@example.com>"
# gpg: WARNING: This key is not certified with a trusted signature!
Warning «not certified», потому что ключ не в web-of-trust для тебя. Это ожидаемо при первом импорте, проверка identity ключа на тебе.
Шифрование (encrypt)
Ключевое: шифровать публичным ключом получателя:
# Импортировать публичный ключ Боба
gpg --import bob-public.asc
# Зашифровать ему
gpg --encrypt --recipient bob@example.com message.txt
▸message.txt.gpg
# Боб расшифровывает своим приватным
gpg --decrypt message.txt.gpg
Можно зашифровать нескольким получателям одновременно:
gpg --encrypt -r alice@... -r bob@... -r carol@... file
Internally, random session key (AES) + N copies этого session key, каждая зашифрована публичным ключом получателя.
Keyring
Локальное хранилище в ~/.gnupg/:
~/.gnupg/
├── pubring.kbx # public keys
├── private-keys-v1.d/ # приватные (новый формат)
├── trustdb.gpg # web-of-trust info
└── gpg-agent.conf # agent settings
Операции:
gpg --import alice.asc # импорт ключа
gpg --export alice@example.com > alice.asc # экспорт public
gpg --export-secret-keys alice > alice-private.asc # backup приватного
gpg --delete-key alice@example.com # удалить public
gpg --delete-secret-key ... # удалить приватный
ASCII-armor (-a или --armor), base64-обёртка для plaintext-
совместимости (email, pastebin):
-----BEGIN PGP PUBLIC KEY BLOCK-----
...
-----END PGP PUBLIC KEY BLOCK-----
Web of Trust
Концепция вместо CA: вы доверяете тому, кому доверяет тот, кому вы доверяете.
Когда Alice встречает Боба лично, проверяет его fingerprint, и подписывает его ключ своей подписью:
gpg --sign-key bob@example.com
# затем выложить обновлённый ключ Боба обратно
gpg --send-keys --keyserver hkps://keys.openpgp.org BOB_KEY_ID
Bob теперь импортирует и видит подпись Alice. Кто-то третий, кто доверяет Alice, теперь будет доверять Bob'у через её подпись.
Уровни trust в gpg:
- unknown, нет данных
- never, явно не доверять (отзыв доверия)
- marginal, требуется N таких подписей чтобы суммарно доверять
- full, одной подписи достаточно
- ultimate, свой собственный ключ
Минусы:
- Заметно сложно для юзеров
- Key-signing parties, вымершая практика
- Большинство людей сегодня просто TOFU (trust on first use)
- Заменено для большинства cases'ов на CA-style модели (sigstore/[[image-signing-cosign|cosign]], keyless через OIDC)
Keyservers
Раньше: загрузить публичный ключ на keyserver (hkps://pool.sks-keyservers.net),
кто угодно скачал. Сейчас:
- keys.openpgp.org, современный, valid email confirmation
- keyserver.ubuntu.com, для Ubuntu PPA
- GitHub,
gh keys add, доступно наhttps://github.com/<user>.gpg
gpg --keyserver hkps://keys.openpgp.org --recv-keys EB423B2514F16F5C
gpg --keyserver hkps://keys.openpgp.org --send-keys EB423B2514F16F5C
curl https://github.com/alice.gpg | gpg --import
Git commit signing
Самое массовое современное использование:
# Один раз настроить
git config --global user.signingkey EB423B2514F16F5C
git config --global commit.gpgsign true
# Подписывать commit
git commit -S -m "fix bug"
# Подписывать tag
git tag -s v1.2.3 -m "Release 1.2.3"
# Verify
git log --show-signature
git verify-tag v1.2.3
GitHub/GitLab верифицируют подпись → значок «Verified» на commit'е.
Защищает от identity spoofing (любой может закоммитить от
Linus Torvalds <torvalds@...>).
Package signing
APT (Debian/Ubuntu):
- Каждый репо подписывает
Releaseфайл своим GPG-ключом - APT хранит trusted keys в
/etc/apt/trusted.gpg.d/ - При
apt updateверифицирует подпись Release
curl -fsSL https://download.docker.com/linux/debian/gpg | \
sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg
RPM (RHEL/Fedora), то же, но через rpm --import:
rpm --import https://packages.example.com/RPM-GPG-KEY-myrepo
Без подписи apt/dnf будет ругаться warning'ом или ошибкой.
Subkeys и offline master
Best practice:
- Master key хранить offline (на USB, в сейфе, на YubiKey)
- Subkeys для повседневного использования (
Encrypt,Sign,Auth) на ноутбуке - Если subkey скомпрометирован, отозвать его, master целый
Создание subkey:
gpg --edit-key alice@example.com
> addkey
> save
Экспорт только subkeys (без master):
gpg --export-secret-subkeys alice@example.com > subkeys-only.asc
# на ноуте импортировать только это
Хранение на YubiKey/HSM:
gpg --edit-key alice@example.com
> key 1
> keytocard # перенести encryption-subkey на YubiKey
Revocation
Если приватный ключ утёрян/скомпрометирован, нужно опубликовать revocation certificate. Лучше сгенерить сразу при создании:
gpg --output revoke.asc --gen-revoke alice@example.com
И сохранить в безопасном месте. При компрометации:
gpg --import revoke.asc
gpg --keyserver hkps://keys.openpgp.org --send-keys EB423B...
# все, кто refresh'ит ключ, увидят revocation
GPG vs cosign vs Signed Commits
| | GPG | [[image-signing-cosign|cosign]] | git-signed-commits | |---|-----|--------|---------------------| | Identity | self-published key + WoT | OIDC-keyless | git config + GPG | | Поддержка signing | универсальна | только OCI artifacts | только git objects | | Когда выбрать | email, packages, file-sigs | container images, supply chain | git history integrity |
Когда что-то пошло не так
gpg: decryption failed: No secret key, у тебя нет приватного ключа для этого ciphertext. Проверьgpg --list-secret-keysи сверь key-id сgpg --list-packets file.gpg.gpg: signature ... not made by ... key, подпись валидна но другим ключом. Проверь fingerprint sender'а.Inappropriate ioctl for deviceпри--gen-keyв скрипте - pinentry хочет TTY. УстановитьGPG_TTY=$(tty)или использовать--pinentry-mode loopback --passphrase ....- gpg-agent не находит passphrase,
gpg-connect-agent reloadagent /byeили kill старый agent. apt updateругается NO_PUBKEY, не импортирован ключ репо, скачать с keyserver:apt-key adv --recv-keys(deprecated) или черезsigned-byв sources.list:deb [signed-by=/etc/apt/trusted.gpg.d/foo.gpg] http://repo ...
- YubiKey не виден,
gpg --card-statusдолжен показать. Если нет,pcscdnot running или USB permissions. - WoT trust score «marginal», нужно подписать ключ сначала
(
--sign-key) или поднять trust manually (--edit-key→ trust). - Backup приватного ключа потеряли, все зашифрованные ему данные необратимо потеряны. Только revoke. Никакого «password reset» в asymmetric crypto.