Post

OpenPGP Part 5: Using a Yubikey

Welcome back to the OpenPGP Series. Here’s what we’ve done so far:

At this point in the series we have a functioning set of security keys that we can perform various tasks with. However, all of the private subkeys reside on our personal machine. In this post I’m going to show you how to get your keys onto a smart card device. Specifically, a Yubikey 5 Series device.

Smart cards are physical devices that can store keys in a very secure way and are resistant to tampering. When using a smart card for key storage we have two options:

  1. Move the keys to the smart card
  2. Copy the keys to the smart card

Moving the keys is just what it sounds like. The private keys are copied to the smart card device and then removed from your local machine. Anytime you need to use the private portion of one of your keys you will have to use the physical device and enter the passphrase as well as the smart card PIN. This provides an additional layer of security.

Copying the keys treats the smart card as another very secure backup location. The keys will still reside on your computer but they will also be available on the smart card. If you carry that device with you it can serve as another method of accessing and using your subkeys.

The decision to move or copy is a personal one. Think through your use cases for the keys and the layers of security you want to implement. From a logistical standpoint, the process for copying vs moving is almost identical with only one difference as will be illustrated below.

Prepare the Yubikey

It’s non-trivial to use a Yubikey in a Docker container so if you’ve been following along so far in Docker this is where you’ll have to move to your main machine. But don’t worry, like other things, there is no irreversible damage you’ll do on the Yubikey. In fact, you can continually reset it to try things over and over.

Before getting started you will likely need to install some additional things on your machine for your Yubikey to be detected. The instructions are likely to change over time so please see the Yubico docs for help there.1

Make sure your Yubikey is plugged in and then run the following command to make sure everything is installed properly on your system to allow gpg to interact with it.

1
gpg --card-status
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Reader ...........: Yubico YubiKey OTP FIDO CCID 00 00
Application ID ...: D2760001240103040006139148540000
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
Serial number ....: 13914854
Name of cardholder: [not set]
Language prefs ...: [not set]
Salutation .......:
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
KDF setting ......: off
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

The Yubikey adds an additional layer of security on top of the key passphrase by requiring PINs to be entered. There are two types of PINS: normal and admin.

  • Default PIN: 123456
  • Default Admin PIN: 12345678

The first thing we are going to do is change the default PINs to something more secure. To do that we need to enter the gpg command prompt a special way.

1
gpg --card-edit

This will open a prompt that looks familiar.

1
gpg/card>

It’s the same gpg prompt we’re used to but there is now a card indicator letting us know we are interacting with a smart card device. To change the PINs we will need to be in admin mode by running the admin command and then use the passwd command to start resetting the PINS. Just follow the prompts. PINS on the Yubikey 5 must be a minimum of 4 characters but can be as long as 63 alphanumeric characters so don’t feel like you have to stick to numbers. After setting the PINS you may want to store them in a secure digital vault like Bitwarden, KeePass, LastPass, etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
gpg/card> admin <------------------------------------- Enter admin mode
Admin commands are allowed

gpg/card> passwd < ----------------------------------- Change PINS command
gpg: OpenPGP card no. D2760001240103040006139148540000 detected

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 1 <----------------------------------- Change PIN
PIN changed.

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 3 <----------------------------------- Change Admin PIN
PIN changed.

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? q <----------------------------------- Quit

A Quick Note On Slot Limits

Depending on your smart card you may have limited slots for your keys. The Yubikey 5, for example, has 3 slots (signature, authentication, encryption). But we have 4 subkeys (remember we created two signing keys) so we have to make some decisions. Other options like the YubiHSM can hold significantly more keys but also carry a much higher price tag (about 10x the cost). My main driver Yubikey will be used for signing Git commits so I’m going to store the ECC signing key on the primary Yubikey and the RSA one on a backup.

Move/Copy Keys to Card

As I mentioned above, whether moving or copying your keys to the card, the process is essentially the same. The only difference is whether you save the changes. In the process outlined below the steps are all the same. Calling keytocard copies the key to the Yubikey. When you quit the gpg app it will ask you if you want to save your changes. If you say yes, the private keys are removed from your local system. If you say no, they are left on your local system but have also been copied to the Yubikey.

One more time. To move keys, save your changes when exiting gpg. To copy keys, don’t save changes. It’s kind of confusing but it’s how it works.

Another head’s up. Selecting the keys is a little clunky. You select them by entering key [key number]. It’s also how you deselect them. You can always tell what keys are selected based on whether there is a * by it. In the following example I’m copying just the first key (encryption key) to show how it’s done.

1
gpg --edit-key {your primary key ID}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
gpg> key 1 <---------------------------------------------- Select key 1 (encryption)

