Skip to content

GPG Signing, Encryption, and SSH Auth Subkeys with a YubiKey on macOS

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-mac so GPG can prompt correctly on macOS
  • point Git at the signing subkey

Install the tools with Homebrew:

Terminal window
brew install gnupg pinentry-mac

If you manage the YubiKey applet with the vendor tools, install those too:

Terminal window
brew install yubikey-manager

Before transferring keys, I like to set the retry counters and touch policy:

Terminal window
ykman openpgp access set-retries 30 30 30
ykman openpgp keys set-touch aut on

The 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.

On macOS, pinentry-mac is the part that matters most. GPG needs to know where to find it.

Find the Homebrew prefix:

Terminal window
brew --prefix pinentry-mac

Then add this to ~/.gnupg/gpg-agent.conf:

Terminal window
pinentry-program /opt/homebrew/bin/pinentry-mac

Use /usr/local/bin/pinentry-mac instead if that is your Homebrew prefix.

After changing the config, restart the agent:

Terminal window
gpgconf --kill gpg-agent

In your shell profile, set the TTY so GPG can attach prompts correctly:

Terminal window
export GPG_TTY="$(tty)"

If the key already exists, open it and move the signing subkey onto the YubiKey:

Terminal window
gpg --edit-key YOUR_KEY_ID

Inside the interactive prompt:

key 1
keytocard
save

If you have more than one subkey, make sure key 1 selects the signing subkey before keytocard.

Confirm that macOS can see the token:

Terminal window
gpg --card-status

The OpenPGP applet exposes separate slots for signing and encryption, so keep the roles straight when GPG asks where to store each subkey.

If you also want the YubiKey to hold the encryption subkey, repeat the same flow and choose the encryption slot when GPG asks:

Terminal window
gpg --edit-key YOUR_KEY_ID

Inside the interactive prompt:

key 2
keytocard
save

Use 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.

If you want SSH to use the YubiKey, move the authentication subkey onto the auth slot:

Terminal window
gpg --edit-key YOUR_KEY_ID

Inside the interactive prompt:

key <auth-subkey-number>
keytocard
save

Use 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:

Terminal window
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:

Terminal window
enable-ssh-support

On newer GnuPG builds, the SSH socket wiring is often enough on its own. Restart the agent after changing either file:

Terminal window
gpgconf --kill gpg-agent

Export the SSH-format public key from the auth subkey:

Terminal window
gpg --export-ssh-key YOUR_KEY_ID

Use 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:

Terminal window
ssh-add -L

Tell Git to use the signing subkey and sign commits by default.

Terminal window
git config --global gpg.program gpg
git config --global commit.gpgsign true
git config --global user.signingKey YOUR_SIGNING_SUBKEY_ID

Use the signing subkey ID, not the master key ID.

Create a throwaway commit and inspect the signature:

Terminal window
git commit --allow-empty -m "Test GPG signing"
git log --show-signature -1

You should see a valid signature and, on first use, a PIN prompt from pinentry-mac.

These are the commands I keep around when something is stuck:

Terminal window
brew --prefix pinentry-mac
gpg --list-secret-keys --keyid-format=long
gpg --card-status
gpgconf --kill gpg-agent
git log --show-signature -1

If signing stops working on macOS, the usual fixes are:

  • confirm the YubiKey is inserted
  • confirm pinentry-program points at the Homebrew pinentry-mac
  • confirm user.signingKey is the signing subkey
  • restart gpg-agent

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.