EDIT: Update 07/07/2018, added SSH_AUTH_SOCK
information, a few pointers about key generation and backup, and info about gpg-agent’s bad behavior.
When I first started at the job I’m currently at at Gandi,
I was given a Yubikey NEO, looked into it for a few minutes and quickly decided
to not give more thought about it. I put it away and didn’t look back, partly
because the yubikey-personalization-gui
is maybe the most unusable and
unintelligible interface I’ve ever seen, and partly because I had a 4096 bit key
and didn’t think to make smaller subkeys to put in the smartcard (the NEO only
supports 2048 bit keys).
A few weeks ago I decided to buy a Yubikey 4 on a drunken evening and got it in the mail a few days later, having completely forgotten I had ordered it. I decided to finally take a look at how this thing worked and what I could actually do with it.
So, first things first. The Yubikey is basically a GPG smartcard, with an added X.509 smartcard, WITH added U2F support. It can also be used to do TOTP/HOTP with the Yubico app on android smartphones or computers. It can probably also be programmed to solve quadratic equations, but I haven’t tried.
To clarify: functionally, a smartcard is a device that has an integrated small CPU and storage space for keys, and that you can only write keys to, not read from. The integrated CPU can then be asked to sign/encrypt/decrypt arbitrary data, but the keys can never be compromised from the key itself.
Anyway, my use case is as such: beforehand, I stored my GPG and SSH keys on my everyday computers, but I couldn’t use either of them on my work computers (I didn’t want to leave personal stuff like that on work equipment). I wanted to be able to connect to my servers and ideally decrypt files on any computer I encounter (okay, decrypting files on anyone’s computer is not a great idea, but there are a few computers I trust enough to decrypt files on them but not to give them my private keys). I also use pass to store my passwords, decrypting them with the yubikey everywhere is nice. Since the key allows me to access my passwords, I feel like putting the second factor of authentication on it just wouldn’t be reasonable, so I’m still using my phone as a 2fa TOTP source. Finally, I stored an X.509 cert for OpenVPN to try and see how it would work with the yubikey. I’m not using it for now but it’s there.
GPG key storage
Anyways, here’s how to use this thing:
-
First, always keep a backup of your private keys. You’ll need them to sign other people’s keys at keysigning parties, and if you lose the yubikey you’ll also need them to recover… well, everything. Anyway, keeping a good offline backup or two is important.
-
We’re gonna start by adding our GPG subkeys to the yubikey. That article covers pretty much everything, except generating an Authentication subkey, which is done by doing
gpg --expert --edit-key <KeyID>
, thenaddkey
. You now need to select “(8) RSA (set your own capabilities)” as the type of key, then typeS
to toggle signing off,E
to toggle encryption off, and finallyA
to toggle authentication on. TypeQ
to confirm and quit, then keep as usual for the key size/expiration date/etc. You’re now done, and we can start by setting up the yubikey. This is really easy, since the yubikey is detected as a smartcard by gpg:
$ gpg --edit-card gpg --card-status Reader ...........: Yubico Yubikey 4 OTP U2F CCID 00 00 Application ID ...: D2760001240102010006076003030000 Version ..........: 2.1 Manufacturer .....: Yubico Serial number ....: 07600303 Name of cardholder: [not set] Language prefs ...: [not set] Sex ..............: unspecified URL of public key : [not set] Login data .......: [not set] Signature PIN ....: not forced Key attributes ...: rsa2048 rsa2048 rsa2048 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] gpg/card> admin Admin commands are allowed gpg/card> passwd gpg: OpenPGP card no. D2760001240102010006076003030000 detected 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 gpg/card> url URL to retrieve public key: https://pub.wxcafe.net/wxcafe.asc gpg/card> name Cardholder's surname: Hertling Cardholder's given name: Clément Error: Only plain ASCII is currently allowed. Cardholder's given name: Clement gpg/card> login Login data (account name): wxcafe gpg/card> sex Sex ((M)ale, (F)emale or space): M gpg/card> lang Language preferences: en gpg/card> list Reader ...........: Yubico Yubikey 4 OTP U2F CCID 00 00 Application ID ...: D2760001240102010006076003030000 Version ..........: 2.1 Manufacturer .....: Yubico Serial number ....: 07600303 Name of cardholder: Clement Hertling Language prefs ...: en Sex ..............: male URL of public key : https://pub.wxcafe.net/wxcafe.asc Login data .......: wxcafe Signature PIN ....: not forced Key attributes ...: rsa2048 rsa2048 rsa2048 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] gpg/card> %
The default PINs are 123456
for the user PIN and 12345678
for the admin PIN.
Do take caution to export the private keys for safekeeping BEFORE moving them
to the yubikey (the gpg keytocard
command MOVES the keys, after you’ve run it
you don’t have the private keys available anymore to backup) (backups are
easily done with gpg --armor --export-secret-keys <KeyID> > out.asc
and gpg
--armor --export-secret-subkeys <KeyID> > subkeys_out.asc
. You obviously need
to save these to a secure location.)
Now that we’ve prepped the card, we’re gonna move the keys over to it. We’re gonna move only the subkeys over, and since we’re gonna need to use the yubikey for everything we’ll have an Encryption subkey, a Signing subkey and an Authentication subkey.
$ gpg --edit-key wxcafe@wxcafe.net gpg (GnuPG) 2.2.7; Copyright (C) 2018 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Secret key is available. sec rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 usage: SC trust: ultimate validity: ultimate ssb rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 usage: S ssb rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 usage: E ssb rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 usage: A [ultimate] (1). Wxcafé <wxcafe@wxcafe.net> [ultimate] (2) Wxcafé <wxcafe@wxcafe.net> [ultimate] (3) Wxcafé <wxcafe@wxcafe.net> [ultimate] (4) Wxcafé <🖕@fu.cking.network> [ultimate] (5) [jpeg image of size 22243] gpg> key 1 sec rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 usage: SC trust: ultimate validity: ultimate ssb* rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 usage: S ssb rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 usage: E ssb rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 usage: A [ultimate] (1). Wxcafé <wxcafe@wxcafe.net> [ultimate] (2) Wxcafé <wxcafe@wxcafe.net> [ultimate] (3) Wxcafé <wxcafe@wxcafe.net> [ultimate] (4) Wxcafé <🖕@fu.cking.network> [ultimate] (5) [jpeg image of size 22243] gpg> keytocard Please select where to store the key: (1) Signature key (3) Authentication key Your selection? 1 sec rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 usage: SC trust: ultimate validity: ultimate ssb* rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 usage: S ssb rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 usage: E ssb rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 usage: A [ultimate] (1). Wxcafé <wxcafe@wxcafe.net> [ultimate] (2) Wxcafé <wxcafe@wxcafe.net> [ultimate] (3) Wxcafé <wxcafe@wxcafe.net> [ultimate] (4) Wxcafé <🖕@fu.cking.network> [ultimate] (5) [jpeg image of size 22243] gpg> key 1 sec rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 usage: SC trust: ultimate validity: ultimate ssb rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 usage: S ssb rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 usage: E ssb rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 usage: A [ultimate] (1). Wxcafé <wxcafe@wxcafe.net> [ultimate] (2) Wxcafé <wxcafe@wxcafe.net> [ultimate] (3) Wxcafé <wxcafe@wxcafe.net> [ultimate] (4) Wxcafé <🖕@fu.cking.network> [ultimate] (5) [jpeg image of size 22243] gpg> key 2 sec rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 usage: SC trust: ultimate validity: ultimate ssb rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 usage: S ssb* rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 usage: E ssb rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 usage: A [ultimate] (1). Wxcafé <wxcafe@wxcafe.net> [ultimate] (2) Wxcafé <wxcafe@wxcafe.net> [ultimate] (3) Wxcafé <wxcafe@wxcafe.net> [ultimate] (4) Wxcafé <🖕@fu.cking.network> [ultimate] (5) [jpeg image of size 22243] gpg> keytocard Please select where to store the key: (2) Encryption key Your selection? 2 sec rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 usage: SC trust: ultimate validity: ultimate ssb rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 usage: S ssb* rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 usage: E ssb rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 usage: A [ultimate] (1). Wxcafé <wxcafe@wxcafe.net> [ultimate] (2) Wxcafé <wxcafe@wxcafe.net> [ultimate] (3) Wxcafé <wxcafe@wxcafe.net> [ultimate] (4) Wxcafé <🖕@fu.cking.network> [ultimate] (5) [jpeg image of size 22243] gpg> key 2 sec rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 usage: SC trust: ultimate validity: ultimate ssb rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 usage: S ssb rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 usage: E ssb rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 usage: A [ultimate] (1). Wxcafé <wxcafe@wxcafe.net> [ultimate] (2) Wxcafé <wxcafe@wxcafe.net> [ultimate] (3) Wxcafé <wxcafe@wxcafe.net> [ultimate] (4) Wxcafé <🖕@fu.cking.network> [ultimate] (5) [jpeg image of size 22243] gpg> key 3 sec rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 usage: SC trust: ultimate validity: ultimate ssb rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 usage: S ssb rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 usage: E ssb* rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 usage: A [ultimate] (1). Wxcafé <wxcafe@wxcafe.net> [ultimate] (2) Wxcafé <wxcafe@wxcafe.net> [ultimate] (3) Wxcafé <wxcafe@wxcafe.net> [ultimate] (4) Wxcafé <🖕@fu.cking.network> [ultimate] (5) [jpeg image of size 22243] gpg> keytocard Please select where to store the key: (3) Authentication key Your selection? 3 sec rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 usage: SC trust: ultimate validity: ultimate ssb rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 usage: S ssb rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 usage: E ssb* rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 usage: A [ultimate] (1). Wxcafé <wxcafe@wxcafe.net> [ultimate] (2) Wxcafé <wxcafe@wxcafe.net> [ultimate] (3) Wxcafé <wxcafe@wxcafe.net> [ultimate] (4) Wxcafé <🖕@fu.cking.network> [ultimate] (5) [jpeg image of size 22243] gpg> key 3 gpg> save gpg> %
Now that this is done, we only need to gpg --card-status
should show us this:
Reader ...........: 1050:0407:X:0 Application ID ...: D2760001240102010006076003030000 Version ..........: 2.1 Manufacturer .....: Yubico Serial number ....: 07600303 Name of cardholder: Clement Hertling Language prefs ...: en Sex ..............: male URL of public key : https://pub.wxcafe.net/wxcafe.asc Login data .......: wxcafe Signature PIN ....: not forced Key attributes ...: rsa4096 rsa4096 rsa4096 Max. PIN lengths .: 127 127 127 PIN retry counter : 3 0 3 Signature counter : 0 Signature key ....: 752F 1ED2 D038 BCB6 F09C A942 11E9 9643 DEE9 E336 created ....: 2018-06-29 22:27:20 Encryption key....: 37B4 4050 8744 344C 0975 2656 E00F 1332 4D0D 1703 created ....: 2018-06-29 22:28:22 Authentication key: 775D 1613 1B3F FEE7 843F D3BC FD92 FB8B D73D 1D70 created ....: 2018-06-29 22:29:29 General key info..: sub rsa4096/11E99643DEE9E336 2018-06-29 Wxcafé <wxcafe@wxcafe.net> sec# rsa4096/58DD226B3EA71DC7 created: 2016-12-29 expires: 2019-06-18 ssb> rsa4096/11E99643DEE9E336 created: 2018-06-29 expires: 2019-06-29 card-no: 0006 07600303 ssb> rsa4096/E00F13324D0D1703 created: 2018-06-29 expires: 2019-06-29 card-no: 0006 07600303 ssb> rsa4096/FD92FB8BD73D1D70 created: 2018-06-29 expires: 2019-06-29 card-no: 0006 07600303
To actually use the keys we just copied over, we need to install a smarcard
daemon (apt install scdaemon
) and authorize our user to access it. For that we
write a udev rule in (/etc/udev/rules.d/70-yubikey.conf
) with this in it:
https://raw.githubusercontent.com/Yubico/libu2f-host/master/70-u2f.rules
Now we should be able to use the yubikey to sign and encrypt whatever we want!
We can test this by doing touch test; gpg -a --sign test
, which should prompt us
for the yubikey PIN and then create the test.asc
file.
We can still do gpg --export-secret-keys <key-id>
, and while it looks like it
works (because GPG’s output is undecipherable and it’s UX is the worst thing
I’ve ever seen), it actually outputs “stubs”, which allows a system to “see” the
keys on the card. They’re not needed anymore since for a while now, gpg
–card-status automatically detects the keys on the card.
Now we want to use our gpg authentication key with SSH, to log in to our
servers. To do that, we need to tell gpg-agent to act as an ssh-agent, by adding
a single line to its configuration: echo 'enable-ssh-support' >>
.gnupg/gpg-agent.conf
. Then we restart gpg-agent (gpgconf --kill gpg-agent
).
Then, we need to tell ssh to use gpg-agent’s socket as its agent. We do this by
adding a small snippet to our $shrc
(for me, ~/.zshrc
):
## use gpg agent as ssh agent if which gpgconf 2>&1 >>/dev/null ; then unset SSH_AGENT_PID if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)" fi fi
Unplug the key, plug it back in, run gpg --card-status
,
then ssh-add -L
should show you a public key that ends with
cardno:xxxxxxxxxxxx
. That means it’s done, you can now add this public key to
.ssh/authorized_keys
on your remote systems and you should be able to log in
with that key.
Oh, and, side note. gpg-agent
won’t actually delete your cached keys when you
ssh-add -D
, which is fucking bullshit, but in the meantime the solution is to
gpg-connect-agent
, then KEYINFO --ssh-list --ssh-fpr
to list the cached
keys, and then you can DELETE_KEY <FINGERPRINT>
that particular key, with the
fingerprint being the part right after KEYINFO. Quit by saying /bye
X.509 key and certificate storage
Now for the X.509 storage, this is a bit easier. You will need to make a pkcs12
out of your certificate and associated key (openssl pkcs12 -export -out out.p12
-inkey key.pem -in cert.crt --certfile ca.crt -nodes
), and then we can import
it into the yubikey (this will not destroy the .p12 file, nor the key or cert,
because this has a sensible UX, as opposed to gnupg): yubico-piv-tool -s 9c -i
out.p12 -K PKCS12 -a import-key -a import-cert -k
In my case, I was using this with OpenVPN, so I needed to install opensc-pkcs11
(which is a library that allows applications to see certificates from
a smartcard), then to look up under which ID OpenVPN saw my certificate with
openvpn --show-pkcs11-ids /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
. After
that, I updated my OpenVPN configuration by replacing the cert and key lines
with
pkcs11-id 'piv_II/PKCS\x2315\x20emulated/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' pkcs11-providers /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
And then it worked like a charm! What actually stopped me is that I use
networkmanager on my machines since wicd
is dead, and it doesn’t support that
kind of config. Of course, wicd would since it can launch arbitrary scripts…
but I’m stuck with NM and so I can’t use this easily, sadly.
Pass
So, technically pass is not related to the yubikey at all, and doesn’t directly interact with it. If you haven’t heard about it, it’s a simple password manager for unix written in bash by zx2c4. But it uses GPG as an encryption mechanism, and since I was moving towards using GPG to authenticate SSH connections, I thought I might as well start using pass with the yubikey. So, here goes.
Pass is a very simple password manager, so simple that all it does is keep your
passwords for you and output them when you ask nicely. It’s installed on
basically all systems with the package manager (in my case, apt install pass
),
and then it’s a matter of pass init <keyid>
to initialize the folder with the
specified gpg key id, pass git init
and pass git remote add origin <remote>
to use git to store the passwords (that’s optional, but it’s so much better it’d
be dumb not to do it…). Then you can add a password with pass insert <path>
,
or generate one with pass generate <path> <length>
. Don’t forget to pass git
push
(it’ll commit automatically if it’s in git).
To see all the passwords you have in pass, use pass ls
, and to read a specific
password use pass <path>
. The problem with that is that the password is
written to stdout, and we don’t really want to a) have people able to snoop the
password, nor b) open a terminal and copy-paste every time we want to get
a password. That’s where passmenu comes in! It’s a simple script included with
pass (at least it is in debian) in
/usr/share/doc/pass/examples/dmenu/passmenu
, and it relies on dmenu to show
you all of your passwords. From there, you can type a few letters and select
which password you want, and once you confirm your selection it will copy it
into your clipboard, ready for you to paste into any password field. There are
extensions to have it support TOTP and other stuff too, but as previously I’m
not too comfortable with putting all my eggs in the same basket.
So yeah, move all your passwords to pass and make them as complex as possible!
When you get to a new machine you can simply git clone <repo> ~/.password-store
and apt install pass
and you’re all set! Don’t lose your GPG key though, or
you’re SCREWED.
Don’t lose it. Ever. Print it out and put it in the bank. Whatever. But don’t lose it.
Anyway, that was it! I’m seriously happy with how this solution works for me, and how secure it is without sacrificing much towards usability, even improving it in most cases.
(P.S.: I don’t get anything by saying this, but my employer (gandi) has a partnership thing in place with yubico, such that if you login on this page with a gandi account, you get 20% off your yubikey. You don’t even need to buy anything with gandi afaik, only to have an account)