[Deprecated] GPG and SSH with Yubikey for Mac
Important! This blog post is now out of date and shouldn’t be relied upon. I’m leaving it in place for history’s sake.
The Yubikey Neo [affiliate] is a great, inexpensive security device that supports Universal 2nd Factor authentication to web services and OpenPGP smart card support.
Yubikey Neo
The goal of this post is to describe the setup steps for:
- GPG Mail encryption and signing
- SSH public-key authentication, e.g. for connecting to servers, Git source control, and Heroku.
The latter establishes a second factor for controlling access that cannot be compromised simply by theft of an SSH private key and/or use of a keylogger. With the private key for GPG and SSH held on the Yubikey, it is much more secure than if it were held on the local hard disk.
This post is written to help set this up for Macs running Yosemite or El Capitan, using Fish shell. It is assumed that Homebrew and brew cask are installed.
This post is a combination/distillation of a handful of HOWTO guides I found useful for getting this set up. This was compiled a little after I actually performed the process, so if there are any errors/omissions please let me know.
Preparing the Yubikey #
Install Yubikey management tools:
$ brew update
$ brew cask update
$ brew cask install yubikey-neo-manager yubikey-personalization-gui
$ brew install yubikey-personalization
Insert the Yubikey into your USB port.
Set the Yubikey’s mode to allow concurrent OpenPGP SmartCard and OTP usage:
$ ykpersonalize -m82
Install GPG Tools for Mac:
$ brew cask install gpgtools
Set up PINs for GPG on the Yubikey. If at this stage you receive a ‘card error’, try removing and reinserting the Yubikey.
Note: if you enter the factory default PIN incorrectly too many times the Yubikey will become blocked. It seems that it may be possible to reset this, but I have not tested this.
First, set an admin PIN (factory default is 12345678). Next, set your user PIN (factory default is 123456).
$ gpg --card-edit
... snip ...
gpg/card> admin
Admin commands are allowed
gpg/card> passwd
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection? 3
PIN changed.
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection? 1
PIN changed.
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection? q
Creating a public/private key pair #
Next, create a key on the device. We’ll choose not to make an off-card backup of the key, and to have the key expire after 1 year:
$ gpg --card-edit
gpg/card> admin
gpg/card> generate
Make off-card backup of encryption key? (Y/n) n
gpg: 3 Admin PIN attempts remaining before card is permanently locked
Admin PIN
PIN
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 1y
...
Is this correct? (y/N) y
You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
"Heinrich Heine (Der Dichter) <[email protected]>"
Real name: ...
Email address: ...
Comment: tester
You selected this USER-ID:
...
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
gpg: generating new key
gpg: please wait while key is being generated ...
gpg: key generation completed (45 seconds)
gpg: signatures created so far: 0
gpg: signatures created so far: 0
You need a Passphrase to protect your secret key.
+++++
..+++++
gpg: signatures created so far: 2
gpg: signatures created so far: 2
gpg: generating new key
gpg: please wait while key is being generated ...
gpg: key generation completed (25 seconds)
gpg: signatures created so far: 4
gpg: signatures created so far: 4
gpg: key 79C56617 marked as ultimately trusted
public and secret key created and signed.
gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, classic trust model
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
pub 2048R/79C56617 2015-08-25
Key fingerprint = ...
uid ...
sub 2048R/... ...
sub 2048R/... ...
I’ve removed specific elements from the above, but note the public key ID you receive (in my case, this was 79C56617).
Next, export the GPG public key somewhere safe for later use:
$ gpg --armor --export <your public key ID> > ~/my_gpg_public_key.pub
Open up the ‘GPG Keychain’ application and right click on your keypair (it should show with sec/pub, in bold).
Firstly, generate a Revoke Certificate and store it in a safe place. You will need to use this to revoke your key if you lose your Yubikey.
Next, do ‘Send public key to Keyserver’.
Configuring GPG Agent #
This step allows GPG Agent to authenticate your SSH sessions. Edit ~/.gnupg/gpg-agent.conf
:
pinentry-program /usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac
enable-ssh-support
write-env-file
use-standard-socket
default-cache-ttl 600
max-cache-ttl 7200
debug-level advanced
log-file /var/log/gpg-agent.log
Note that:
- the
pinentry-program
setting above stipulates that a Mac native GUI should be used to prompt for your GPG User PIN enable-ssh-support
… does what it saysdefault-cache-ttl
means that your PIN will be cached for 10 minutes- By default the log file will not be writable and no logs will be produced. Touch the file
/var/log/gpg-agent.log
andchown
it to your username if you want GPG Agent to log information out.
Next, as I’m using fish shell, I had to add this to my shell configuration to start GPG Agent with my shell. Put the following in ~/.config/fish/gnupg.fish
:
# Start or re-use a gpg-agent.
#
gpgconf --launch gpg-agent
# Ensure that GPG Agent is used as the SSH agent
set -e SSH_AUTH_SOCK
set -U -x SSH_AUTH_SOCK ~/.gnupg/S.gpg-agent.ssh
(Added 2016-04-17): With the release of GPG Agent Autostart in v2.1 of GPG, this section has become vastly easier! Changes are reflected above.
Now, restart your shell to make GPG Agent reload.
Using GPG for SSH login #
With GPG Agent running, not much more needs to be done to use the Yubikey for SSH authentication. We still need to obtain your public key in a format that SSH can understand:
$ gpgkey2ssh <your public key ID> > ~/my_ssh_public_key.pub
This yields a regular SSH format public key. Next, edit the comment section (final part of the file).
You can now transfer this SSH public key in the usual manner to:
authorized_keys
files on servers you need access to- Github
- Heroku
- … any other services you use that require your SSH public key
If you’ve successfully set up your Yubikey, GPG Agent and provided your new public key to Github, the following should tell you your Github username (mine is rnorth, as seen here):
$ ssh [email protected]
PTY allocation request failed on channel 0
Hi rnorth! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.
Testing GPG for encryption and signing #
To check that your base GPG setup works, try the following:
$ gpg --sign -a
Type Hello World!
followed by newline and Ctrl-D. The output should look vaguely similar to:
-----BEGIN PGP MESSAGE-----
Comment: GPGTools - https://gpgtools.org
owEBQwG8/pANAwAKAaUoZrl5xWYXAcsTYgBV/qIXSGVsbG8gV29ybGQhCokBHAQA
AQoABgUCVf6iHAAKCRClKGa5ecVmF+SGB/9oq34MZRkwuUfLD1uyIsyulLfzunVG
LJkOaeOTiwO0aeQCcPOe/uSqt5yi5o3a+8L/pBjD7+lwaTYNT4+3Tf4UpbuoDq7L
bcgK3ZIbWfqW56OjqdubBMp+hP1Jwgolfc5lZw62LIkUZ/PLf7dJfevSX6PTcxlV
UUkIG0xmq/ZIc5ZbyFcCqZ8RaR35LPcTJ3nvsCzF50+HTK15W2+nxspgsorl+uSz
JwQHPilsvDyC3PuaMu6+6VvI+xpjor/XcODO/0w6x/vrlXGNluM9/Vc6rgm8vn5E
dr34eKaAz48dS/21qK41Oxp2Xx6vf9/culDJzihWlfAtF5Qf2u+6aDKl
=1GQg
-----END PGP MESSAGE-----
References #
The following excellent guides were used in deriving these steps:
- Yubikey Neo Composite device
- Easy multifactor authentication for SSH using YubiKey NEO tokens
- http://25thandclement.com/~william/YubiKey_NEO.html
- Secure yourself, Part 1: Air-gapped computer, GPG and smartcards
- https://www.gnupg.org/howtos/card-howto/en/ch03s03.html
- [HOW-TO] - Yubikey NEO, OpenPGP, OpenSSH authentication