Yubikey GPG
Setup GPG with Yubikey
Intro
This guide is based on drduh/YubiKey-Guide and how I setup my YubiKeys. I differ on some parts for example I use ed25519
.
Prerequisites
Previous articles:
Setup
Insert and boot the USB (air-gapped system without network capabilities)
Identity
export IDENTITY="dohjon <[email protected]>"
Expiration
export EXPIRATION=2y
Passphrase
Write down the passphrase L4QC-272D-JHZZ-G4B6-VH83-62YC
export CERTIFY_PASS=$(LC_ALL=C tr -dc 'A-Z1-9' < /dev/urandom | \
tr -d "1IOS5U" | fold -w 30 | sed "-es/./ /"{1..26..5} | \
cut -c2- | tr " " "-" | head -1) ; echo "\n$CERTIFY_PASS\n"
Create Certify key (PRIVATE KEY)
gpg --batch --passphrase "$CERTIFY_PASS" --quick-generate-key "$IDENTITY" ed25519 cert never
export KEYID=$(gpg -k --with-colons "$IDENTITY" | awk -F: '/^pub:/ { print $5; exit }')
export KEYFP=$(gpg -k --with-colons "$IDENTITY" | awk -F: '/^fpr:/ { print $10; exit }')
# save and view private key (Certify key) identifier and fingerprint for use later
printf "\nKey ID: %40s\nKey FP: %40s\n\n" "$KEYID" "$KEYFP"
Create subkeys for signing, encryption, and authentication
gpg --batch --pinentry-mode=loopback --passphrase "$CERTIFY_PASS" --quick-add-key $KEYFP ed25519 sign 2y
gpg --batch --pinentry-mode=loopback --passphrase "$CERTIFY_PASS" --quick-add-key $KEYFP cv25519 encr 2y
gpg --batch --pinentry-mode=loopback --passphrase "$CERTIFY_PASS" --quick-add-key $KEYFP ed25519 auth 2y
# List available secret keys
gpg --list-secret-keys
Backup keys
Save a copy of the Certify key, Subkeys and public key:
gpg --output $GNUPGHOME/$KEYID-Certify.key \
--batch --pinentry-mode=loopback --passphrase "$CERTIFY_PASS" \
--armor --export-secret-keys $KEYID
gpg --output $GNUPGHOME/$KEYID-Subkeys.key \
--batch --pinentry-mode=loopback --passphrase "$CERTIFY_PASS" \
--armor --export-secret-subkeys $KEYID
gpg --output $GNUPGHOME/$KEYID-$(date +%F).asc \
--armor --export $KEYID
Insert USB and decrypt and mount the encrypted LUKS volume(s). Then copy from live usb to backup usb
lsblk # sudo dmesg | tail
sudo cryptsetup luksOpen /dev/sdb1 gnupg-secrets
sudo mkdir -p /mnt/encrypted-storage
sudo mount /dev/mapper/gnupg-secrets /mnt/encrypted-storage
# Copy from live usb to backup usb
sudo cp -av $GNUPGHOME /mnt/encrypted-storage/
# Unmount and close the encrypted volume
sudo umount /mnt/encrypted-storage
sudo cryptsetup luksClose gnupg-secrets
Transfer Subkeys
# transfer Signature key
gpg --edit-key $KEYID
key 1 # Usage: S
keytocard
1
# follow prompt enter CERTIFY_PASS and ADMIN_PIN
save
# transfer Authentication key
gpg --edit-key $KEYID
key 2 # Usage: A
keytocard
3
# follow prompt enter CERTIFY_PASS and ADMIN_PIN
save
# transfer Encryption key
gpg --edit-key $KEYID
key 3 # Usage: E
keytocard
2
# follow prompt enter CERTIFY_PASS and ADMIN_PIN
save
# Verify
gpg --list-secret-keys
# The `>` after a tag indicates the key is stored on a smart card.
$ sec rsa4096/0xF0F2CFEB04341FB5 2024-01-01 [C]
$ Key fingerprint = 4E2C 1FA3 372C BA96 A06A C34A F0F2 CFEB 0434 1FB5
$ uid [ultimate] YubiKey User <yubikey@example>
$ ssb> rsa4096/0xB3CD10E502E19637 2024-01-01 [S] [expires: 2026-05-01]
$ ssb> rsa4096/0x30CBE8C4B085B9F7 2024-01-01 [E] [expires: 2026-05-01]
$ ssb> rsa4096/0xAD9E24E1B8CB9600 2024-01-01 [A] [expires: 2026-05-01]
Configure touch
Finally, configure the Yubikey to require a touch to sign or decrypt anything. This avoids any random program from using the key without manual approval.
# help
ykman openpgp keys set-touch -h
# authentication
ykman openpgp keys set-touch aut on
# signature
ykman openpgp keys set-touch sig on
# encryption
ykman openpgp keys set-touch dec on
Upload to public server
After on laptop with internet connection I inserted yubikey and uploaded public key to keyserver keys.openpgp.org
gpg --export [email protected] | curl -T - https://keys.openpgp.org
# after adding key to public server we can add the public key URL to the yubikey itself
Add GPG key to backup yubikey(s)
Insert drduh isolated live USB.
Importing to keys to the yubikey will delete the PGP keys and only leave a stub so we need to redo the process in combination with our backup.
It is now time to repeat some steps.
# Insert USB and decrypt and mount the encrypted LUKS volume(s).
lsblk # sudo dmesg | tail
sudo cryptsetup luksOpen /dev/sdb1 gnupg-secrets
sudo mkdir -p /mnt/encrypted-storage
sudo mount /dev/mapper/gnupg-secrets /mnt/encrypted-storage
# create tmp dir and copy from backup usb to tmp dir
export GNUPGHOME=$(mktemp -d -t gnupg-$(date +%Y-%m-%d)-XXXX)
cd $GNUPGHOME
cp -avi /mnt/encrypted-storage/gnupg/* $GNUPGHOME
# Unmount and close the encrypted volume
sudo umount /mnt/encrypted-storage
sudo cryptsetup luksClose gnupg-secrets
# remove backup usb
# Verify
gpg --list-secret-keys
# Assign needed variables again
export IDENTITY="dohjon <[email protected]>"
export CERTIFY_PASS="<REDACTED>"
export ADMIN_PIN="<REDACTED>"
export KEYID=$(gpg -k --with-colons "$IDENTITY" | awk -F: '/^pub:/ { print $5; exit }')
export KEYFP=$(gpg -k --with-colons "$IDENTITY" | awk -F: '/^fpr:/ { print $10; exit }')
echo $KEYID $KEYFP
# Insert backup yubikey and transfer subkeys
gpg --list-secret-keys
# transfer Signature key
gpg --edit-key $KEYID
key 1 # Usage: S
keytocard
1
# follow prompt enter CERTIFY_PASS and ADMIN_PIN
save
# transfer Authentication key
gpg --edit-key $KEYID
key 2 # Usage: A
keytocard
3
# follow prompt enter CERTIFY_PASS and ADMIN_PIN
save
# transfer Encryption key
gpg --edit-key $KEYID
key 3 # Usage: E
keytocard
2
# follow prompt enter CERTIFY_PASS and ADMIN_PIN
save
# Verify
gpg --list-secret-keys
# The `>` after a tag indicates the key is stored on a smart card.
$ sec rsa4096/0xF0F2CFEB04341FB5 2024-01-01 [C]
$ Key fingerprint = 4E2C 1FA3 372C BA96 A06A C34A F0F2 CFEB 0434 1FB5
$ uid [ultimate] YubiKey User <yubikey@example>
$ ssb> rsa4096/0xB3CD10E502E19637 2024-01-01 [S] [expires: 2026-05-01]
$ ssb> rsa4096/0x30CBE8C4B085B9F7 2024-01-01 [E] [expires: 2026-05-01]
$ ssb> rsa4096/0xAD9E24E1B8CB9600 2024-01-01 [A] [expires: 2026-05-01]
Done, remove yubikey and reboot
Renewing subkeys by updating expiration
To update the expiration of the subkeys we need the offline certify key.
Insert USB and decrypt and mount the encrypted LUKS volume(s)
lsblk # sudo dmesg | tail
sudo cryptsetup luksOpen /dev/sdb1 gnupg-secrets
sudo mkdir -p /mnt/encrypted-storage
sudo mount /dev/mapper/gnupg-secrets /mnt/encrypted-storage
# create tmp dir and copy from backup usb to tmp dir
export GNUPGHOME=$(mktemp -d -t gnupg-$(date +%Y-%m-%d)-XXXX)
cd $GNUPGHOME
cp -avi /mnt/encrypted-storage/gnupg/* $GNUPGHOME
# Confirm the identify is available
gpg --list-secret-keys
# export required values
export IDENTITY="dohjon <[email protected]>"
export EXPIRATION=2y
export CERTIFY_PASS=<REDACTED>
export KEYID=$(gpg -k --with-colons "$IDENTITY" | awk -F: '/^pub:/ { print $5; exit }')
export KEYFP=$(gpg -k --with-colons "$IDENTITY" | awk -F: '/^fpr:/ { print $10; exit }')
# renew the subkeys
echo "$CERTIFY_PASS" | gpg --batch --pinentry-mode=loopback \
--passphrase-fd 0 --quick-set-expire "$KEYFP" "$EXPIRATION" \
$(gpg -K --with-colons | awk -F: '/^fpr:/ { print $10 }' | tail -n "+2" | tr "\n" " ")
# export the updated public key to backup usb
gpg --output /mnt/encrypted-storage/gnupg/$KEYID-$(date +%F).asc --armor --export $KEYID
# (optional) remove the previous public key
# Unmount and close the encrypted volume
sudo umount /mnt/encrypted-storage
sudo cryptsetup luksClose gnupg-secrets
# Insert the second backup USB and copy the updated public key to it as well
lsblk # sudo dmesg | tail
sudo cryptsetup luksOpen /dev/sdb1 gnupg-secrets
sudo mount /dev/mapper/gnupg-secrets /mnt/encrypted-storage
# export the updated public key to backup usb
gpg --output /mnt/encrypted-storage/gnupg/$KEYID-$(date +%F).asc --armor --export $KEYID
# (optional) remove the previous public key
# Unmount and close the encrypted volume
sudo umount /mnt/encrypted-storage
sudo cryptsetup luksClose gnupg-secrets
Insert
Extende subkeys expiration
# Create new empty tmp folder and point GNUPGHOME to it
export GNUPGHOME=$(mktemp -d -t $(date +%Y.%m.%d)-XXXX)
# Insert USB backup device 1 and decrypt then mount
lsblk # sudo dmesg | tail
sudo cryptsetup luksOpen /dev/sdb1 gnupg-secrets
sudo mkdir -p /mnt/encrypted-storage
sudo mount /dev/mapper/gnupg-secrets /mnt/encrypted-storage
# Copy everything from USB to tmp folder
cp -avi /mnt/encrypted-storage/gnupg/* $GNUPGHOME/
cd $GNUPGHOME
# Insert yubikey
gpg --card-status
gpg --fingerprint
# Export required variables to avoid typing and for use in scripting
export IDENTITY="doh jon <[email protected]>"
export EXPIRATION="2026-07-29"
export CERTIFY_PASS="<REDACTED>"
export KEYID=$(gpg -k -with-colons "$IDENTITY" | awk -F: '/^pub:/ { print $5; exit }')
export KEYFP=$(gpg -k -with-colons "$IDENTITY" | awk -F: '/^fpr:/ { print $10; exit }')
# Update expiration date on subkeys
echo "$CERTIFY_PASS" | gpg --batch --pinentry-mode=loopback \
--passphrase-fd 0 --quick-set-expire "$KEYFP" "$EXPIRATION" \
$(gpg -K --with-colons | awk -F: '/^fpr:/ { print $10 }' | tail -n "+2" | tr "\n" " ")
# Export the updated public key
gpg --armor --export $KEYID | sudo tee /mnt/encrypted-storage/gnupg/$KEYID-$(date +%F).asc
# Unmount and close the encrypted volume then remove USB backup device 1
sudo umount /mnt/encrypted-storage
sudo cryptsetup luksClose gnupg-secrets
# Insert USB backup device 2 and decrypt and mount
lsblk # sudo dmesg | tail
sudo cryptsetup luksOpen /dev/sdb1 gnupg-secrets
sudo mount /dev/mapper/gnupg-secrets /mnt/encrypted-storage
# Export the updated public key
gpg --armor --export $KEYID | sudo tee /mnt/encrypted-storage/gnupg/$KEYID-$(date +%F).asc
# Unmount and close the encrypted volume then remove USB backup device 2
sudo umount /mnt/encrypted-storage
sudo cryptsetup luksClose gnupg-secrets
Upload to keyserver
I use keys.openpgp.org as keyserver usage
To configure GnuPG to use keys.openpgp.org as keyserver, add this line to your .gnupg/gpg.conf
file:
keyserver hkps://keys.openpgp.org
Uploading your key
You can try this shortcut for uploading your key, which outputs a direct link to the verification page:
lsblk # sudo dmesg | tail
sudo cryptsetup luksOpen /dev/sdb1 gnupg-secrets
sudo mkdir -p /mnt/encrypted-storage
sudo mount /dev/mapper/gnupg-secrets /mnt/encrypted-storage
cat /mnt/encrypted-storage/gnupg/<public-key> | curl -T - https://keys.openpgp.org
Alternatively, visit https://keys.openpgp.org/upload and upload public key manually
lsblk # sudo dmesg | tail
sudo cryptsetup luksOpen /dev/sdb1 gnupg-secrets
sudo mkdir -p /mnt/encrypted-storage
sudo mount /dev/mapper/gnupg-secrets /mnt/encrypted-storage
cat /mnt/encrypted-storage/gnupg/<public-key>
Retrieving keys
To locate the key of a user, by email address:
gpg --auto-key-locate keyserver --locate-keys [email protected]
To refresh all your keys (e.g. new revocation certificates and subkeys):
gpg --refresh-keys