Goal: to remove my ssh key entirely from disk and use my yubikey instead. Why? Hopefully better security: it will be much more difficult to steal my SSH key and access all the things.
What I use my SSH key for:
- GitHub push/pull access
- Accessing servers that I run
Which method to use
YubiKey offers two different methods that can be used for SSH: PIV or OpenPGP. So which one should I use? Luckily yubico already provides a good discussion on this topic.
Because the SSH communication is done through the GnuPG agent, the OpenPGP option is more complicated than PIV to set up. However, developers who use Git to sign their commits typically choose the OpenPGP path because they already have it up and running.
I’m a developer, so interested in signing my commits too. Guess I should go the hard route. While rare, sometimes I might also want to perform some openpgp stuff too.
Planning for failure
rm .ssh/id_rsa*, I brainstormed on what would happen if I were to lose or have my yubikey stolen.
If I lose the SSH key:
- I can still restore access my servers via rescue mode, or via a friend in some cases.
- GitHub also authenticates via password and MFA and I can simply add a new key.
- Revocation is pretty easy once I regain access. On GitHub I can easily remove the old key via account management. On servers I can remove the key from the
authorized_keys. If I want to be extra diligent most of my servers can be rebuilt from scratch as they are mostly stateless.
If I lose a signing key:
- Revocation is slightly more troublesome, as usually revocation has to occur using a slightly complicated certificate revocation process. If used to verify packages for example, this could be problematic and I need to ensure that I save the revocation key.
- … however, I currently only plan to use this for git commit signing which is not as critical.
Depending on how it’s set up, the yubikey itself will also ask for local authentication before doing any signing or authentication. This should also harden against adversaries that manage to steal the yubikey (which I would think is extremely unlikely in my case).
I don’t see a need to keep a backup of the key, as revocation and resetting should be fairly easy. I will only keep a backup of the revocation certificate.
Installing yubikey manager
sudo apt-add-repository ppa:yubico/stable sudo apt update sudo apt install yubikey-manager
Once installed, can see some info on the plugged in key:
tethik@mimikyu:~$ ykman info Device type: YubiKey 4 Serial number: <snip> Firmware version: 4.3.7 Enabled USB interfaces: OTP+FIDO+CCID Applications OTP Enabled FIDO U2F Enabled OpenPGP Enabled PIV Enabled OATH Enabled FIDO2 Not available
Reset the yubikey
As good practice, it’s probably a good idea to reset the key before use. In my case I had experimented with the yubikey previously and wanted to start from a clean slate.
ykman openpgp reset WARNING! This will delete all stored OpenPGP keys and data and restore factory settings? [y/N]: y Resetting OpenPGP data, don't remove your YubiKey... Success! All data has been cleared and default PINs are set. PIN: 123456 Reset code: NOT SET Admin PIN: 12345678
Generating new gpg key
What follows is my own notes from doing the same and may be useful in the future if the above link no longer works.
The basic procedure here is to generate the keypair on your own computer first, then import it to the yubikey. The yubikey itself does not support generating the PGP key. The key itself is also has two subkeys attached, one for signing and one for authentication, as per recommendations in the above reference. If needed, a further one could be added for encryption.
My yubikey (version 4) only supports 2048 bit rsa keys, so make sure to pick a cipher that your version of yubikey supports. Otherwise it will fail when you import the key to the yubikey.
I generate key with…
From the output, grab the revocation certificate and store it somewhere safe.
gpg: key 79A936FAEE8C3B4A marked as ultimately trusted gpg: revocation certificate stored as '/home/tethik/.gnupg/openpgp-revocs.d/644892D6680B2FABB541FA5779A936FAEE8C3B4A.rev' public and secret key created and signed.
gpgp --expert --edit-key two subkeys are added. One for authentication
and one for signing.
gpg --expert --edit-key 13AFCE85 gpg (GnuPG) 2.0.22; Copyright (C) 2013 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. pub 2048R/13AFCE85 created: 2014-03-07 expires: 2014-06-15 usage: SC trust: ultimate validity: ultimate sub 2048R/D7421CDF created: 2014-03-07 expires: 2014-06-15 usage: E [ultimate] (1). Foo Bar <firstname.lastname@example.org> gpg> addkey 2048-bit RSA key, ID 13AFCE85, created 2014-03-07 Please select what kind of key you want: (3) DSA (sign only) (4) RSA (sign only) (5) Elgamal (encrypt only) (6) RSA (encrypt only) (7) DSA (set your own capabilities) (8) RSA (set your own capabilities) Your selection?
Finally, the key is imported to the yubikey. Note the
gpg --edit-key 13AFCE85 gpg (GnuPG) 2.0.22; Copyright (C) 2013 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. pub 2048R/13AFCE85 created: 2014-03-07 expires: 2014-06-15 usage: SC trust: ultimate validity: ultimate sub 2048R/D7421CDF created: 2014-03-07 expires: 2014-06-15 usage: E sub 2048R/B4000C55 created: 2014-03-07 expires: 2014-06-15 usage: A [ultimate] (1). Foo Bar <email@example.com> gpg> toggle sec 2048R/13AFCE85 created: 2014-03-07 expires: 2014-06-15 ssb 2048R/D7421CDF created: 2014-03-07 expires: never ssb 2048R/B4000C55 created: 2014-03-07 expires: never (1) Foo Bar <firstname.lastname@example.org> gpg> keytocard Really move the primary key? (y/N) y Signature key ....: [none] Encryption key....: [none] Authentication key: [none] Please select where to store the key: (1) Signature key (3) Authentication key Your selection? 1
Generate the public key via the following. The string it returns can be used for the
authorized_keys file as well as GitHub auth.
gpg --export-ssh-key 4B4C120067AC0E52 (authentication key)
Switch from OpenSSH ssh-agent to GnuPG as ssh-agent.
First find path to the unix socket created by gnupg
gpgconf --list-dirs | grep ssh
Then set the
SSH_AUTH_SOCK variable with that path. E.g.
Consider adding it to your
.bashrc to automatically apply it for new terminals.
Git Commit Signing
Enable commit signing in your git config:
git config --global user.signingkey 79A936FAEE8C3B4A (signing key) git config --global commit.gpgSign true
You should ensure also that the email you configured for git is the same as the one you generated the gpg key for, otherwise github will show your commits as unverified.
To verify that your commits are signed, you should see them show up if
you use the
git log --show-signature
Changing the OpenGPG PIN
When using the key now, it should ask you for a PIN. There are two pins to keep track of: the Normal PIN which is the one used for the daily operations of signing/encrypting using the card, and an Admin PIN which authorizes you to change settings on the card (e.g. force signature) as well as reset the Normal PIN.
By default, the Admin PIN’s value is 12345678. The normal PIN is 123456. The Admin PIN seems to require at least 8 digits, while the normal PIN can be shorter.
You can and should change these via the
gpg --change-pin command. This will
provide some protection if your key from being used if it is stolen
tethik@mimikyu:~$ gpg --change-pin gpg: OpenPGP card no. <snip> detected 1 - change PIN 2 - unblock PIN 3 - change Admin PIN 4 - set the Reset Code Q - quit
Option 2) allows you to change the normal PIN using the admin PIN, which is why it’s important to change both.