I have been using a Yubico Yubikey NEO security token to store my GnuPG private keys for about a year now. The Yubikey is a powerful security token and I wanted to make use of it to manage my SSH keys as well. I also recently transitioned to the newer GnuPG ‘Modern’ 2.1.x release and there are significant changes to how you would go about using your GPG keys for SSH. Since I ran into a few issues, and the existing documentation on the web is somewhat lacking, I thought I would write up some notes documenting how I did it.
UPDATE : November 19, 2016 : When this post was first written there were issues with using GnuPG 2.1.x with versions of macOS prior to 10.12 (Sierra). As a result of upgrades in both macOS and GnuPG these issues seem to have largely dissapeared for me. I have updated this post to reflect current reality.
# NOTE : Installation of 'pinentry-mac' requires a full Xcode installation. # The command line tools are not sufficient. brew tap homebrew/versions brew install gnupg21 brew install pinentry-mac
Installing GPG keys on Yubikey
Here is a useful site for learning how to add your SSH keys to your Yubikey.
Using GnuPG for SSH Authentication
The following websites offered some useful info when learning about using the Yubikey for SSH.
- https://gnupg.org/faq/whats-new-in-2.1.html (see ‘Auto-start of the gpg-agent’)
Setup GnuPG Agent for SSH
gpg-agent to start with SSH support in
~/.gnupg/gpg-agent.conf by adding the
enable-ssh-support entry. Here’s
my config (which also makes use of the pinentry-mac package we installed earlier).
default-cache-ttl 900 max-cache-ttl 999999 pinentry-program /usr/local/bin/pinentry-mac enable-ssh-support
Next, configure all of your shell environment to have an appropriate
SSH_AUTH_SOCK environment variable. Here are the relevant lines
~/.zshrc. You’ll want to source your config file or close
and open a new terminal window after this change.
... # GPG 2.1.x SSH support # See : http://incenp.org/notes/2015/gnupg-for-ssh-authentication.html export SSH_AUTH_SOCK=$HOME/.gnupg/S.gpg-agent.ssh ...
Ensure your Yubikey is removed and restart your
then re-insert your Yubikey. Alternatively, run
You may want to also confirm that you no longer have any
ssh-agent is no longer used at all with this setup.
* remove key * /usr/local/bin/gpgconf --kill gpg-agent && /usr/local/bin/gpgconf --launch gpg-agent * insert key *
gpg2 --card-status should display a summary of your Yubikey config
including the keys you have installed on it.
Now you are almost ready to go. Your GPG keys are on your Yubikey, the
is running and ready to support your SSH client, and all that you need to do is
reveal your SSH public key so you can add it to the
authorized_keys file on
your remote server you want to access with SSH.
$ ssh-add -L ssh-rsa AAAAB3Nza+MY_LONG_SSH_PUB_KEY cardno:000600000000
Copy the line starting with
ssh-rsa and paste
that into the
~/.ssh/authorized_keys file on the remote machine you want to SSH to.
This should paste as a single long line with no line breaks.
Here is what the docs say
about the behavior of
gpg-agent in the context of SSH. This is important to understand.
gpg-agent must already be running for SSH using GnuPG + Yubikey to work.
Auto-start of the gpg-agent : If the option –enable-ssh-support is used the auto-start mechanism does not work because ssh does not know about this mechanism. Instead it is required that the environment variable SSH_AUTH_SOCK is set to the S.gpg-agent.ssh socket in the GnuPG home directory. Further gpg-agent must be started: Either by using a GnuPG command which implicitly starts gpg-agent or by using gpgconf –launch gpg-agent to explicitly start it if not yet done.
Ensure gpg-agent is running
gpg-agent must be running before you can use your Yubikey for SSH you can
get it running with a
gpg2 --card-status command. This should auto start
You can confirm its running with
ps aux | grep gpg-agent | grep -v grep
OK, you should be ready to
ssh email@example.com now! Make sure your Yubikey
is inserted and ssh to that host. If all is working right, you should be prompted
for the user PIN for your Yubikey to unlock it, and once you enter that PIN you
should succeed in your ssh login.
Now all thats to do is to migrate your old collection of old insecure SSH public keys over to your new single GPG/SSH key to rule them all. Enjoy!
FIXME : Does this fix to disable OS X scdaemon still apply to my system? Is it affecting what is written here? Does it still apply?
FIXME : Remove or update problem fix section?
UPDATE : January 15, 2016 : I am working through this issue in the gnupg-users mailing list. You can follow along with the discussion thread here. I’ll update this post again if there is a resolution to this issue.
I ran into one major problem with this setup. It seems that if the
is running and you remove your Yubikey and re-insert it, the
no longer recognize that your Yubikey is present and SSH logins won’t work.
Normally this isn’t a problem when just using the Yubikey for GPG keys since
GnuPG will take care of launching the agent as needed. But in the SSH scenario
you want the
gpg-agent to be ready to go for SSH whenever your Yubikey is
The only way I could discover to fix this was to kill and restart
each Yubikey insertion.
gpgconf --kill gpg-agent && gpgconf --launch gpg-agent
Of course this is a PITA. There has to be a better way.
Using controlPlane to restart GnuPG agent after Yubikey removal and insertion
What I wanted, as a workaround to the problem, was to re-start the agent after the
Yubikey is removed, and then once again whenever it is re-inserted. I think this may
be possible using
launchd on macOS, but I settled on using ControlPlane which is
an macOS application that can apply system config, or run arbitrary shell scripts,
in reaction to designated system events. In our case it can be configured to run
a shell script that will re-start
gpg-agent every time a Yubikey is inserted
I added the following shell script (make sure it is
chmod 700) to
#!/bin/bash /usr/local/bin/gpgconf --kill gpg-agent && /usr/local/bin/gpgconf --launch gpg-agent
You can test that this technique will work for you by running that script manually after a removal and re-insertion of the Yubikey. If it does, then you can install ControlPlane to automate it.
Setup a ‘Yubikey’ context. This may not be strictly necessary.
Make sure the ‘Attached USB Device’ checkbox is checked on the Evidence Sources tab.
On the Rules tab click the + to add a rule which is based on the presence of the Yubikey. Make sure the Yubikey is plugged in when you select this tab so its available in the drop-down.
On the Actions tab click the + to add a new Action. Provide the full path to your shell script and be sure to set the context to the name of the context you chose in the first step, no delay, and choose to execute on both arrival and departure of the Yubikey.
And that should do it! When you insert or remove the Yubikey you should see
a notification (and a log entry in the advanced tab of ControlPlane) showing
that the script is getting run. Your
gpg-agent should be getting a new PID
every time you insert the Yubikey.
Know a better way?
Please do let me know and I’ll be sure to try out your ideas and document them here if they improve things.