Securing My Digital Life: GPG, Yubikey, & SSH on macOS

I wrote previously about trying to secure my digital life. I’ve spent some time covering researching ideas and talking to others. It was clear to me that moving to PGP via GnuPG was the first step. I could use PGP together with my YubiKey (Neo model) to secure large parts of my digital life. This post details how I achieved my desired end state of:

  • Securely generated and stored PGP key
  • Private keys stored on my YubiKey
  • SSH access via gpg when my YubiKey plugged in
  • git commit signing via YubiKey
  • Have all this working on the latest macOS

This was a stumbling journey for me, but eventually I made it. Hopefully this post helps you with your own security practices.

Step 1: Install Software

I bumbled around in this area with some bugs because I installed gpg 2.0. 2.1 is the newer “modern” version. The setup may work on gpg 2.0, but it’s untested. I assume you have brew installed on your machine. You’ll need gpg and YubiKey tools. The YubiKey tools can be installed as a GUI or a CLI.

brew tap homebrew/versions
brew install gnupg21 pinentry-mac

Now, for the YubiKey specific bits. Some guides mention the CLI or use the GUI. The tools configure the YubiKey’s modes. The YubiKey must be in CCID mode. The YubiKey supports multiple modes, so it should not impact your current YubiKey’s functionality. I’m calling this out because some guides I followed did not cover this part.

brew install ykneomgr # If you want the ykpersonalize CLI
brew cask install yubikey-neo-manager # If you want the GUI

This should be enough to get you going.

Step 2: Configure Your YubiKey

This step was thrown into the middle or end of the guides I read. I think it’s better to cover this now because this guide is all about configuring gpg and YubiKey from the get go.

Set the YubiKey mode to OTP/U2F/CCID.

Other guides use theykpersonalize command. This one supports older Yuibkeys. ykneomgr is for new models. ykpersonalize examples do the same thing.

ykneomgr --set-mode=6

Or, if you prefer to use the GUI:

YubiKey NEO Manager GUI

This example shows the desired final state. You may only see “OTP+UTF” in your window. Change that by pressing the “Change Connection mode” button. Check all the boxes and follow the on-screen instructions. You’ll be prompted to unplug and plug back in the YubiKey. You should see the list of available apps on the left when CCID is activated.

Change the YubiKey Pins

The YubiKey uses two PINs. The Admin PIN secures operations to the card. The normal bin secures reading from the card. The default Admin PIN is 12345678 and the default PIN is 123456. I strongly recommend changing these before continuing. See the instructions in this excellent guide. Keep these points in mind while reading the guide:

  1. The guide is written for linux. Replace gpg with gpg2 in the example.
  2. PIN must be at least 6 characters long. You can change it a shorter value but it will break things later on.
  3. You may need to “Unblock PIN” before changing it. Use default PIN 123456 if you don’t know it.

Memorize the admin and general usage PIN. You’ll be prompted for the general usage PIN when you need your keys.

Check your YubiKey’s Features

This was another stumbling block for me. I have an older YubiKey which only supports 2048 bit keys. This broke the process later on because my 4096 bit PGP keys could not be written to the YubiKey. You can find this out by checking the card status and reading the key features.

$ gpg2 --card-status | grep -Fi 'key attributes'
Key attributes ...: rsa2048 rsa2048 rsa2048

This example shows there is room for 3 2048 bit RSA keys. Newer YubiKey’s support 4096 bit keys. You’re ready to generate your PGP keys armed with this information.

Step 3: Generate PGP Keys & Add to YubiKey

