# GPG/PGP - подпись, шифрование, web of trust _Протоколы · LinuxLab Knowledge Base_ **TL;DR:** GPG = open-source реализация OpenPGP. Пара ключей (RSA/ECDSA), публичный для verify/encrypt, приватный для sign/decrypt. keyring локально, web-of-trust между людьми. Используется в git-commit signing, package-signing (apt/rpm), email. ## Что есть 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. Создать: ```bash gpg --full-generate-key # interactive: выбрать тип, размер, expiry, name+email ``` Или non-interactive: ```bash gpg --batch --gen-key <" # gpg: WARNING: This key is not certified with a trusted signature! ``` Warning «not certified», потому что ключ не в web-of-trust для тебя. Это **ожидаемо** при первом импорте, проверка identity ключа на тебе. ## Шифрование (encrypt) Ключевое: **шифровать публичным ключом получателя**: ```bash # Импортировать публичный ключ Боба gpg --import bob-public.asc # Зашифровать ему gpg --encrypt --recipient bob@example.com message.txt # → message.txt.gpg # Боб расшифровывает своим приватным gpg --decrypt message.txt.gpg ``` Можно зашифровать **нескольким получателям** одновременно: ```bash 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 ``` Операции: ```bash 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, и **подписывает его ключ** своей подписью: ```bash 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/.gpg` ```bash 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 Самое массовое современное использование: ```bash # Один раз настроить 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 `). ## Package signing **APT** (Debian/Ubuntu): - Каждый репо подписывает `Release` файл своим GPG-ключом - APT хранит trusted keys в `/etc/apt/trusted.gpg.d/` - При `apt update` верифицирует подпись Release ```bash 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`: ```bash 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: ```bash gpg --edit-key alice@example.com > addkey > save ``` Экспорт **только subkeys** (без master): ```bash gpg --export-secret-subkeys alice@example.com > subkeys-only.asc # на ноуте импортировать только это ``` Хранение на YubiKey/HSM: ```bash gpg --edit-key alice@example.com > key 1 > keytocard # перенести encryption-subkey на YubiKey ``` ## Revocation Если приватный ключ утёрян/скомпрометирован, нужно опубликовать **revocation certificate**. Лучше сгенерить **сразу при создании**: ```bash gpg --output revoke.asc --gen-revoke alice@example.com ``` И сохранить в безопасном месте. При компрометации: ```bash 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` должен показать. Если нет, `pcscd` not running или USB permissions. - **WoT trust score «marginal»**, нужно подписать ключ сначала (`--sign-key`) или поднять trust manually (`--edit-key` → trust). - **Backup приватного ключа потеряли**, все зашифрованные ему данные **необратимо потеряны**. Только revoke. Никакого «password reset» в asymmetric crypto. ## Команды ```bash gpg --full-generate-key ``` Создать новую пару ключей с интерактивным выбором алгоритма и expiry ```bash gpg --list-keys --keyid-format LONG ``` Все публичные ключи в keyring с long-id (16 hex - безопаснее short) ```bash gpg --detach-sign release.tar.gz ``` Создать .sig файл рядом с release - стандартный паттерн для tarball'ов ```bash gpg --verify release.tar.gz.sig release.tar.gz ``` Проверить подпись - Good signature + проверка fingerprint глазами ```bash gpg --export --armor alice@example.com > alice.asc ``` Экспортировать публичный ключ в ASCII - для отправки или upload в keyserver ```bash git config --global commit.gpgsign true && git config --global user.signingkey ``` Включить подпись всех git-commit'ов выбранным ключом - GitHub покажет Verified ```bash curl -fsSL https://example.com/gpg-key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/example.gpg ``` Современный способ добавления apt-репо с явной trust-key (вместо apt-key) ## См. также - [SSH hardening - закрытие сервера](/kb/ssh-hardening.md) - [SMTP и MTA - доставка email](/kb/smtp-mta.md) - [Cosign и подпись container images (sigstore)](/kb/image-signing-cosign.md) - [TLS-сертификаты - X.509, цепочка доверия, Let's Encrypt](/kb/tls-certificates.md) - [Управление секретами - Vault, k8s Secrets, sealed-secrets](/kb/secrets-management.md)