There aren’t too many great articles out there about how to set up a YubiKey simply for SSH access. I will be showing you how to:
Generate a GPG key
Move the keys over to your YubiKey
Enable SSH on GPG so that your SSH authenticates with GPG
YubiKey implements the smart card interface for GPG, which means you shouldn’t need any external libraries specific to YubiKey. However you still need an up-to-date version of GPG and gpg-agent
to allow SSH. You can check if your version of gpg-agent
is recent by running the command below.
$ gpg-agent --help | grep ssh
--enable-ssh-support enable ssh support
If the --enable-ssh-support
ssh flag exists, you can continue, otherwise, download the latest versions of GPG and gpg-agent
from your package manager of choice.
First, let’s plug in the blank YubiKey and make sure you can edit it with GPG.
$ gpg --card-edit
Application ID ...: D2760001240102010006061566540000
Version ..........: 2.1
Manufacturer .....: unknown
Serial number ....: 06156654
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : [not set]
Login data .......: [not set]
Private DO 1 .....: [not set]
Private DO 2 .....: [not set]
Signature PIN ....: not forced
Key attributes ...: 2048R 2048R 2048R
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]
If your YubiKey already has data on it and you want to reset it, I’ve created this little script you can use to reset it. The hex values to reset the device I got off a GPG forum, but but you can probably find them elsewhere:
#!/bin/bash |
echo "This will wipe the Yubikey and restore to factory settings" |
read -p "Are you sure? (y/n)" -n 1 -r |
if [[ $REPLY =~ ^[Yy]$ ]] |
then |
gpg-connect-agent <<EOF |
/hex |
scd serialno |
scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40 |
scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40 |
scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40 |
scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40 |
scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40 |
scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40 |
scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40 |
scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40 |
scd apdu 00 e6 00 00 |
scd apdu 00 44 00 00 |
/echo card has been reset to factory defaults |
EOF |
echo "Now remove your Yubikey, wait a few seconds and plug it back in. I'll wait" |
sleep 5 |
echo "Yubikey has been successfully reset." |
echo "The factory default PINs are 123456 (user) and 12345678 (admin)." |
fi |
Once you have a clean slate, we can get started! I’ll wait…ok that’s enough.
Open a terminal an type:
$ gpg --gen-key
Now it should ask for at least a full name, email and password. DO NOT leave your password blank. If your GPG password is blank, anyone who gains access to your computer or the key itself, will be able to use it without authentication. This will generate an RSA 2048 bit key for you without an expiration. If you’d like to have more options, strengthen the key or add an expiration, you can use the expert flag like so:
$ gpg --expert --gen-key
RSA 2048 might not be as secure as other bit lengths, but it’s perfectly fine for most use cases. Once you have the key, you can validate it like so:
$ gpg --list-keys
pub rsa2048/D0B4CE63 2017-12-28 [SC]
uid [ultimate] Ryan Canty <[jrcanty@gmail.com](mailto:jrcanty@gmail.com)>
sub rsa2048/157D0431 2017-12-28 [E]
Let’s talk a bit about this output. If you don’t care, feel free to skip to the next section. As a disclaimer, these are learnings I picked up from doing this and reading a few articles, so if I’m missing something, please leave a comment letting me know where I’m wrong.
The first column is the descriptor, you’ll notice pub
, uid
, and sub
. One of the core pieces of GPG I didn’t know before doing this, is that a single GPG key can have multiple sub-keys that can potentially handle different things, such as encryption, authentication, etc. The first two lines in the output give the public key portion of the master key and the user identifier.
The next column is the key identifier (I’m making up terms here, so again correct me if I’m wrong). The first portion (before the slash) is the type of key and the portion after the slash is the actual unique id for that key. You’ll need this later when you want to edit a key.
Finally the flags in the brackets are capabilities of the key. Here is a good post detailing all the flags, which is where I got this:
[C]
Certificate
[E]
Encryption
[S]
Sign
[A]
Authenticate
To get SSH to work you mainly just need to create an authentication and signature sub-key since we already have an encryption sub-key created for us. We also don’t want to move the primary key to the card since the private key will be unusable by other keys. To do this you’ll want to plug in your YubiKey and then edit the master key you just created. As I mentioned previously, you’ll need the key identifier to do this. So with my example above:
$ gpg --edit-key D0B4CE63
You’ll now be dropped into the GPG shell, where you can do many magical things that are out of the scope of this tutorial, but I’d encourage you to look at the options in help
and find out for yourself.
So with our card plugged in, within the GPG shell you can either create a subkey and transfer it to the YubiKey OR you can create a key directly on the device. We’re going to be doing both, but let’s start with adding a key directly to the card:
gpg> addcardkey
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
Please select the type of key to generate:
(1) Signature key
(2) Encryption key
(3) Authentication key
As I mentioned above, the only type of key we don’t currently have is an Authentication key, so just type 3
to create one of those. You’ll have to enter both the user and admin PIN on your YubiKey as well as your GPG password. As mentioned in the script above, the defaults are 123456
and 12345678
respectively. You’ll want to change those once we’re done adding keys. Just repeat this process for the signature key.
At this point we have only one key on the card. You can verify this by running gpg --card-edit
and noticing the key in the spot for authentication. Now let’s move the existing keys to the YubiKey. Still within the GPG shell, let’s try to switch the key we’re working with. By default, the current key is the primary or master key, with an index of 0. If you want to change the current key, simply run:
gpg> key 1
This should give you an output similar to this:
gpg> key 1
sec rsa2048/D0B4CE63
created: 2017-12-28 expires: never usage: SC
trust: ultimate validity: ultimate
ssb* rsa2048/157D0431
created: 2017-12-28 expires: never usage: E
ssb rsa2048/8E6FAB0A
created: 2017-12-28 expires: never usage: A
card-no: 0006 06156654
ssb rsa2048/16BA1B45
created: 2017-12-28 expires: never usage: S
card-no: 0006 06156654
[ultimate] (1). Ryan Canty <[jrcanty@gmail.com](mailto:jrcanty@gmail.com)>
Notice the *
next to the Encryption key? Ok now that the encryption key is selected, we can move it to the card using the command:
gpg> keytocard
You’ll be asked what key slot you want to place this key into, and be give a single option of Encryption
which you should select with it’s corresponding number. At this point you can exit out of the GPG shell by typing quit
. Make sure you type y to save your changes or you’ll have to start all over.
You should now be able to export the SSH public key to be used with whatever server you have. I’ll show you how to export the SSH key as well as the PGP public key so you can use it to sign code into GitHub with the same key.
First, to export as an ssh key, just run:
$ gpg --export-ssh D0B4CE63
Second, to get the PGP key ASCII armored, run:
$ gpg --export --armor D0B4CE63
Save these two outputs somewhere safe!
This part might be the trickiest to wrap your head around unless you’re familiar with Unix Sockets. I’m not an expert either so bare with me. SSH has an authentication socket (or file) that it uses to communicate with the key chain on your computer. This file is referenced by the SSH_AUTH_SOC
environment variable. What we’re doing here is enabling gpg-agent to run with SSH support and telling SSH where to find the authentication data it needs, as opposed to the standard ~/.ssh/id_rsa
files. So first, you’ll need to kill your running gpg-agent
if you have one running:
$ pkill gpg-agent
Then just restart it with the --enable-ssh-support
flag:
$ gpg-agent --enable-ssh-support --daemon
SSH_AUTH_SOCK=/home/ryan/.gnupg/S.gpg-agent.ssh; export
SSH_AUTH_SOCK;
The output here is what you need to run in order to let SSH know about your GPG private key. You’ll either need to copypasta this onto your shell or add it into your .bashrc
(or whatever fancy shell you’re using). Be careful though, if you have other SSH keys in your .ssh
directory once you have that line in your .bashrc
, SSH will ONLY authenticate with GPG and if your YubiKey isn’t inserted, you’ll get a “permission denied” error.
Now for the final step before hooking your YubiKey to your key ring, strapping on your messenger bag, and riding your bike to a coffee shop: Please, please, please change your PINs. This is super simple using the same method we used earlier.
$ gpg --card-edit
...
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? 1
Once you open up the card edit shell, you’ll need to switch to admin mode and the run the passwd
command. You’ll need to run this twice: once for the admin and once for the user. I’d highly recommend making the keys different. Once this is done, you’re off to the races.
GPG is a fantastic tool that has myriad uses that can help protect your sensitive information during communications. This use not only protects you from fallout of a compromised machine, but it gives you the ability to have a single centralized authentication key for all your devices and logins. Let me know if I missed anything or you’d like help.