All Articles

Yubikey OpenPGP Setup for SSH and Commit Signing

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.

https://developers.yubico.com/PIV/Guides/SecuringSSHwithOpenPGPor_PIV.html

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

Before I 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.

Setup

Installing yubikey manager

Reference: https://developers.yubico.com/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

Reference: https://developers.yubico.com/PGP/Importing_keys.html

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…

gpg --full-gen-key

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.

Using 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 <foo@example.com>

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 keytocard command.

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 <foo@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 <foo@example.com>

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

SSH

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.

export SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh

Consider adding it to your .bashrc to automatically apply it for new terminals.

Git Commit Signing

https://developers.yubico.com/PGP/Git_signing.html

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 --show-signature option.

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.