Plenty of tutorial describe this. I will not duplicate that effort. Please follow this excellent guide. Keep these things in mind while following the guide:

  1. The guide is written for linux. Replace gpg with gpg2 in the example.
  2. Remember your Yubikey’s supported key sizes. I generated a 4096bit master key with 2048 bit subkeys. Fill in the correct values when prompted.
  3. The bits about the gpg conf file are not entirely relevant for this example. Make sure you generate the master key, sub keys, and move the subkeys to the device.
  4. The guide covers exporting, backing up keys, and not keeping the master private key on your machine. This is important! The guide assumes you’re using Linux, so it recommends LUKS. This does not work for OSX. I used an alternate strategy. I formatted an existing USB pen drive as FAT32. I used VeraCrypt (an updated, bug fixed, and maintained version of [defunkt] TrueCrypt) to create a new 128MB encrypted volume on the pen drive. I mounted this at ~/crypt and did all exports to that directory. This approach keeps my backups encrypted and accessible on all platforms (given all platforms support FAT32 & VeraCrypt supports all platforms).

Step 4: Configure GPG Agent

You should have generated keys, backed them up, copied sub keys to the YubiKey, and imported back the relevant bits to your laptop. You’re just about ready to start using this stuff!

This is where I hit another stumbling block. I followed the guide’s encryption and decryption examples. There was a problem with using gpg2 --armor --decrypt from STDIN. Some program was writing to STDOUT which prepopulated the pinentry screen. I could just delete the refilled text, but it made me suspicious about other users. I also tried to upload my new GPG key via keybase. That did not work at all with the terminal pinentry-curses. Then I came across pinentry-mac. This is a GUI program which seems to work all the time unlike the terminal versions.

GnuPG 2.1 introduces another change that does not fit with older guides. GnuPG 2.1 requires the GPG agent and automatically starts it for you. There is no need to start your own agent process in your shell configuration files. You can ignore everything you find on the internet related to startig an agent and all that. However, you do need export the SSH_AUTH_SOCK environment variable for use with an SSH agent. You will need change the pin entry program in the agent configuration file.

Update the GPG Agent Configuration File

Add the following lines to ~/.gnugp/gpg-agent.conf

pinentry-program /usr/local/bin/pinentry-mac

Update your Shell Environment

Add the following to your shell configuration. I use fish. Here’s an example:

set -x GPG_TTY (tty)
set -x SSH_AUTH_SOCK ~/.gnupg/S.gpg-agent.ssh

For pretty much all shells:

export "GPG_TTY=$(tty)"
export "SSH_AUTH_SOCK=${HOME}/.gnupg/S.gpg-agent.ssh"

Now restart any existing gpg agents

gpg-connect-agent killagent /bye
gig-connect-agent /bye

Step 5: Take it for a Spin

It’s finally time to take the whole setup out for a spin. You should have keys in your gpg agent via the YubiKey and in your SSH agent via the gpg agent. Testing SSH access is straight forward. We’ll capture SSH public key on the YubiKey and add it to GitHub. You can use any remote service for this bit. I opted for GitHub because I don’t have any running servers for SSH testing.

First, capture the SSH public key on the YubiKey.

ssh-add -L | grep -iF 'cardno' | pbcopy

Now the public key is on your clipboard. Go ahead and add it to GitHub (or wherever else) you need.

You should also remove an existing SSH from the agent so tests uses the authorization key on the YubiKey.

ssh -T

Enter your PIN when prompted in the GUI. You should authenticate successfully to GitHub:

Hi ahawkins! You've successfully authenticated, but GitHub does not provide shell access.

This ensures the full toolchain is working as expected. Now you can move onto whatever other fun things you can do with PGP.

Next Steps

This guide gives you enough to get going. I recommend you investigate the following:

  • Configuring gpg TTLs; this essentially sets how long the agent should wait before prompting for PIN/Passphrase.
  • Set your default PGP signing key in your gpg.conf file
  • Git Commit Signing
  • Adding your GPG keys to HINT: keybase pgp select
  • Get my PGP key from Keybase and send me an encrypted message
  • Upload your PGP key to a key server

Note that you may encountered random failed signing with git commits. I’m not sure why this is happening. Simply unplugging and plugging the YubiKey back in seems to resolve the issue. See the reference 4 for more information.

I hope this guide helps you improve your digital security. Just remember to keep that YubiKey safe.