Skip to content

Instantly share code, notes, and snippets.

@voretaq7
Last active November 19, 2020 07:40
Show Gist options
  • Save voretaq7/9e9a213f8936449d7f8e37cffa145bc2 to your computer and use it in GitHub Desktop.
Save voretaq7/9e9a213f8936449d7f8e37cffa145bc2 to your computer and use it in GitHub Desktop.
Yubikey Smartcard Authentication (OS X, Linux, Windows)

Minimally Invasive SSH Smartcard (PIV/PKCS11) Support.

This is a very short guide to getting smartcards working for SSH key authentication. It is focused on minimal intervention - that is to say I tried to work with the tools the operating system provides rather than ask you to install extra software.

Since I use a YubiKey for both home and work that's the example device (YubiKey 5 Series or FIPS Series both work the same for this purpose), but any PKCS11 card that supports an Authentication key will work, the only difference is getting the certificate/public key out so you can convert it.

My assumption is you are performing most of this work on either a Linux system, a BSD system, or a Mac - Basically somewhere you have access to openssh, openssl, and a YubiKey Manager program.

Setting up the SmartCard (Your YubiKey)

Use the YubiKey Manager to generate a certificate in Slot 9a (Authentication)

If you're working in an environment that has PKI do this right and generate a CSR, have that signed with all the right extensions and key usage constraints, and import the certificate back to your YubiKey.

If you're just using this on your own as a better/more securely portable solution than keys in files on a USB thumb drive then the certificate & expiration date don't matter. Just let the YubiKey tool generate a self-signed certificate.

NOTE: YOU MUST USE SLOT 9a

Previously other slots would work, but either the PKCS11 drivers (providers) have gotten pickier about this or OpenSSH is being more specific about what kind of key it wants.

The Server (authorized_keys)

Export the certificate from the YubiKey using the YubiKey Manager. The GUI version has a handy Export button, if you're using a command line version it's something like ykman piv export 9a YubiKey-9a.crt

The export gives you a PEM-format public certificate. Useful, but not here. To make it into something SSH can understand use OpenSSL to extract the public key:
openssl x509 -pubkey -in YubiKey-9a.crt -noout > YubiKey-9a.pub

That gives you just the public key, but it's in PKCS8 format so OpenSSH still won't like it. Take that .pub file and run it through ssh-keygen:
ssh-keygen -f YubiKey-9a.pub -i -mPKCS8

The output of that command is suitable for adding to your authorized_keys file. Tack a comment on the end so you remember what YubiKey serial number it's on or some other useful identifier so you can remove it if you lose the key (or know which line to change if you accidentally nuke the private key on your token).

The Client (PKCS11 Levels of Hell)

To use your YubiKey the client device needs to know how to talk to it and get keys. That means you need a PKCS11 provider (driver, library, whatever) that OpenSSH can talk to to request keys.

Mac OS X:

OS X 10.13.2 (High Sierra) & later makes the client stuff pretty seamless. Just add
PKCS11Provider /usr/lib/ssh-keychain.dylib to your ~/.ssh/config file and OpenSSH will try to get keys from whatever smartcard or PKCS11 device is currently plugged in.

This will prompt you for your SmartCard PIN when you SSH somewhere, and then you just tap your blinking YubiKey.

If you want to save yourself the password you can use ssh-agent. Just plug in your smartcard and run the following command:
ssh-add -s /usr/lib/ssh-keychain.dylib

When it adds the keys the agent keeps track of which keys are on cards & retains the PIN, so all you need to do is tap the blinking YubiKey. You only need to run ssh-add when you reboot, log out, or kill the agent, it should take care of everything else (including you adding or removing your smartcard) internally.

Linux & BSD:

Any recent version of Linux or any one of the BSDs should have an OpenSSH new enough that things work just like they did on the Mac, but you will. probably need to install a PKCS11 provider.

The usual one to use is OpenSC. It's in FreeBSD ports as "security/opensc" and in Debian packages as "opensc" - I'm sure you'll find it for other distributions too but if not compile it yourself from https://github.com/OpenSC/OpenSC/releases

