Initial version

This commit is contained in:
Olaf
2026-03-10 17:30:49 +01:00
commit 7d78356617
2 changed files with 301 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=rCLz
-----END PGP PUBLIC KEY BLOCK-----

215
README.md Normal file
View File

@@ -0,0 +1,215 @@
# GPG Keys with Multiple Yubikey
- - -
_"One yubikey is no yubikey"_ (an old proverb).
- - -
## TL;DR:
I use this key as of March 2026. My key Key ID/Fingerprint: 74FEDBB2 or [01B157D574FEDBB2](./01B157D574FEDBB2-2026-03-10.asc) or 645FA10143EFBE17108030F801B157D574FEDBB2
## Who is this for
Mostly myself, so I remember what I did.
## What are we trying to solve.
I recently bought 3 yubikeys, mainly to use with passphrases. But I also decided to store my "GPG key" on them. The problem I am trying to solve is that I want to be able to use 3 yubikeys for redundancy. One to always keep at home.
Two that I take with me for travel so that, if I loose one I can always continue doing business on the trip. I also want to make sure that when I loose a key, I do not have to revoke my master Identity.
## GPG and subkeys
When you generate a PGP key it will by default create an a 'Signing and Certification' [SC] and a 'Encryption' [E].
Idealy one would want to generate multiple Encryption subkeys, one for each yubikey. But, that will lead to drama because if Alice wants to send you something encrypted she has to know wich encryption subkey you have available at the time. So we equip all our yubikeys with the same encryption key. Which makes that if you loose one, you still have to revoke the whole lot.
## The workflow
### generate keys
We follow the recipee [here](https://github.com/drduh/YubiKey-Guide?tab=readme-ov-file) that creates a certiv
Generate a GPG key. We do this not on the yubikey but on a machine. We want to be able to backup the generated key.
```
export KEY_TYPE=Ed25519 # while we are at it we use a modern curve.
export IDENTITY="Olaf M. Kolkman <olaf@xolx.nl>"
export CERTIFY_PASS="<redacted-certfication-password>"
export EXPIRATION=9y # I have my reasons
```
Generate the Certificate:
```
echo "$CERTIFY_PASS" | \
gpg --batch --passphrase-fd 0 \
--quick-generate-key "$IDENTITY" "$KEY_TYPE" cert never
```
Make note of the stored revocation certificate. follow the recipee to set and view the fingerprints and keyid
```
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 }')
printf "\nKey ID/Fingerprint: %20s\n%s\n\n" "$KEYID" "$KEYFP"
Key ID/Fingerprint: 01B157D574FEDBB2
645FA10143EFBE17108030F801B157D574FEDBB2
```
Now first eddit the key to add additional Identifiers and create an encryption ey
```
gpg --expert --edit-key 01B157D574FEDBB2
...
gpg> adduid
...
gpg> sign
gpg> addkey
<select ECC encrypt only (12)>
```
And generate 3 sets of signing and authentication subkeys
```
for i in {0..2}; do
echo creating signing key $i:
echo "$CERTIFY_PASS" | \
gpg --batch --pinentry-mode=loopback --passphrase-fd 0 \
--quick-add-key "$KEYFP" "${KEYTYPE}" sign "$EXPIRATION"
echo creating authentication key $i:
echo "$CERTIFY_PASS" | \
gpg --batch --pinentry-mode=loopback --passphrase-fd 0 \
--quick-add-key "$KEYFP" "${KEYTYPE}" auth "$EXPIRATION"
done
```
Now check what we generated
```
> gpg -k ${KEYFP}
pub ed25519 2026-03-10 [C]
645FA10143EFBE17108030F801B157D574FEDBB2
uid [ultimate] Olaf M. Kolkman <kolkman@isoc.org>
uid [ultimate] Olaf M. Kolkman <olaf@xolx.nl>
uid [ultimate] Olaf M. Kolkman <olaf@dacht.net>
sub cv25519 2026-03-10 [E] [expires: 2035-03-08]
sub ed25519 2026-03-10 [S] [expires: 2035-03-08]
sub ed25519 2026-03-10 [A] [expires: 2035-03-08]
sub ed25519 2026-03-10 [S] [expires: 2035-03-08]
sub ed25519 2026-03-10 [A] [expires: 2035-03-08]
sub ed25519 2026-03-10 [S] [expires: 2035-03-08]
sub ed25519 2026-03-10 [A] [expires: 2035-03-08]
```
Backup the key:
```
gpg --export-secret-key --armor 645FA10143EFBE17108030F801B157D574FEDBB2
```
store output safely.
### Transfer to the yubikey
You will need the `ykman` cli tool for this.
First make sure your yubikey is set up. For each of your yubikey do the following. (Note on macOSX if the tool doesn't respond to return, use ctr-j as return char)
```
ykman openpgp reset
...
ykman openpgp access change-admin-pin
...
ykman openpgp access change-pin
...
ykman openpgp access set-retries 5 5 5
```
After preparing the yubikeys copy the encryption key [E] to all yubikeys and one unique signing [S] and Authorization keys [A] to each yubikey. So we do now follow the yubikey recipee to the letter, since we do not export the primary keys signature key.
Carfully select the appropriate key by *toggling* `key <keyno>`. And choose the appropriate slots.
Validate with `ykman openpgp info` if you transfered the correct keys.
The resulting keys have been transfered like so:
```
Yubikey type SUBKey IO
--------------------------------------------------------
35775013 S ECCE B28A 6E5F EE99 D17A 8663 A20F 6458 99FA 4923
35775013 E 85B2 FBDA E6DC E92A 0676 07D7 9C82 2115 2E48 E8BE
35775013 A 3FC6 049C FC4C A286 C919 86B1 1B7F 003F 881B 9F3B
36431751 S E540 031A 32A2 B74A 45D0 7AFC FA69 0576 F726 C4C7
36431751 E 85B2 FBDA E6DC E92A 0676 07D7 9C82 2115 2E48 E8BE
36431751 A D464 A0DE 003C 561B 6677 6665 085A 93EB 5D85 EE54
36431811 S 80E1 9313 13F8 734F 10A4 92B7 D426 4719 8122 395F
36431811 E 85B2 FBDA E6DC E92A 0676 07D7 9C82 2115 2E48 E8BE
36431811 A B395 CE5B C4C3 759D C859 B68A EDFF 356F B975 5D1A
--------------------------------------------------------
```
### Transfer from the Yubikey
Now comes the scary bit: Delete the secret key. And immediatly reimport it from the yubikey:
```
gpg --delete-secret-keys ${KEYID}
gpg --card-edit fetch
```
That results in a table where one can see which keys are now available.
```
sub ed25519/A20F645899FA4923 2026-03-10 Olaf M. Kolkman <kolkman@isoc.org>
sec# ed25519/01B157D574FEDBB2 created: 2026-03-10 expires: never
ssb> cv25519/9C8221152E48E8BE created: 2026-03-10 expires: 2035-03-08
card-no: 0006 35775013
ssb# ed25519/D42647198122395F created: 2026-03-10 expires: 2035-03-08
ssb# ed25519/EDFF356FB9755D1A created: 2026-03-10 expires: 2035-03-08
ssb> ed25519/A20F645899FA4923 created: 2026-03-10 expires: 2035-03-08
card-no: 0006 35775013
ssb> ed25519/1B7F003F881B9F3B created: 2026-03-10 expires: 2035-03-08
card-no: 0006 35775013
ssb# ed25519/FA690576F726C4C7 created: 2026-03-10 expires: 2035-03-08
ssb# ed25519/085A93EB5D85EE54 created: 2026-03-10 expires: 2035-03-08
```
Any time you change the yubikey you use you have to delete (`gpg --delete-key`) and refetch the key (`gpg --card-edit fetch`).