SSH keys on a yubikey

Dec 27, 2015

There is something oddly satisfying about having my private ssh keys only on a hardware device where they cannot be directly accessed.

For the past 6 months I’ve been using a yubikey for SSH access to my servers and github. In this configuration the private key only exists on the yubikey and cannot be transferred to the host computer. All cryptographic operations that require the private key are preformed on the yubikey. Here’s how I set it up.

Install software

Assuming you have homebrew and brew cask installed:

brew cask install yubikey-neo-manager
brew install gnupg2 gpg-agent pinentry-mac 

Configure the yubikey

  • Start yubikey-neo-manager
  • Insert your yubikey
  • Select the device
  • and click Change connection mode to enable CCID

I disabled all other modes since I only cared about using this as an OpenGPG smart card.

Create a key

gpg2 --card-edit
gpg/card> admin
gpg/card> generate

At this point a new keypair is on your yubikey. Your public key will be added to your gpg keychain. Note that you won’t be able to retrieve your public or (private key) from the yubikey. You may want to backup or publish your public key to a key server - but I’ll leave that as an exercise to the reader.

gpg/card> quit

To turn your newly minted GPG public key into an ssh formatted public key:

gpgkey2ssh <key id>

and you will get…


I suggest changing to COMMENT to something more descriptive, like your name or email address then save that off your github or digitalocean.

Add the following to your .bash_profile so that your ssh agent (gpg-agent) will know to also use your yubikey.

gpg-agent &> /dev/null
if [ "$?" -ne "0" ]; then
  gpg-agent --daemon &> /dev/null

if [ -f "${HOME}/.gpg-agent-info" ]; then
  . "${HOME}/.gpg-agent-info"
  export SSH_AUTH_SOCK
  export SSH_AGENT_PID
export GPG_TTY=$(tty)

And setup your ~/.gnupg/gpg-agent.conf to be:

# from brew install pinentry-mac
pinentry-program /usr/local/bin/pinentry-mac

# enables SSH support (ssh-agent)

# writes environment information to ~/.gpg-agent-info

# default cache timeout of 600 seconds
default-cache-ttl 600
max-cache-ttl 7200

Restart your terminal and test it out.

$ ssh-add -l
2048 6e:2f:6d:0d:ad:23:1c:1e:f0:e1:05:4e:f8:de:ad:1f cardno:000603633910 (RSA)
$ ssh -T
Hi tam7t! You've successfully authenticated, but GitHub does not provide shell access.

Fixing issues

Occasionally the gpg-agent daemon seems to lock up causing ssh to timeout and commands like ssh-add -l to hang. I’ve added the following functions to my .bash_profile to fix these 💩.

kill-gpg-agent() {
  killall -9 gpg-agent

start-gpg-agent() {
  eval $(gpg-agent --daemon)

restart-gpg-agent() {
  echo 'remove your key'
  sleep 3

I had to do restart-gpg-agent about 4-5 times a day originally, but in the past month I’ve only had to do it a handful of times.