GPG, Yubikey, and Pass
Recently I've wanted to move back to using pass as a password manager. In the past, I used pass
with my GPG key on a yubikey and it worked out well. I do still have the key I used, but I can't remember how I set pass/GPG up to use the yubikey. So, I thought I'd do it from scratch, write about it, and maybe learn something.
Initial setup
The gpg
and scdaemon
packages need to be installed. Once the yubikey is inserted it should be used immediately by GPG. GPG runs a daemon called gpg-agent
which talks through scdaemon
to communicate with the yubikey.
Generate a key
To start, we need to generate some keys! Run gpg --expert --full-gen-key
and choose 1
, "RSA and RSA", as the key type. The other options should be self-explanatory.
❯ gpg --expert --full-gen-key
gpg (GnuPG) 2.2.20; Copyright (C) 2020 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.
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? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want for the subkey? (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) 2y
Key expires at Mon 29 Apr 2024 17:17:40 IST
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: Stephen OBrien
Email address: wayofthepie@protonmail.com
Comment:
You selected this USER-ID:
"Stephen OBrien <wayofthepie@protonmail.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.
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: key 01ED8E917ECF4036 marked as ultimately trusted
gpg: revocation certificate stored as '/home/chaospie/.gnupg/openpgp-revocs.d/985DB4AF50F89C5B9ED41803817494BAD88093CB.rev'
public and secret key created and signed.
pub rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
985DB4AF50F89C5B9ED41803817494BAD88093CB
uid Stephen OBrien <wayofthepie@protonmail.com>
sub rsa4096 2022-04-30 [E] [expires: 2024-04-29]
You can run gpg --list-secret-keys
to get a list of the secret keys generated:
❯ gpg --list-secret-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
sec rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
985DB4AF50F89C5B9ED41803817494BAD88093CB
uid [ultimate] Stephen OBrien <wayofthepie@protonmail.com>
ssb rsa4096 2022-04-30 [E] [expires: 2024-04-29]
To see the public keys:
❯ gpg --list-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
pub rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
985DB4AF50F89C5B9ED41803817494BAD88093CB
uid [ultimate] Stephen OBrien <wayofthepie@protonmail.com>
sub rsa4096 2022-04-30 [E] [expires: 2024-04-29]
GPG Terminology
Another thing I always forget when using GPG after some time is what all the jargon means!
sec rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
985DB4AF50F89C5B9ED41803817494BAD88093CB
uid [ultimate] Stephen OBrien <wayofthepie@protonmail.com>
ssb rsa4096 2022-04-30 [E] [expires: 2024-04-29]
Let's start from the first line sec rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
.
sec
means this is a secret key.rsa4096
is the algorithm and key size.[SC]
is the capability of the key, in this caseSC
.S
isSign
,C
isCertify
,A
isAuthenticate
, andE
isEncrypt
. You can create subkeys (more on this later) that have specific capabilities.[expires: 2024-04-29]
this just says when the key expires
The second line, 985DB4AF50F89C5B9ED41803817494BAD88093CB
, is just the key id. The last line, ssb rsa4096 2022-04-30 [E] [expires: 2024-04-29]
:
ssb
says this key is a subkey. The key denotedsec
is the primary key, and allssb
keys are subkeys of this.- The rest is similar to the first line.
Move keys to the yubikey
Moving the keys to the yubikey is pretty simple. First, you should back up the secret key before you move it to the yubikey, you won't have access to this after moving. I have a second yubikey that I import all keys to, and I also move the secret key to a USB stick with a LUKS volume. Not the best practice security-wise, but it's a USB stick I never use. You can also print them so they never live anywhere except on paper. To export the secret key:
gpg --export-secret-key --armor $SECRET_KEY_ID > secret.key
secret.key
will contain the key. Store this somewhere safe! Time to move the keys to the yubikey. Insert it, and run:
❯ gpg --card-status
Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240103040006120418440000
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
...
If it displays the card info, GPG can see it. To import we need the id of the secret key, you can get this by running:
❯ gpg --list-secret-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
sec rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
985DB4AF50F89C5B9ED41803817494BAD88093CB
uid [ultimate] Stephen OBrien <wayofthepie@protonmail.com>
ssb rsa4096 2022-04-30 [E] [expires: 2024-04-29]
The id in this case is 985DB4AF50F89C5B9ED41803817494BAD88093CB
. To move the keys to the yubikey run gpg --edit-key $SECRET_KEY_ID
:
❯ gpg --edit-key 985DB4AF50F89C5B9ED41803817494BAD88093CB
gpg (GnuPG) 2.2.20; Copyright (C) 2020 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.
Secret key is available.
sec rsa4096/817494BAD88093CB
created: 2022-04-30 expires: 2024-04-29 usage: SC
trust: unknown validity: unknown
ssb rsa4096/5DCD3D0ABD15FA6F
created: 2022-04-30 expires: 2024-04-29 usage: E
[ unknown] (1). Stephen OBrien <wayofthepie@protonmail.com>
gpg>
In the prompt type keytocard
- remember, moving the key to the card means you can never read this key directly again, so make sure you back it up!
gpg> keytocard
Really move the primary key? (y/N) y
Please select where to store the key:
(1) Signature key
(3) Authentication key
Your selection? 1
sec rsa4096/817494BAD88093CB
created: 2022-04-30 expires: 2024-04-29 usage: SC
trust: unknown validity: unknown
ssb rsa4096/5DCD3D0ABD15FA6F
created: 2022-04-30 expires: 2024-04-29 usage: E
ssb rsa4096/F35B9D17C107C8AC
created: 2022-04-30 expires: 2024-04-29 usage: A
[ unknown] (1). Stephen OBrien <wayofthepie@protonmail.com>
We select the Signature Key
slot in the yubikey as the place to store this primary key. It should prompt for the keys passphrase. Once done, select the subkey with key 1
:
gpg> key 1
sec rsa4096/817494BAD88093CB
created: 2022-04-30 expires: 2024-04-29 usage: SC
trust: unknown validity: unknown
ssb* rsa4096/5DCD3D0ABD15FA6F
created: 2022-04-30 expires: 2024-04-29 usage: E
[ unknown] (1). Stephen OBrien <wayofthepie@protonmail.com>
Note the asterisk (*
) after ssb
, this means this subkey is selected.
gpg> keytocard
Please select where to store the key:
(2) Encryption key
Your selection? 2
sec rsa4096/817494BAD88093CB
created: 2022-04-30 expires: 2024-04-29 usage: SC
trust: unknown validity: unknown
ssb* rsa4096/5DCD3D0ABD15FA6F
created: 2022-04-30 expires: 2024-04-29 usage: E
[ unknown] (1). Stephen OBrien <wayofthepie@protonmail.com>
This time we select the Encryption Key
slot in the yubikey. Once complete, type quit
and make sure to say y
when prompted to save.
gpg> q
Save changes? (y/N) y
Now, when we list the secret keys, both the primary key and the subkey should be postfixed with a >
, this denotes that GPG only has a reference to the keys, they are not stored directly in its keyring.
❯ gpg --list-secret-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
sec> rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
985DB4AF50F89C5B9ED41803817494BAD88093CB
Card serial no. = 0006 12041844
uid [ unknown] Stephen OBrien <wayofthepie@protonmail.com>
ssb> rsa4096 2022-04-30 [E] [expires: 2024-04-29]
Export and store the public key
It's important to export and store the public key somewhere publicly accessible. To export:
gpg --output public.key --armor --export $SECRET_KEY_ID
public.key
will contain the key. Once you've stored the public key somewhere, edit the card again with gpg --edit-card
. This time enable admin commands with the admin
command, and then enter the URL to access the public key with url
.
❯ gpg --edit-card
Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240103040006120418440000
Application type .: OpenPGP
...
gpg/card> admin
Admin commands are allowed
gpg/card> url
URL to retrieve public key: https://raw.githubusercontent.com/wayofthepie/public-keys/main/proton.key
I stored mine in a github repo, you can also upload it to a keyserver and reference from there.
Set up pass
Install pass, with sudo apt install pass
or however else you want to install it. Initialize the password store with the email you used to set up your key (you can also use the full key id instead of the email):
❯ pass init wayofthepie@protonmail.com
mkdir: created directory '/home/chaospie/.password-store/'
Password store initialized for wayofthepie@protonmail.com
Enable git support:
❯ pass git init
Initialized empty Git repository in /home/chaospie/.password-store/.git/
[main (root-commit) ac03bd2] Add current contents of password store.
1 file changed, 1 insertion(+)
create mode 100644 .gpg-id
[main 894baaa] Configure git repository for gpg file diff.
1 file changed, 1 insertion(+)
create mode 100644 .gitattributes
Configure the gpg-agent
to open any prompts on the terminal by editing/creating ~/.gnupg/gpg-agent.conf
and adding:
pinentry-program /usr/bin/pinentry-curses
Either pinentry-curses
or pinentry-tty
can be used here, if neither is installed you should be able to install it directly through the package manager. Kill the agent to force it to reload its config:
❯ gpgconf --kill gpg-agent
It will restart automatically. Let's do a quick test, insert a key test
with the value test
:
❯ pass insert test -e
Enter password for test: test
The -e
flag just means echo back the value I type, without it the value is hidden. Now, test retrieval, run pass test
and it should prompt you for your PIN:
┌──────────────────────────────────────────────┐
│ Please unlock the card │
│ │
│ Number: 0006 33333333 │
│ Holder: │
│ │
│ PIN ________________________________________ │
│ │
│ <OK> <Cancel> │
└──────────────────────────────────────────────┘
The default PIN is 123456, you should change that if it's still that value. Once entered, pass should print the value of test
. This means it's working!
We need to test that the key is only accessible on the yubikey also. Pull out the yubikey and try to retrieve the value for test
again, it should show a prompt like the following:
┌────────────────────────────────────────────┐
│ Please insert the card with serial number: │
│ │
│ 0006 33333333 │
│ │
│ <OK> <Cancel> │
└────────────────────────────────────────────┘
Now pop it back in and run pass test
, it should ask for the PIN again.
Set up on another computer
This is the bit that always drives me mad when I haven't used GPG for a while. Initially, on a new computer, you'll have no public or secret keys:
❯ gpg --list-keys
gpg: checking the trustdb
gpg: no ultimately trusted keys found
❯ gpg --list-secret-keys
If you try to use pass
to get a secret in the store, you will get the following error:
❯ pass test
gpg: decryption failed: No secret key
Let's fix it!
Fetch the public key
To get a reference to the keys on the yubikey, plug it in and run:
❯ gpg --card-edit
Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240103040006120418440000
Application type .: OpenPGP
...
gpg/card>
In the prompt type fetch
:
gpg/card> fetch
gpg: requesting key from 'https://raw.githubusercontent.com/wayofthepie/public-keys/main/proton.key'
gpg: key 01ED8E917ECF4036: public key "Stephen OBrien <wayofthepie@protonmail.com>" imported
gpg: Total number processed: 1
gpg: imported: 1
That's it! Now, GPG should have a reference to the keys, and pass test
should prompt you for your pin and read the value of our test secret test
correctly.
❯ gpg --list-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
pub rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
985DB4AF50F89C5B9ED41803817494BAD88093CB
uid [ unknown] Stephen OBrien <wayofthepie@protonmail.com>
sub rsa4096 2022-04-30 [E] [expires: 2024-04-29]
❯ gpg --list-secret-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
sec> rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
985DB4AF50F89C5B9ED41803817494BAD88093CB
Card serial no. = 0006 12041844
uid [ unknown] Stephen OBrien <wayofthepie@protonmail.com>
ssb> rsa4096 2022-04-30 [E] [expires: 2024-04-29]
❯ pass test
test
Phew...
This is the third time I have had to go re-learn how GPG works. It's a bit of a nightmare usability-wise. Hopefully, this serves as a good reference for future me at the very least!