How to generate a secure PGP key
Generate the key
The goal is to make a secure and modern PGP key.
The primary key should only be used to sign my Subkeys, it should expire in 10 years.
gpg --expert --full-generate-key
> (11) ECC (set your own capabilities)
> (S) Toggle the sign capability # Remove sign capability
> (Q) Finished
> (1) Curve 25519 *default*
> 10y
> Real name: Julie Windelborg Nielsen
> Email address: REDACTED
> (O)kay
gpg --expert --edit-key DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA
> addkey
> (10) ECC (sign only)
> (1) Curve 25519 *default*
> 5y
> addkey
> (12) ECC (encrypt only)
> (1) Curve 25519 *default*
> 5y
> addkey
> (11) ECC (set your own capabilities)
> (S) Toggle the sign capability # Remove sign
> (A) Toggle the authenticate capability # Add authenticate
> (Q) Finished
> (1) Curve 25519 *default*
> 5y
> save
> -----
> sec ed25519/72013DD9A8B991DA
> created: 2025-10-25 expires: 2035-10-23 usage: C
> trust: ultimate validity: ultimate
> ssb ed25519/0CF393A64DB64668
> created: 2025-10-25 expires: 2030-10-24 usage: S
> ssb cv25519/6D64DF9B6A10C7AF
> created: 2025-10-25 expires: 2030-10-24 usage: E
> ssb ed25519/5D68F7D4B2ACC5E2
> created: 2025-10-25 expires: 2030-10-24 usage: A
> [ultimate] (1). Julie Windelborg Nielsen <REDACTED>
PGP Public Key
gpg --export --armor DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEaP0uMBYJKwYBBAHaRw8BAQdAytKbR3tBPwwSfEPJP+XDEO+1S9tlfNju6NkQ
j6llPSW0Lkp1bGllIFdpbmRlbGJvcmcgTmllbHNlbiA8anVsaWVAd2luZGVsYm9y
Zy5kaz6ImQQTFgoAQRYhBN1Y3poW5felZXzFqHIBPdmouZHaBQJo/S4wAhsBBQkS
zAMABQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEHIBPdmouZHaPJoBAMkP
XZq16EBvcPC3F2n/aP05CuuIkbf3jr8mOBOmTS04AP9VomYaY9kSq4Nx9KBTIbKL
yYtsCtWzj9ZZsD+owwXMALgzBGj9L8sWCSsGAQQB2kcPAQEHQLM1kHx/qk4DKawM
c36KfO/NT96MbfmdWqNwhUqbL7LBiPUEGBYKACYWIQTdWN6aFuX3pWV8xahyAT3Z
qLmR2gUCaP0vywIbAgUJCWYBgACBCRByAT3ZqLmR2nYgBBkWCgAdFiEEq/ZJjfwc
S4nGlDHyDPOTpk22RmgFAmj9L8sACgkQDPOTpk22RmhpfQEA4yc1lwVsffx5NhaK
fYNcshFzB40P+cL3eLIGCH3OL+YBAPdte6DXYeReXwkvdKdOA3UQ2ndP76b0H6qH
zHOp5SQOQ68A/0r3XLXUDxwGboJHKM3EeiINfEAQg5ysU64dkSnWrbg1AP9/Dzg3
iaQoyiCnpZgrHO5kVnahoOP1lBU9+cW4ONZzALg4BGj9MWkSCisGAQQBl1UBBQEB
B0AmVkCZzGOfXUALwY8pTIMQKpaRKzV37VUjuUEbcA4rEAMBCAeIfgQYFgoAJhYh
BN1Y3poW5felZXzFqHIBPdmouZHaBQJo/TFpAhsMBQkJZgGAAAoJEHIBPdmouZHa
CzwBAL6Y+efZnuE3QSaaqnQqePF1jOza5AC/eVT6ImfCB0o8AP9JUsfow1jaM6Py
rUdwGQ57mFcIG1HQsxqckB27zEVsBbgzBGj9MYEWCSsGAQQB2kcPAQEHQPnpxKYk
Jw9TjLmtLsZuKaet2VuJjKVfsdDqdve9pdJGiH4EGBYKACYWIQTdWN6aFuX3pWV8
xahyAT3ZqLmR2gUCaP0xgQIbIAUJCWYBgAAKCRByAT3ZqLmR2qZHAQDGUgvYzmT7
I/pR0DWsMnHX3IapWRK+CzTtommgCEHC4gEAvngRHdnhJVAjr3UXl0X4C1KbdNQv
m4JCYnUROQmUCw4=
=fA3M
-----END PGP PUBLIC KEY BLOCK-----
SSH public key
gpg --export-ssh-key DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPnpxKYkJw9TjLmtLsZuKaet2VuJjKVfsdDqdve9pdJG openpgp:0xB2ACC5E2
Fingerprint
DD58 DE9A 16E5 F7A5 657C C5A8 7201 3DD9 A8B9 91DA
Setup the Yubikey
We need to set the PIN, admin pin and the reset code.
Lets get a new admin pin using Random.org
ykman openpgp access change-admin-pin
ykman openpgp access change-pin
ykman openpgp access change-reset-code
The default admin PIN is 12345678 and the default PIN is 123456.
Make a solid backup
gpg --output julie.pub.gpg --export DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA
gpg --output julie.private.gpg --export-secret-keys DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA
gpg --output julie.subkeys.private.gpg --export-secret-subkeys DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA
gpg --export-ownertrust > trust.gpg
I save all these backup in a hardened KeePassXC Vault.
Move keys to Yubikey
gpg --edit-key DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA
> key 1 # Select key 1 (usage: S)
> keytocard # Move key to card
> (1) Signature key # Use signature slot
> key 1 # Unselect key 1
> key 2 # Select key 2 (usage: E)
> keytocard
> (2) Encryption key
> key 2
> key 3 # Select key 3 (usage: A)
> keytocard
> (3) Authentication key
> key 3
> save
Save the key to the Secondary yubikey
So, this is probably not the right way to do it, but this is my solution?
gpg --delete-secret-keys DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA
gpg --import julie.private.gpg
Then following the setup steps as on the primary key and the moving steps of the primary key.
Delete the certifying key
To keep the keys very secure delete the certifying key, and use it only when you need to change your keys.
gpg --delete-secret-key DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA\!
Verify
sec# ed25519 2025-10-25 [C] [expires: 2035-10-23]
DD58DE9A16E5F7A5657CC5A872013DD9A8B991DA
uid [ultimate] Julie Windelborg Nielsen <REDACTED>
ssb> ed25519 2025-10-25 [S] [expires: 2030-10-24]
ssb> cv25519 2025-10-25 [E] [expires: 2030-10-24]
ssb> ed25519 2025-10-25 [A] [expires: 2030-10-24]
The # at sec#, means that only the public key is stored, the secret key we have stored away in our keepassxc The > on the subkeys ssb> means the secret parts of these keys are on a smartcard and not on the machine.