Using Yubikeys for SSH

In Yubikey PIV for SSH on Macs I described the full process for setting up and using Yubikeys for SSH. This is an abbreviated version that only describes how to use the Yubikey; the assumption is that some admin has already configured your Yubikey.

OS Compatibility

If you're running OS X 10.11 (El Capitan) or macOS 10.12 (Sierra) everything below should work. If you're running an older version of OS X you should upgrade, or follow Yubikey's instructions to ensure that the Yubikey is recognized.

Setup

Install the Yubikey personalization tool (ykpers), the Yubikey PIV tool (yubico-piv-tool), and the OpenSC tools (opensc):

% brew install ykpers yubico-piv-tool opensc

You can also install Yubico's GUI PIV Manager.

Check that your Yubikey can be used as a PIV card:

% opensc-tool --list-readers
# Detected readers (pcsc)
Nr.  Card  Features  Name
0    Yes             Yubico Yubikey 4 OTP+U2F+CCID

The Name field should include the string CCID; if it does not, and you have a Yubikey NEO, this might work (untested):

ykpersonalize -m1

Export the public key in SSH format

ssh-keygen can export the SSH public key from the Yubikey:

% ssh-keygen -D ${HOMEBREW_ROOT}/lib/opensc-pkcs11.so -e
ssh-rsa AAAAB...

Add this SSH key to your ~/.ssh/authorized_keys file on systems you want to authenticate to with the Yubikey.

Changing your PIN

When you receive your Yubikey from your admin, it will have a PIN already set. You should change this as soon as possible:

% yubico-piv-tool -a change-pin
Enter pin:
Enter new pin:
Verifying - Enter new pin:
Successfully changed the pin code.

Using the SSH key

With SSH Agent, and careful use of ~/.ssh/config settings, a Yubikey can be setup to only requiring the PIN once after the Yubikey is inserted, then only touch is required for each connection.

First, add the PKCS11 library to SSH Agent. You'll be prompted to enter a passphrase, which is really your PIN:

% ssh-add -s ${HOMEBREW_ROOT}/lib/opensc-pkcs11.so
Enter passphrase for PKCS#11:
Card added: /usr/local/homebrew/lib/opensc-pkcs11.so
% ssh-add -l
2048 SHA256:... /usr/local/homebrew/lib/opensc-pkcs11.so (RSA)

Now just SSH to any account that is configured to allow the Yubikey SSH key:

% ssh -v remotehost
OpenSSH_6.9p1, LibreSSL 2.1.8
debug1: Reading configuration data /Users/alice/.ssh/config
debug1: /Users/alice/.ssh/config line 87: Applying options for remotehost
debug1: /Users/alice/.ssh/config line 228: Applying options for *
debug1: Reading configuration data /etc/ssh/ssh_config
...
debug1: Next authentication method: publickey
debug1: Offering RSA public key: /usr/local/homebrew/lib/opensc-pkcs11.so
debug1: Server accepts key: pkalg ssh-rsa blen 279

Touch the Yubikey

debug1: Authentication succeeded (publickey).
...
Last login: ...

The only complication with this method is that if you remove the Yubikey then re-insert it, you'll need to manually unload then load the PKCS11 library (and type in the PIN again), otherwise it will silently fail to use the Yubikey and proceed to try other keys, then any other mechanisms supported by the server.

To unload and re-load the library:

% ssh-add -e ${HOMEBREW_ROOT}/lib/opensc-pkcs11.so
Card removed: /usr/local/homebrew/lib/opensc-pkcs11.so
% ssh-add -s ${HOMEBREW_ROOT}/lib/opensc-pkcs11.so
Enter passphrase for PKCS#11:
Card added: /usr/local/homebrew/lib/opensc-pkcs11.so

Alternatively, install this script as yubikey somewhere on your path (/usr/local/bin, or ~/bin), ensure it is executable, then call it whenever you insert or remove your Yubikey:

#! /bin/bash

cmd=$1

yubikey=0
pkcslib=0

HOMEBREW_ROOT=${HOMEBREW_ROOT:-/usr/local}

# Check if Yubikey is present
system_profiler SPUSBDataType | grep -q 'Manufacturer: Yubico' && yubikey=1

# Check if PKCS11 is loaded
ssh-add -l | grep -q opensc-pkcs11.so && pkcslib=1

if [ $yubikey -eq 1 ] && [ $pkcslib -eq 1 ]; then
  ## have yubikey and pkcslib => reload
  cmd=${cmd:-reload}
elif [ $yubikey -eq 1 ] && [ $pkcslib -eq 0 ]; then
  ## have yubikey, but not pkcslib => load
  cmd=${cmd:-load}
elif [ $yubikey -eq 0 ] && [ $pkcslib -eq 1 ]; then
  ## no yubikey, but pkcslib loaded => unload
  cmd=${cmd:-unload}
else
  ## no yubikey, no pkcslib => nothing to do
  cmd=nop
  echo No Yubikey, No PKCS library
fi

if [ $cmd = 'reload' ] || [ $cmd = 'unload' ]; then
  ssh-add -e $(ssh-add -l | awk '/opensc-pkcs11/ {print $3}')
fi

if [ $cmd = 'reload' ] || [ $cmd = 'load' ]; then
  ssh-add -s ${HOMEBREW_ROOT}/lib/opensc-pkcs11.so
fi