Find where your particular system installs opensc-pkcs11.so (it's usually /usr/lib/opensc-pkcs11.so or /usr/local/lib/opensc-pkcs11.so) and use that file in place of /usr/lib/ssh-keychain.dylib from the Mac instructions.

Windows:

As of Windows 10 you can easily install OpenSSH on Windows. (Settings UI under Apps -> Apps & Features, select "Manage optional features", hit "Add a feature" and look for "OpenSSH Client (Beta)" in the list). Unfortunately as of February 2019 it's still a shit old version that doesn't support PKCS11.

All is not lost though - Microsoft has a better OpenSSH available! Just grab the latest one from GitHub at:
https://github.com/PowerShell/Win32-OpenSSH/releases

Extract the zip file and either add that directory to the front of your PATH variable or make sure you're in the spot where it got extracted when you run ssh. There's an install script in the release zip file but it looks somewhat half-assed. Use at your own discretion until Microsoft updates the version you can get in the Optional Features section.

You will also need a PKCS11 provider because Windows doesn't come with one. I recommend OpenSC (they have MSI packages, though they aren't signed so you get the "Unknown Developer" warning). Grab their latest 64-bit release from GitHub at:
https://github.com/OpenSC/OpenSC/releases

When you install the MSI the OpenSC PKCS11 driver gets installed at C:\Program Files\OpenSC Project\OpenSC\pkcs11\opensc-pkcs11.dll

From here follow the Mac instructions but use that file in place of /usr/lib/ssh-keychain.dylib

One last wart: The SSH agent doesn't seem to work with PKCS11 providers, even in the latest beta version of OpenSSH for Windows (It always gives me an error adding the card).

You can either try to debug this (let me know if you get it working), give up like I did and use the .ssh/config option (typing your PIN every time you use the key), or hunt around for a Windows SSH client that has better PKCS11 support (one option is PuTTY SC).

My assumption is you are using the 64-bit versions of OpenSSH and OpenSC on Windows. That's waht I tested, and that's what I know works. If for some reason you have to install the 32-bit versions they should work fine, but OpenSC will probably install to "Program Files (32 bit)" or wherever your system is configured to put 32-bit applications. Update your paths as needed.

Bonus Instructions

The following instructions are provided for reference, however they are not intended to be comprehensive. For full details refer to the OpenSSH documentation.

Generating public keys without OpenSSL

It's possible to use ssh-keygen to generate the authorized_keys (SSH pubkey) formatted keys directly. To do this on a mac you would run:
ssh-keygen -D /usr/lib/ssh-keychain.dylib
As before substitute yout PKCS11 provider on Linux or Windows (and on Windows you will need the version of OpenSSH that actually supports PKCS11.

This will return several keys (one for every authentication slot your card has apparently). If you have only configured Slot 9a that will be the only key returned. If not it appears that Slot 9a is always the first one returned, but it might vary so be prepared to test.

Multi-Factor SSH Authentication

OpenSSH 6.8p1 and later supports multi-factor authentication. To set this up all you need to do is add an AuthenticationMethods directive to your OpenSSH server configuration (typically /etc/ssh/sshd_config).

Typically you'll want something like:
AuthenticationMethods publickey,password

If you're using a more complex PAM configuration you can use:
AuthenticationMethods publickey,keyboard-interactive

You can even require two (different) public keys by specifying
AuthenticationMethods publickey,publickey

By using this in combination with "Match User" or "Match Group" directives it is possible to configure M-of-N key access for some accounts.

Two things to bear in mind here:

  • The authentication methods are requested int he order listed
    (if you specify password,publickey you will be prompted for a password BEFORE the server checks for a key)
  • Specifying password or keyboard-interactive twice DOES NOT require different authentication tokens
    The SSH server is smart enough to remember what keys you've presented, but your password is always your password & keyboard-interactive just hands the request off to PAM). You can of course require multiple authentication steps in your PAM configuration.

GPG-Smartcard Authentication

There is an excellent writeup on using a YubiKey in GPG Smartcard mode instead of PIV + PKCS11 mode. You can find it here: https://gist.github.com/lizthegrey/9c21673f33186a9cc775464afbdce820

GPG Smartcard mode has the advantage of allowing longer RSA keys and the use of GPG for general-purpose encryption, but requires additional software and configuration as detailed in that gist.

Those instructions also contain examples of how to allow users with hardware keys to use them as a single factor while requiring passwords for users whose keys are in a file on their computer. While this is a security improvement over trusting a single public key in an id_rsa file that can be stolen without being missing I would advise sticking to a design which requires a second factor of some kind - either a second public key (which may be a file) or a password.


List of revisions:

2020-03-18: Posted to GitHub GIST.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment