### 2023-12-31
**As someone who actually uses my documentation...**
I decided I'm updating this post to better provide the commands I keep using all the time and to curate a little bit more the information here provided.
I'll be using the lambda sign (λ) to signify updated content.
### 2023-11-12
## **Overview**
This guide explains how to use a YubiKey with PGP (Pretty Good Privacy) for data encryption and communication, as well as signing Git commits, in Gnu and Linux environments. The YubiKey, a USB device that functions as both a smart card and reader, securely stores your PGP public and private keys and manages all encryption and decryption. It's compatible with applications that support OpenPGP smart cards and requires firmware 5.2 or higher for elliptic-curve cryptography (ECC) keys. The focus is on loading your PGP keys onto the YubiKey for encrypted email, file communication, and securely signing Git commits, enhancing data privacy and offering efficient peer-to-peer authentication.
## Prerequisites
Download required packages.
- YubiKeys using OpenPGP are typically configured using [GnuPG (GPG)](https://www.gnupg.org/). Select from the download tab.
- For some MacOS use [GPGTools](https://gpgtools.org/). This is typically preinstalled in most MacOS computers.
- For Windows, select [Gpg4win](https://gpg4win.org/download.html) (includes Kleopatra)
- λ For Linux, we already have openpgp installed. (λ)
- YubiKey Manager, to ensure that the operating system recognizes the YubiKey as a smart card.
YubiKey Manager is available for Windows, OSX, and Linux. Installers for the different operating systems can be downloaded from the Yubico website using the links listed at: [YubiKey Manager](https://www.yubico.com/products/services-software/download/yubikey-manager/)
## Configuring the Yubikey
Step 1. open gpg-card options from the command terminal:
```bash
**user@debian:~$ gpg --card-edit
Application ID ...: D2760001240102000060000000420000
Version ..........: 2.0
Manufacturer .....: unknown
Serial number ....: 00000042
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: forced
Key attributes ...: 2048R 2048R 2048R
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 3 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]**
```
Step 2. Set administrator permissions:
```bash
gpg/card> admin
Admin commands are allowed
```
Step 3. Change Admin and User passwords:
You are changing two PINs: the admin PIN, and the day-to-day PIN
Have two PINs picked out – minimum 8 digits each (only digits, no symbols or letters). Or use an online [random number generator](https://lastpass.com/generatepassword.php)
Safeguard these PINS very, very well and **do not lose them**.
```bash
gpg/card> passwd
gpg: OpenPGP card no. D2760001240102000060000000420000 detected
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
```
Step 4. Change the Admin password. Enter the default PIN, to get permissions to change it.
Default PIN is: 12345678
```bash
Your selection? 3
12345678
PIN changed.
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
```
Step 5. Change day-to-day password. Enter the default PIN, to get permission to change.
Default PIN is: 123456
```bash
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
```
Step 6. Set user information.
```bash
gpg/card> name
Cardholder's surname: Vazquez-Silva
Cardholder's given name: Daniel
gpg/card> lang
Language preferences: en
gpg/card> url
URL to retrieve public key: https://www.danvazquez.com/pgp_signing_key
gpg/card> sex
Sex ((M)ale, (F)emale or space): m
gpg/card> login
Login data (account name): dan
gpg/card>
Application ID ...: D2760001240102000060000000420000
Version ..........: 2.0
Manufacturer .....: unknown
Serial number ....: 00000042
Name of cardholder: Daniel Vazquez-Silva
Language prefs ...: en
Sex ..............: male
URL of public key : https://www.danvazquez.com/pgp_signing_key
Login data .......: dan
Signature PIN ....: forced
Key attributes ...: 2048R 2048R 2048R
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 3 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]
gpg/card> quit
user@debian:~$
```
## Loading Keys
For this step you want to create a new key on an offline system, such as a live Linyx distro like Ubuntu. Note that with live Linux, certain packages (like **scdaemon**) may need to be installed manually.
1. Insert the YubiKey into the USB port if it is not already plugged in.
2. Enter the GPG command: `gpg --expert --full-gen-key`
3. When prompted to specify the key type, enter 1 (for "RSA and RSA (Default)") and press Enter.
4. Specify the size of key you want to generate. Do one of the following:
- For a YubiKey NEO, enter 2048 and press Enter.
- For a YubiKey 4 or 5, enter 4096 and press Enter.
5. Specify the expiration date of the key, and press Enter. Verify the expiration date when prompted.
6. Now you will enter your user information. The prompt will ask you for you Real Name, but the name needs to match on the Git version control system you are committing and signing to.After writing your full name, press Enter.
7. Enter the verified Email Address of where you are Git committing and signing, then press Enter.
8. If desired, enter a Comment about this key, and press Enter. If you have multiple keys, use this to logically tag each key. (To leave the comment blank, just press Enter.)
9. Review the information you entered, make any changes if necessary. If all information is correct, enter O (for Okay) and press Enter.
10. A dialog box is displayed so you can enter the passphrase for your key. While the key is being generated, move your mouse around or type on the keyboard to gain enough entropy. When the key has been generated, you will see several messages displayed. Make a note of the key ID, that is displayed in the message such as "gpg: key 1234ABC marked as ultimately trusted". The key ID in this case is 1234ABC and you will need this key ID to perform other operations.
**Add an authentication key:**
**Note:** Recent release of GnuPG may have the default allowed actions to be both sign and encrypt. Please be sure to check the default allowed action before proceeding with adding the authentication key. This was my case when I did it on my macbook, the step above only created a key for both Signing and Encrypting, but not Authenticating.
1. Insert the YubiKey into the USB port if it is not already plugged in.
2. Enter the GPG command: `gpg --expert --edit-key 1234ABC` (where 1234ABC is the key ID of your key, and can be looked on using this command `gpg --list-secret-keys --keyid-format=long` and identifying the key using the comment/tag from the step above. It is the string after the trailing slash. like this)
```bash
sec rsa4096/5B8B9668A543545E # this is the KEY ID
created: 2023-11-12 expires: 2023-12-12 usage: SC
card-no: 0006 12345678
trust: unknown validity: ultimate
```
3. Enter the command: `addkey`
4. Enter the passphrase for the key. Note that this is the passphrase, and not the PIN or admin PIN.
5. You are prompted to specify the type of key. Enter 8 for RSA.
6. Initial default will be Sign and Encrypt. To select authentication key, toggle S to disable sign, E to disable encrypt, A to enable authentication.
7. Once you can confirm that authentication is the current allowed actions select Q to Finish the selection.
8. Specify the key size. (In my case, I used 4096)
9. Specify the expiration of the authentication key (this should be the same expiration as the key).
10. When prompted to save your changes, enter y (yes).
Steps from 3 - 9: (This is what it should look like)
```bash
gpg> addkey
Secret parts of primary key are stored on-card.
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)
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(12) ECC (encrypt only)
(13) Existing key
(14) Existing key from card
Your selection? 8
Possible actions for this RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? A
Possible actions for this RSA key: Sign Encrypt Authenticate
Current allowed actions: Sign Encrypt Authenticate
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? S
Possible actions for this RSA key: Sign Encrypt Authenticate
Current allowed actions: Encrypt Authenticate
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? E
Possible actions for this RSA key: Sign Encrypt Authenticate
Current allowed actions: Authenticate
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? Q
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0)
Is this correct? (y/N) y
Really create? (y/N) y
```
**Create a backup of your key:**
1. Insert the YubiKey into the USB port if it is not already plugged in.
2. Enter the GPG command: `gpg --export-secret-key --armor 1234ABC` (where 1234ABC is the key ID of your key)
3. Store the text output from the command in a safe place ( e.g. Print the text, save the text in password managers, save the text on a USB storage device).
**Import the key on your YubiKey:**
1. Insert the YubiKey into the USB port if it is not already plugged in.
2. Enter the GPG command: `gpg --edit-key 1234ABC` (where 1234ABC is the key ID of your key)
3. Enter the command: keytocard
4. When prompted if you really want to move your primary key, enter y (yes).
5. When prompted where to store the key, select 1. This will move the signature subkey to the PGP signature slot of the YubiKey.
6. Enter the command: key 1
7. Enter the command: keytocard
8. When prompted where to store the key, select 2. This will move the encryption subkey to the YubiKey.
9. Enter the command: key 1
10. Enter the command: key 2
11. Enter the command: keytocard
12. When prompted where to store the key, select 3. This will move the authentication subkey to the YubiKey.
13. Enter the command: quit
14. When prompted to save your changes, enter n (no). Otherwise, GPG will delete you key from your hard drive, and you won't be able to copy it to another YubiKey/keep it as a backup/etc. See [here](https://lists.gnupg.org/pipermail/gnupg-users/2016-July/056353.html) for a more detailed explanation.
## Using the Key with Multiple Hosts
I'd the say the primary advantage of having you PGP keys imported to the Yubikey is the fact we can use it to sign commits, encrypt files and send authenticated emails on the fly. Technically you should just be able to plug in your key, and use it to sign, encrypt and authenticate things, but it is not the case. The following were the steps I had to take to get my keys to work on my other macbook.
1. First we start by exporting both public key and trust settings. Where KEYID is the identified key after the trailing slash when using this command `gpg --list-secret-keys --keyid-format=long` and exporting it with this command: `export KEYID=0xFF3E7D88647EBCDB # your key here`
```bash
$ gpg --armor --export $KEYID > gpg-public-key-$KEYID.asc
$ gpg --export-ownertrust > gpg-owner-trust.txt
```
Both need to be moved to the second host. I used a service such as BitWarden to move secured files across all my systems.
2. Import the public key to the second host.
```bash
gpg --import gpg-public-key-$KEYID.asc
```
3. Import the trust settings:
```bash
gpg --import-ownertrust < gpg-owner-trust.txt
```
4. Insert your YubiKey into a USB port.
5. Import the private key stubs from the YubiKey:
```bash
gpg --card-status
```
Fair to say you can also fetch the public key from a key server. For example:
```bash
export KEYID=0xFF3E7D88647EBCDB
gpg --keyserver hkps://keyserver.ubuntu.com:443 --recv $KEYID
```
Set ultimate trust:
```bash
gpg --edit-key $KEYID
gpg> trust
Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y
gpg> quit
```
Insert Yubikey into USB port, and import the private key stubs from the Yubikey, you get the point: `gpg --card-status`
## To Test the Keys...
Whats all this configuration for if you cant encrypt stuff with your yubikey? The first thing we need to do is:
```bash
echo "test message string" | gpg --encrypt --armor --recipient $KEYID -o encrypted.txt
```
Then decrypt the file:
```bash
gpg --decrypt --armor encrypted.txt
gpg: anonymous recipient; trying secret key 0x0000000000000000 ...
gpg: okay, we are the anonymous recipient.
gpg: encrypted with RSA key, ID 0x0000000000000000
test message string
```
Sign a message:
```bash
echo "test message string" | gpg --armor --clearsign > signed.txt
```
Then verify the signature:
```bash
gpg --verify signed.txt
gpg: Signature made Wed 25 May 2016 00:00:00 AM UTC
gpg: using RSA key 0xBECFA3C1AE191D15
gpg: Good signature from "Dr Duh <
[email protected]>" [ultimate]
Primary key fingerprint: 011C E16B D45B 27A5 5BA8 776D FF3E 7D88 647E BCDB
Subkey fingerprint: 07AA 7735 E502 C5EB E09E B8B0 BECF A3C1 AE19 1D15
```
## Signing Git Commits using the Yubikey
For Git object signing, the default tool is GPG. Git remains indifferent to the location of signing keys, allowing the use of keys imported onto your YubiKey. To begin, you must inform Git about your key. While GPG may automatically select the correct key if you have only one, it's advisable, especially with multiple keys, to explicitly specify the GPG key you wish to use. This can be achieved with
```bash
git config --global user.signingkey $KEYID
```
Where `$KEYID
is your GPG key ID. By removing the `--global` switch it is possible make this setting repository-specific.
### Signing Tags
Tags are one of the things that can be signed with Git. To do so you can use the `-s` switch:
```bash
git tag foo-1.0 -s -m 'Release 1.0 of Foo'
```
After issuing the command, you will be prompted for your GPG User PIN and a signed tag will be created. You can check the result of this operation by running the following command:
```bash
git show foo-1.0
tag foo-1.0
Tagger: Committer Name <
[email protected]>
Date: Sat Feb 22 10:30:00 2014 +0200
Release 1.0 of Foo
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQEcBAABCAAGBQJW/OhLAAoJEJDLBFvTmUcBKXAH/0i3O/F+YjD8xMknsMZGSa2/
/uGNnF5SUCxQjztWCJecHmp88GdyagT9rcgv/q6eniElwp3M3dQXBTdJ+tPH+m7G
yZdrmuLqrn/NTzZKj3E5xMT9IXJ+jg4RsfhALGqnrG5XFtsB5VVucURbEsrqNM+Y
k5PJPQD4jroT/jOOWBysQMlJRNVZGYhtCC2DkRPQo8lII8/KW5mGu/GJzpQepW4K
vnqd6h9vwhTddzQ+EosNGscQvQBM4+CtLznK3iCYEnDe111wCtMm/ukxd7378/tj
O+mdC0Q+mxTOgIHcgZKBFzVosxiSHVXo7cvmGgk8kuONdaGo2D0k0PqceZPOjRw=
=4ZAu
-----END PGP SIGNATURE-----
```
A similar output can also be achieved with the command:
```bash
git cat-file -p foo-1.0
```
or with the command
```bash
git verify-tag foo-1.0
```
### Signing Commits
You can also sign commits with Git, similar to tagging. Use the following command to sign a commit:
```bash
git commit -S -m 'Fixed a small undocumented feature that made foo crash'
```
You'll be prompted for your User PIN to create the signed commit. The `-S` flag (or `--gpg-sign` in extended form) is used for signing. Note that using lowercase `-s` will only add a `Signed-off-by:` line to the commit message without actually signing the commit.
To view the signature of the last commit:
```bash
git cat-file -p HEAD
```
### Verifying Commits
Signed commits can be verified manually. For instance, to display and verify the latest commit:
```bash
git show HEAD --show-signature
```
This will show the commit details along with the GPG signature verification. Remember, GPG requires the public key of the signer for successful verification.
To verify a specific commit, replace `HEAD` with the commit ID.
Alternative commands for verifying commit signatures include:
- `git log --show-signature` (displays and verifies all commits)
- `git verify-commit HEAD` (displays and verifies the latest commit)
### Merging and Pushing
When merging branches or tags, use:
```bash
git merge --verify-signatures other_branch
```
If signatures cannot be verified, the merge will be aborted. The `-S` switch can also be used to sign merge commits.
When merging annotated tags, Git verifies involved signatures and includes the verification in the merge commit message.
Since Git version 2.2.0, you can sign git pushes with:
```bash
git push --signed
```
This feature ensures the author's intention of pushing a specific set of commits, making them the new tip of a branch.
---
If you have any questions or errata, please email me at
[email protected].
Signing out!
-- Daniel