Setting up GPG with YubiKey/Smart Cards
Before We Start
Check Your Device
If you’re using a real Smart Card, ignore this part.
If you’re using a YubiKey or a compatible security key product, check with the manufacture to make sure it has OpenPGP Smart Card feature/Applet enabled. Some products have PIV and not OpenPGP, which could not be used to store your GPG key.
After making sure you have the right product, know the key size your key supports. YubiKey 4 or 5 supports up to 4096 bits, and YubiKey NEO only supports 2048 bits.
Backup Before You Proceed
Before reading this, you should already know that using the command line without knowing what you’re doing could be dangerous. Make sure you understand clearly which line does what and do not proceed if otherwise.
Just to make sure you don’t make any unwanted mistakes(because we all make mistakes), before you proceed, create a
backup of the ~/.gnupg
directory.
Use a Live USB Installation
Since we’ll be deleting the master key after moving it to your card, it’s important to wipe the device afterwards. Deleting your files from a drive doesn’t mean it’s really gone. In most cases data recovery is still possible. The best option would be to store it on an usb drive and erase it completely afterwards. Even shred it into pieces if you wish. But why go through all those hassle when you can just use an in-memory/Live USB installation? If the key only resides in-memory, after a single power cycle, it will be completely gone, without a trace.
Generate the Master Key
We’ll generate a 4096 bit RSA key with only the c
ertify capability enabled. Don’t worry about the capability part. It
could be changed later. If your smart card only supports up to 2048 bits, not a big deal, use that instead. Execute the
following command:
gpg --expert --full-generate-key
gpg (GnuPG) 2.2.29; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
gpg: directory '/home/skyfalls/.gnupg' created
gpg: keybox '/home/skyfalls/.gnupg/pubring.kbx' created
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(7) DSA (set your own capabilities)
(8) RSA (set your own capabilities)
(9) ECC and ECC
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(13) Existing key
(14) Existing key from card
Your selection? 8
Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Sign Certify Encrypt
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? s
Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Certify Encrypt
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? e
Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Certify
(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) 0
Key does not expire at all
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: TestUser
Email address: test@example.com
Comment:
You selected this USER-ID:
"TestUser <test@example.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /home/skyfalls/.gnupg/trustdb.gpg: trustdb created
gpg: key 02FCFF106CA8320A marked as ultimately trusted
gpg: directory '/home/skyfalls/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/skyfalls/.gnupg/openpgp-revocs.d/D67443B6CEED99FAC2BC0C8702FCFF106CA8320A.rev'
public and secret key created and signed.
pub rsa4096 2021-10-16 [C]
D67443B6CEED99FAC2BC0C8702FCFF106CA8320A
uid TestUser <test@example.com>
The information here is public, so if you want to stay anonymous, use a fake name instead. As for email, always use a valid one.
Test the Key
Replace TestUser
with your name or email.
gpg --list-key --keyid-format long
pub rsa4096/02FCFF106CA8320A 2021-10-16 [C]
D67443B6CEED99FAC2BC0C8702FCFF106CA8320A
uid [ultimate] TestUser <test@example.com>
[C]
: certify, ability to create subkeys
02FCFF106CA8320A
: short id(same as the last 16 characters of the long id)
D67443B6CEED99FAC2BC0C8702FCFF106CA8320A
: long id, also called the key grip
Create a Revocation Certificate
The revocation certificate exists so that you can revoke your master key if it ever gets lost. You can always regenerate a new one when you still have the secret key available(both on disk or smart card).
The output of --full-generate-key
already told you that, a revocation certificate has already been created for you
along with the master key. You could use that, or you could generate a new one if you need to. Keep in mind that leaking
your revocation certificate could lead to a key revocation attack, so treat it as if it’s your private key.
gpg --output revoke-master.pgp --gen-revoke TestUser
sec rsa4096/02FCFF106CA8320A 2021-10-16 TestUser <test@example.com>
Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
0 = No reason specified
1 = Key has been compromised
2 = Key is superseded
3 = Key is no longer used
Q = Cancel
(Probably you want to select 1 here)
Your decision? 0
Enter an optional description; end it with an empty line:
>
Reason for revocation: No reason specified
(No description given)
Is this okay? (y/N) y
ASCII armored output forced.
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: This is a revocation certificate
iQI2BCABCAAgFiEE1nRDts7tmfrCvAyHAvz/EGyoMgoFAmFqZUQCHQAACgkQAvz/
...
-----END PGP PUBLIC KEY BLOCK-----
Revocation certificate created.
Please move it to a medium which you can hide away; if Mallory gets
access to this certificate he can use it to make your key unusable.
It is smart to print this certificate and store it away, just in case
your media become unreadable. But have some caution: The print system of
your machine might store the data and make it available to others!
Keep the revocation certificate to somewhere safe, presumably offline.
Back up the Master Key
To File
gpg --armor --output public.asc --export TestUser
gpg --armor --output private.asc --export-secret-key
The output keys should look like this:
-----BEGIN PGP PUBLIC/PRIVATE KEY BLOCK-----
mQINBGFqTbYBEADeSPqdWGBJOWzaq+TGG18JU6fw38Q1WyWfbh45RjUh/feorMTi
...
-----END PGP PUBLIC/PRIVATE KEY BLOCK-----
You should avoid storing your master key’s secret on your HDD/SSD, instead use a throwaway thumb drive.
To QR Code With PaperKey
This requires the paperkey package. In my opinion, it’s the best way to store a key long-term wise. It’s safe, cheap, and it lasts amazingly long under proper storage conditions.
gpg --export-secret-key TestUser | paperkey --output-type raw | qrencode --8bit --output secret-qr.png
The QR code generated with default settings are already quite large, mine is 387×387 pixels, and this is with the
correction level set to L
, which only allows about 7% of lost data. Increasing it to Q
(second highest) produces an image that’s 519×519 pixels,
borders included. With an 4096 bits key, the H
(highest) option would throw an Input data too large
error.
gpg --export-secret-key TestUser | paperkey --output-type raw | qrencode --8bit --level H --output secret-qr.png
Paperkey will require you to have your public key ready when decoding your private key. It’s important to keep that safe as well. Using a key server is a good option. I also recommend you to print out the public key grip along with your private key qr code for easy lookups.
Uploading Public Keys to a Keyserver
Upload your public key to any keyserver you want. Replace the domain with the one you’re using.
gpg --keyserver pgp.mit.edu --send-key 02FCFF106CA8320A
Most keyservers have a web page where you can submit your public key. You could also use that instead. Note: This only backs up the public key, so you still have to keep your private key safe. Most keyservers are also publicly accessible and searchable, and other people can view your email and name.
Moving the Key to Card/Yubikey
gpg --edit-key TestUser
You shouldn’t have the warning if you’re starting from scratch or having already performed a factory reset.
gpg> keytocard
Really move the primary key? (y/N) y
Please select where to store the key:
(1) Signature key
Your selection? 1
gpg: WARNING: such a key has already been stored on the card!
Replace existing key? (y/N) y
sec rsa4096/02FCFF106CA8320A
created: 2021-10-16 expires: never usage: C
trust: ultimate validity: ultimate
[ultimate] (1). TestUser <test@example.com>
gpg> q
Save changes? (y/N) y
After saving your keys in a safe place, you can safely move on to a less secure environment, such as your main working pc. Currently, your private key is still present on your local keyring. It’s important to remove it. Using a Live USB installation? Now it’s the time to perform a reboot/shutdown.
After this, you always need to have your card/Yubikey connected to be able to use gpg functionalities that requires the private key.
gpg --delete-secret-key D67443B6CEED99FAC2BC0C8702FCFF106CA8320A
gpg (GnuPG) 2.2.29; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
sec rsa4096/02FCFF106CA8320A 2021-10-16 TestUser <test@example.com>
Delete this key from the keyring? (y/N) y
This is a secret key! - really delete? (y/N) y
Optionally, verify that the secret key is present on the card:
gpg --card-edit
...
General key info..:
pub rsa4096/02FCFF106CA8320A 2021-10-16 TestUser <test@example.com>
sec> rsa4096/02FCFF106CA8320A created: 2021-10-16 expires: never
card-no: **** ********
And only on your card(gone from your local keyring):
gpg --edit-key TestUser
Secret key is available.
sec rsa4096/02FCFF106CA8320A
created: 2021-10-16 expires: never usage: C
card-no: **** ********
trust: ultimate validity: ultimate
[ultimate] (1). TestUser <test@example.com>
Card no. should be present, although sometimes gpg fails to update the key info. if that’s the case,
run gpg --card-status
, it should have your gpg key listed in “General key info”.
Setup For Your Working PC
Check the Disaster recovery section for guide for key recovery and import.
You’ll have to retrieve the public key to your working device before proceeding.
Trusting the Key
gpg --edit-key TestUser
gpg> trust
sec rsa4096/02FCFF106CA8320A
created: 2021-10-16 expires: never usage: C
card-no: **** ********
trust: ultimate validity: unknown
[ultimate] (1). TestUser <test@example.com>
Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)
1 = I don't know or won't say
2 = I do NOT trust
3 = I trust marginally
4 = I trust fully
5 = I trust ultimately
m = back to the main menu
Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y
Subkeys
Subkeys are the keys that you’ll use on a daily basis. It’s possible to generate and revoke individual keys, so that if one did get leaked, your master key is still valid and safe to use.
SSH Key
Expert mode is required.
gpg --expert --edit-key TestUser
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 a 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 a 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 a 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 a 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) 2048
Requested keysize is 2048 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) 1y
Key expires at Sun 16 Oct 2022 01:05:39 PM CST
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
sec rsa4096/02FCFF106CA8320A
created: 2021-10-16 expires: never usage: C
card-no: 0000 00000001
trust: ultimate validity: ultimate
ssb rsa2048/DAD85D6F66B57061
created: 2021-10-16 expires: 2022-10-16 usage: A
[ultimate] (1). TestUser <test@example.com>
gpg -K --with-keygrip
/home/skyfalls/.gnupg/pubring.kbx
---------------------------------
sec> rsa4096 2021-10-16 [C]
D67443B6CEED99FAC2BC0C8702FCFF106CA8320A
Keygrip = 0C33AA3FB96C0D223120D8A34BD19FFAA583AD7E
Card serial no. = 0000
uid [ultimate] TestUser <test@example.com>
ssb rsa2048 2021-10-16 [A] [expires: 2022-10-16]
Keygrip = CBE5A020311F844B3752DCA7A65E08B41DBCD563
Each key has a key grip, in this case, CBE5A020311F844B3752DCA7A65E08B41DBCD563
(40chars). You’ll need to write it
to ~/.gnupg/sshcontrol
, in a new line.
echo CBE5A020311F844B3752DCA7A65E08B41DBCD563 >> ~/.gnupg/sshcontrol
Then, add these lines to ~/.bashrc
, ~/.zshrc
, or whatever your shell is using.
...
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent
#gpg-connect-agent updatestartuptty /bye
Sometimes when performing a ssh connection, authentication will fail instantly because of “agent refused operation”.
Running ssh
in verbose mose(ssh -v
) and you’ll see a line saying “sign_and_send_pubkey: signing failed for RSA “cardno:xxx” from
agent: agent refused operation”. Running gpg-connect-agent updatestartuptty /bye
fixes that. Strange issue but easy
enough to fix.
Get your ssh public key:
ssh-add -L
Or install directly on the target server:
ssh-copy-id root@example.com
Git Sign-offs
Essentially the same. We’ll use a 4096bit RSA key with the sign capability.
The problem here is that each smart card can only store 3 keys with respectively sign, encrypt and authenticate capabilities. And the master key always uses the sign slot. If you want to use a dedicated subkey for signing, it wouldn’t fit on the smart card. You’ll have to change your master key’s capability for that. Or you can generate a subkey per device, and use that instead.
Sign-off Commits With Master Key
gpg> change-usage
Changing usage of the primary key.
Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Certify
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? s
Possible actions for a RSA key: Sign Certify Encrypt Authenticate
Current allowed actions: Sign Certify
(S) Toggle the sign capability
(E) Toggle the encrypt capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? q
Now you can use your master key for signing. IMO this works pretty well with git, on which your commit history is always preserved and there’s no need for key rotation. For other use cases you should think about it throughly before using your master key for anything else.
Continue by following the rest of Github’s documention .
Sign-off Commits With a Subkey
Simply generate a subkey with s
ign enabled.
After the key is ready to go:
# Get the key id
gpg --list-secret-keys --keyid-format=long
# Upload the output to github
gpg --armor --export 1C2159142FDFA9852C5247E028D20361D004716C
Continue by following the rest of Github’s documention .
Card Configuration
Key Retrieval URL
If you have your public key stored somewhere on the internet as a file, use this.
gpg --card-edit
gpg/card> admin
Admin commands are allowed
gpg/card> url
URL to retrieve public key: https://keys.openpgp.org/vks/v1/by-fingerprint/1C2159142FDFA9852C5247E028D20361D004716C
gpg/card> fetch
gpg: requesting key from 'https://keys.openpgp.org/vks/v1/by-fingerprint/1C2159142FDFA9852C5247E028D20361D004716C'
gpg: key 028D20361D004716C: "TestUser <test@example.com>" not changed
gpg: Total number processed: 1
gpg: unchanged: 1
Passwords
gpg --card-edit
gpg/card> admin
Admin commands are allowed
gpg/card> passwd
gpg: OpenPGP card no. xxx detected
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection? 3
PIN changed.
Pin Retries
ykman openpgp access set-retries <PIN-RETRIES> <RESET-CODE-RETRIES> <ADMIN-PIN-RETRIES>
ykman openpgp access set-retries 6 6 6
Read more on Yubico’s man page .
Enabling Touch
Require user touch when using the hardware key. You still need to type your pin once for the first request after powering on.
ykman openpgp keys set-touch <SIG|ENC|AUT|ATT> <cached|cached-fixed|fixed|off|on>
ykman openpgp keys set-touch sig on
WARNING: The use of this command is deprecated and will be removed!
Replace with: ykman openpgp keys set-touch sig on
Enter Admin PIN:
Set touch policy of signature key to on? [y/N]: y
If you encounter the WARNING: PC/SC not available error, run:
sudo systemctl enable pcscd.service
sudo systemctl start pcscd.service
Disaster Recovery
Retrieving the Public Key From Key Server
gpg --keyserver pgp.mit.edu --recv-key 02FCFF106CA8320A
If you do not know your key grip, use the search function of your keyserver’s website.
Recovering the Private Key From Paperkey
You’ll need your public key as a file. Dearmor the public key, then try to import with paperkey:
gpg --dearmor public.pgp
zbarimg -1 --raw -q -Sbinary secret-qr.png | paperkey --pubring public.pgp.gpg | gpg --import
Import Raw/armored Key Files
gpg --import private.asc
Using the Revocation Certificate
If your master key has been stolen by someone and you’re facing identity theft, you should revoke your key ASAP. Remember the revocation certificate we’ve generated before?
gpg --import revoke.pgp
gpg: key 02FCFF106CA8320A: "TestUser <test@example.com>" revocation certificate imported
gpg: Total number processed: 1
gpg: new key revocations: 1
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
Push updates to the keyserver(don’t forget to say goodbye!):
gpg --send-keys D67443B6CEED99FAC2BC0C8702FCFF106CA8320A
Revoking your master key will render all the subkeys unusable. Only consider this as a last resort, as you can always revoke individual subkeys.