[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.
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
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
pinentry-programsetting above stipulates that a Mac native GUI should be used to prompt for your GPG User PIN
enable-ssh-support... does what it says
default-cache-ttlmeans 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
chownit 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
# 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_keysfiles on servers you need access to
- ... 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
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-----
The following excellent guides were used in deriving these steps:
- Yubikey Neo Composite device
- Easy multifactor authentication for SSH using YubiKey NEO tokens
- Secure yourself, Part 1: Air-gapped computer, GPG and smartcards
- [HOW-TO] - Yubikey NEO, OpenPGP, OpenSSH authentication