GPG Signing, Encryption, and SSH Auth Subkeys with a YubiKey on macOS
Table of Contents
Section titled “Table of Contents”- Install on macOS
- Prepare the YubiKey
- Configure pinentry
- Move the signing subkey
- Move the encryption subkey
- Use the auth subkey for SSH
- Configure Git
- Test the setup
- Useful macOS commands
- Reference
GPG Signing, Encryption, and SSH Auth Subkeys with a YubiKey on macOS
Section titled “GPG Signing, Encryption, and SSH Auth Subkeys with a YubiKey on macOS”These are the macOS-specific notes I actually use for keeping signing and encryption subkeys on a YubiKey.
The core idea is simple:
- keep the master key offline
- store the signing subkey on the YubiKey for Git commits
- store the encryption subkey on the YubiKey if you want hardware-backed decryption
- store the authentication subkey on the YubiKey if you want SSH to use the token
- use
pinentry-macso GPG can prompt correctly on macOS - point Git at the signing subkey
Install on macOS
Section titled “Install on macOS”Install the tools with Homebrew:
brew install gnupg pinentry-macIf you manage the YubiKey applet with the vendor tools, install those too:
brew install yubikey-managerPrepare the YubiKey
Section titled “Prepare the YubiKey”Before transferring keys, I like to set the retry counters and touch policy:
ykman openpgp access set-retries 30 30 30ykman openpgp keys set-touch aut onThe first command gives you more room while you are typing PINs during setup. The second makes the auth key require touch, which is a good default if you use the YubiKey for SSH.
Configure pinentry
Section titled “Configure pinentry”On macOS, pinentry-mac is the part that matters most. GPG needs to know where to find it.
Find the Homebrew prefix:
brew --prefix pinentry-macThen add this to ~/.gnupg/gpg-agent.conf:
pinentry-program /opt/homebrew/bin/pinentry-macUse /usr/local/bin/pinentry-mac instead if that is your Homebrew prefix.
After changing the config, restart the agent:
gpgconf --kill gpg-agentIn your shell profile, set the TTY so GPG can attach prompts correctly:
export GPG_TTY="$(tty)"Move the signing subkey
Section titled “Move the signing subkey”If the key already exists, open it and move the signing subkey onto the YubiKey:
gpg --edit-key YOUR_KEY_IDInside the interactive prompt:
key 1keytocardsaveIf you have more than one subkey, make sure key 1 selects the signing subkey before keytocard.
Confirm that macOS can see the token:
gpg --card-statusThe OpenPGP applet exposes separate slots for signing and encryption, so keep the roles straight when GPG asks where to store each subkey.
Move the encryption subkey
Section titled “Move the encryption subkey”If you also want the YubiKey to hold the encryption subkey, repeat the same flow and choose the encryption slot when GPG asks:
gpg --edit-key YOUR_KEY_IDInside the interactive prompt:
key 2keytocardsaveUse the encryption subkey here, not the signing subkey. After this step, GPG can still decrypt data encrypted to your public key, but the private decryption material lives on the YubiKey instead of on disk.
Use the auth subkey for SSH
Section titled “Use the auth subkey for SSH”If you want SSH to use the YubiKey, move the authentication subkey onto the auth slot:
gpg --edit-key YOUR_KEY_IDInside the interactive prompt:
key <auth-subkey-number>keytocardsaveUse the authentication subkey here. If you created signing, encryption, then auth in order, it is often key 3.
The YubiKey OpenPGP applet uses a separate auth slot for SSH-style authentication.
Then point SSH at gpg-agent’s SSH socket. Add this to your shell profile:
export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)"If your GnuPG setup needs it, add this line to ~/.gnupg/gpg-agent.conf as well:
enable-ssh-supportOn newer GnuPG builds, the SSH socket wiring is often enough on its own. Restart the agent after changing either file:
gpgconf --kill gpg-agentExport the SSH-format public key from the auth subkey:
gpg --export-ssh-key YOUR_KEY_IDUse that output in ~/.ssh/authorized_keys on the remote host or in your Git hosting provider’s SSH key settings.
To verify the agent sees the key:
ssh-add -LConfigure Git
Section titled “Configure Git”Tell Git to use the signing subkey and sign commits by default.
git config --global gpg.program gpggit config --global commit.gpgsign truegit config --global user.signingKey YOUR_SIGNING_SUBKEY_IDUse the signing subkey ID, not the master key ID.
Test the setup
Section titled “Test the setup”Create a throwaway commit and inspect the signature:
git commit --allow-empty -m "Test GPG signing"git log --show-signature -1You should see a valid signature and, on first use, a PIN prompt from pinentry-mac.
Useful macOS commands
Section titled “Useful macOS commands”These are the commands I keep around when something is stuck:
brew --prefix pinentry-macgpg --list-secret-keys --keyid-format=longgpg --card-statusgpgconf --kill gpg-agentgit log --show-signature -1If signing stops working on macOS, the usual fixes are:
- confirm the YubiKey is inserted
- confirm
pinentry-programpoints at the Homebrewpinentry-mac - confirm
user.signingKeyis the signing subkey - restart
gpg-agent
Reference
Section titled “Reference”For the broader GnuPG and YubiKey workflow, the best reference I have found is:
The guide covers the full lifecycle, including key creation, transfer, SSH integration, and recovery. I keep this post focused on the macOS-specific parts and the commands I reach for most often.