sec  rsa4096/8AE9ED932D36F9C6
     created: 2024-01-12  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb* rsa4096/76C549929B9C6627 <--------------------------- * indicates key is selected
     created: 2024-01-12  expires: never       usage: E
ssb  rsa4096/C63CC17E8FA62728
     created: 2024-01-12  expires: 2026-01-11  usage: S
ssb  ed25519/23B3DBA54165A31B
     created: 2024-01-12  expires: 2026-01-11  usage: S
ssb  rsa4096/EF101A0F7AE16342
     created: 2024-01-12  expires: 2026-01-11  usage: A
[ultimate] (1). Fake McFakerton <me@fake.com>

gpg> keytocard <------------------------------------------ Copy key to Yubikey
Please select where to store the key:
   (2) Encryption key
Your selection? 2 <--------------------------------------- Save it to the encryption slot

sec  rsa4096/8AE9ED932D36F9C6
     created: 2024-01-12  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb* rsa4096/76C549929B9C6627 <--------------------------- Key 1 still selected
     created: 2024-01-12  expires: never       usage: E
ssb  rsa4096/C63CC17E8FA62728
     created: 2024-01-12  expires: 2026-01-11  usage: S
ssb  ed25519/23B3DBA54165A31B
     created: 2024-01-12  expires: 2026-01-11  usage: S
ssb  rsa4096/EF101A0F7AE16342
     created: 2024-01-12  expires: 2026-01-11  usage: A
[ultimate] (1). Fake McFakerton <me@fake.com>

gpg> key 1 <---------------------------------------------- Deselect key 1

sec  rsa4096/8AE9ED932D36F9C6
     created: 2024-01-12  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/76C549929B9C6627 <--------------------------- Key 1 no longer selected
     created: 2024-01-12  expires: never       usage: E
ssb  rsa4096/C63CC17E8FA62728
     created: 2024-01-12  expires: 2026-01-11  usage: S
ssb  ed25519/23B3DBA54165A31B
     created: 2024-01-12  expires: 2026-01-11  usage: S
ssb  rsa4096/EF101A0F7AE16342
     created: 2024-01-12  expires: 2026-01-11  usage: A
[ultimate] (1). Fake McFakerton <me@fake.com>

Repeat this process for each key and save it to the appropriate slot on the Yubikey.

Once you are finished you will exit the program.

DECISION TIME: Remember, this is where you decide if you want to move or just copy the keys. Save changes to move, don’t save changes to just copy and leave the private keys on your local machine as well.

I’m going to save the changes to show how a move effects the local machine view of keys. After saving the changes and moving the keys let’s look at the secret key output again.

1
gpg --list-secret-keys
1
2
3
4
5
6
7
8
------------------------------
sec#  rsa4096 2024-01-12 [SC]
      B7FE87DD13FD81F63C1AA4948AE9ED932D36F9C6
uid           [ultimate] Fake McFakerton <me@fake.com>
ssb>  rsa4096 2024-01-12 [E]
ssb   rsa4096 2024-01-12 [S] [expires: 2026-01-11]
ssb>  ed25519 2024-01-12 [S] [expires: 2026-01-11]
ssb>  rsa4096 2024-01-12 [A] [expires: 2026-01-11]

There are a couple of things to notice here. First the # next to our Primary Key. Remember, that indicates our Primary Key private key is not on this machine. We moved it offline. The next thing is that the 3 keys we moved (not copied) have a > indicator. This tells you the private keys don’t exist locally, but are stored on a smart card.

At this point, any operation using the private key portion of one of our subkeys will require the Yubikey to be plugged in. It will prompt for both the passphrase of the subkey as well as the PIN of the Yubikey.

Factory Reset

Made a mistake? Need to start over? No problem. You can simply factory reset your Yubikey.

1
gpg --edit-card
1
2
3
4
5
6
7
8
9
10
gpg/card> admin
Admin commands are allowed

gpg/card> factory-reset
gpg: OpenPGP card no. D2760001240103040006139148540000 detected

gpg: Note: This command destroys all keys stored on the card!

Continue? (y/N) y
Really do a factory reset? (enter "yes") yes

What’s Next?

This concludes the setup portion of the series. So far we have generated a series of keys, backed them up, taken the Primary Key offline, and learned how to copy or move keys to a physical smart card device. We have established a secure digital identity that we can now use to perform various task which is what the next part of this series will cover.

We will go through practical examples of how to use our keys. This will include things like signing Git commits, using SSH to connect to a remove server, and encrypting files.

References

© Kevin Sidwar

Comments powered by Disqus.