One key to sign them all, one key to bind them, one key to log you in and—if stolen—quickly un-find them.
In an age of relentless credential leaks and “rotate your secrets now!” alerts, developers want fewer keys to love and fewer chances to slip. Reusing a well-protected GPG key for SSH logins is a smart, publicly documented trick—but it still isn’t mainstream. This guide shows you how to do it safely, why it beats juggling separate private keys, and which pitfalls to avoid. Strap in: by the time you finish, you’ll have a single hardware-backed key unlocking e-mail encryption and every server you own.
The 2025 Case for Key Consolidation
Back in 2015, most engineers kept a handful of private keys on disk, protected by a half-remembered passphrase. That was fine—until the DevOps explosion. Today you might authenticate to:
- A Git host (GitHub, GitLab, Bitbucket)
- A half-dozen cloud VMs
- Kubernetes jump boxes
- CI/CD runners
- A home-lab Raspberry Pi cluster
Every destination wants a distinct public key, auditors want rotation logs, and security teams want hardware tokens. The result is “key sprawl”: ten keys, ten passphrases, ten places to fat-finger.
GPG-SSH unification kills the sprawl in three ways:
- Single root of trust. Your master OpenPGP key already signs e-mails, packages, or Git commits. Extending its authority to SSH makes audits trivial.
- Hardware enforcement. Modern YubiKeys, Nitrokeys and SoloKeys speak both OpenPGP and FIDO2. One tap unlocks e-mail and servers; no files ever leave the chip.
- Revocation simplicity. Lose your token? Revoke one sub-key, issue a replacement, update authorized_keys with a script. Done.
In short: fewer secrets, stronger guarantees, lower cognitive load. Now let’s build it.
What Is GPG?
Ever wondered what is GPG and why security pros rave about it? Short for GNU Privacy Guard, GPG is the free, open-source successor to PGP, empowering you to encrypt, sign, and even authenticate everything from email to server logins. Picture a digital padlock with two halves: one public key you share freely and one private key you guard like gold. The result? Breaches become far less scary, and compliance audits feel almost painless.
Quick Primer—GPG, Sub-Keys, and SSH
Term | One-sentence translation |
GPG (GNU Privacy Guard) | Free implementation of the OpenPGP encryption-and-signing standard. |
Encrypted protocol for remote login, tunnelling, and Git over the network. | |
Asymmetric key pair | Public key (the lock) + private key (the key) = encrypted mailboxes and password-less logins. |
Master key | The top-level OpenPGP key that certifies all sub-keys; you guard it like gold. |
Sub-key | A child key that inherits trust from the master but can be rotated or revoked independently. |
Why all the fuss about sub-keys? Because they’re compartmentalisation in cryptographic form. You can tuck the master offline on an air-gapped laptop or a print-out in a vault, then create three sub-keys:
- Encryption (for messages/files)
- Signing (for e-mail, Git tags)
- Authentication (for SSH)
If the authentication sub-key ever leaks, you revoke only that child, issue a new one, and keep on signing releases with the untouched S-sub-key. Elegant, auditable, battle-tested.
Environment-Ready Checklist
- Operating system
- Linux (Debian/Ubuntu/Fedora/Arch)
- macOS (Intel/Apple Silicon)
- Windows 11 with WSL 2 (recommended) or native Gpg4win
- Linux (Debian/Ubuntu/Fedora/Arch)
Packages
# Debian/Ubuntu
sudo apt install gnupg2 openssh-client pinentry-gtk2 scdaemon
# macOS
brew install gnupg pinentry-mac yubikey-personalization
- Hardware token (optional but ideal)
- YubiKey 5C/5Ci/5NFC
- Nitrokey Start/3A, SoloKey v2
- YubiKey 5C/5Ci/5NFC
Why hardware? Keys inside silicon cannot be copied. A rogue root shell may still tamper with your OS, but it cannot steal metal.
Generate—or Extend—Your OpenPGP Key
Scenario A: fresh start
gpg –full-generate-key
Pick:
- (9) ECC and ECC → curve ed25519 (fast, quantum-resistant-ish)
- Usage: Sign, Encrypt, Certify
- Expiry: 3 years (you’ll renew sub-keys yearly)
Scenario B: upgrade an existing key
gpg –list-secret-keys –keyid-format LONG
gpg –edit-key ABCD1234EFGH5678
Inside the interactive prompt:
sql
CopyEdit
gpg> addkey
Please select what kind of key:
(1) RSA (sign only)
(8) ECC (sign only)
(9) ECC and ECC
Choose ECC then flag usage as Authenticate. Set an expiry (1 year recommended). Finish with save.
When you exit, gpg -K –with-subkey-fingerprint should list a new line marked A—your shiny SSH sub-key.
Turning GPG Agent into an SSH Agent
OpenSSH ships with its own agent (ssh-agent) that watches ~/.ssh/id_*. GPG has a rival: gpg-agent, which can masquerade as an SSH agent if you ask politely.
Edit ~/.gnupg/gpg-agent.conf
python
CopyEdit
enable-ssh-support
pinentry-program /usr/bin/pinentry-gtk-2 # or pinentry-mac
default-cache-ttl 900 # 15 min
max-cache-ttl 7200 # 2 h
Restart the agent
gpgconf –kill gpg-agent
eval “$(gpg-agent –daemon –enable-ssh-support)”
- That eval exports SSH_AUTH_SOCK pointing at GPG’s socket. Add it to your .bashrc or .zshrc.
Confirm
ssh-add -l
4096 ED25519 SHA256:abcd… cardno:FFFE12345678 (GPG: authentication)
If you see “agent has no identities,” the sub-key isn’t cached yet—use ssh-add -L after touching the token.
Export the Sub-Key in OpenSSH Format
gpg –export-ssh-key ABCD1234EFGH5678
You get one long ssh-ed25519 AAAAC3Nz… [email protected] line. Copy it verbatim to the remote server:
bash
CopyEdit
ssh user@server ‘mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys’
Finish with:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Pro tip: store the public line in your password manager labeled “GPG-SSH public” so you can paste it into any new VM in seconds.
First Login & Deep-Dive Debugging
Run:
ssh -o LogLevel=DEBUG3 user@server
Watch for these lines:
Offering public key: cardno:FFFE12345678
sign_and_send_pubkey: signing using cardno:FFFE12345678
Authentication succeeded (publickey).
Stalls?
- agent refused operation → token not unlocked. Run gpg –card-status; if it asks for PIN, enter it once—then try SSH again.
- no such identity → environment changed. Confirm echo $SSH_AUTH_SOCK shows your home dir’s .gnupg/S.gpg-agent.ssh, not /tmp/ssh-XXXX/agent.pid.
- PubkeyAlgorithms mismatch → the server only allows FIPS curves. Generate a cv25519 (X25519) or RSA 4096 auth sub-key.
Hardening with Hardware—Life on a YubiKey
A plastic dongle the size of a fingernail adds friction, but also super-powers:
Feature | Benefit | Command |
Touch-to-sign | Human presence check defeats malware & Supply-Chain 21 attacks | ykman openpgp set-touch aut on |
PIN & PUK | Brute-force throttle; 3 wrong PINs, then PUK needed | ykman openpgp set-pin-retries 3 10 |
PIV + OpenPGP | Same stick stores FIDO2, TOTP, ED25519, and PIV certs | ykman piv info |
Story from the field: A fintech startup in Tel Aviv switched 40 engineers from file-based SSH keys to YubiKey-backed sub-keys. In month one they halved help-desk tickets about “I lost my laptop” because a stolen laptop no longer meant “nuke every production key.” Audit time dropped from five days to two hours: security just grepped YubiKey serials in authorized_keys against HR’s roster.
Automating Sub-Key Rotation
Security auditors love the phrase “crypto agility.” Here’s a Bash/Ansible duo that rotates an auth sub-key every quarter:
#!/bin/bash
MASTER=$1 # 16-char keyid
# 1. Mint new auth sub-key (expires in 1 year)
echo -e “addkey\n8\nA\n1y\nsave\n” | gpg –command-fd 0 –edit-key $MASTER
NEW=$(gpg –list-keys –with-colons $MASTER | grep ‘^sub:’ | tail -1 | cut -d: -f5)
# 2. Export in SSH format
gpg –export-ssh-key $MASTER | tail -n 1 > /tmp/$NEW.pub
Then an Ansible playbook:
yaml
CopyEdit
– hosts: all
tasks:
– name: Push new GPG-SSH key
authorized_key:
user: “{{ ansible_user }}”
key: “{{ lookup(‘file’, ‘/tmp/’ + newkey + ‘.pub’) }}”
Finally, remove the previous public key with authorized_key state=absent once all hosts confirm new logins.
Git over GPG-Backed SSH
- Add key to GitHub
Settings → SSH & GPG Keys → New SSH Key → Paste.
Sign commits with the same token
git config –global user.signingkey ABCD1234EFGH5678
git commit -S -m “One token to push and sign”
- Single tap, dual effect. Push (git push origin main) triggers one tap for SSH auth; the signed commit piggybacks on GPG’s cached PIN— seamless developer flow.
Insider tip: GitHub recognizes the YubiKey’s sign only sub-key for commit signatures and the auth sub-key for SSH. You never expose the encryption sub-key on a server, keeping secret messages strictly P2P.
Advanced Use: Double-Wrapping Sensitive Files
For classified reports or customer databases in transit:
bash
CopyEdit
# Encrypt file for recipient
gpg –recipient 0xBEEFCAFE –encrypt payroll.csv
# Copy over SSH tunnel
scp payroll.csv.gpg finance@vault:/incoming/
Why bother if SSH is already encrypted? Layered defense. If the SSH endpoint is ever compromised (rogue root shell, Heartbleed-style memory dump), the captured file is still unreadable without the recipient’s private key.
Pair this with an MFT platform: configure a pre-transfer GPG encryption hook and a post-transfer decryption hook on the destination. Auditors tick off “data at rest encrypted” and “data in motion encrypted” in one glance.
Troubleshooting Cheat-Sheet
Error | Root Cause | Quick Fix |
sign_and_send_pubkey: agent refused operation | Sub-key on hardware but touch not confirmed | Touch the token; ensure LED flashes. |
gpg: decryption failed: No secret key | Wrong smartcard inserted | gpg –card-status to list card serials; swap token. |
Bad ownership or modes for directory /home/user/.gnupg | Agent refuses world-readable dirs | chmod 700 ~/.gnupg && chmod 600 ~/.gnupg/* |
gpg-agent[1234]: starting in ssh mode failed: Address already in use | Competing ssh-agent still running | pkill ssh-agent && eval “$(gpg-agent –daemon –enable-ssh-support)” |
Keep this table near your coffee mug; 90 % of support questions boil down to these four lines.
Security Checklist (Copy-Pastable)
[ ] Master OpenPGP key offline, encrypted, with paper backup
[ ] Auth sub-key on hardware token; no private bits on disk
[ ] PIN changed from factory default; touch-to-sign enforced
[ ] gpg-agent socket exported; ssh-agent disabled
[ ] default-cache-ttl ≤ 900 seconds
[ ] Sub-key rotation script scheduled quarterly
[ ] Revocation certificate printed + stored off-site
[ ] CI/CD runners mount token in read-only USB mode
[ ] Production servers accept ONLY ed25519-sk or rsa-sha2-512 keys
Stick this in your team wiki and revisit it every sprint retro.
Putting It All Together
By now you’ve:
- Generated—or upgraded—a GPG key with an authentication sub-key.
- Convinced gpg-agent to moonlight as an SSH agent.
- Logged into a server with a single hardware tap.
- Automated rotation, Git pushes, and double-wrapped file transfers.
That’s defense in depth sealed inside a five-gram USB stick. No extra daemons, no proprietary software, no vendor lock-in—just battle-hardened open standards.
The payoff? Peace of mind. Lose a laptop? Disable one sub-key. Ship code from a coffee shop? A shoulder-surfer can’t replicate your fingerprint. Hungry auditors? Hand them a CSV of YubiKey serials and sub-key fingerprints—job done.