### 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