Back

GPG Key Refresh

Published:

GPG is a safe, reliable way to encrypt data. zx2c4's pass password manager uses GPG to encrypt password files.

I use pass a lot, which means that I use GPG keys a lot (or at least that's what happens in the background). When old keys expire, they need to be replaced with new ones. My pass repo also needs to have the keys rotated out. This is the process I use to do so.

First, we need to ensure we're using OpenPGP best practices. My dotfiles contain the most up-to-date configurations I use. As of the time of writing, here is my ~/.gnupg/gpg.conf. The configuration is primarily drawn from the riseup.net OpenPGP Best Practices.

Import the sks-keyservers CA certificate, otherwise the keyserver won't work:

curl --location https://sks-keyservers.net/sks-keyservers.netCA.pem > /tmp/sks-keyservers.netCA.pem
sudo trust anchor --store /tmp/sks-keyservers.netCA.pem
rm /tmp/sks-keyservers.netCA.pem

First, generate the master key. We'll be storing the master key offline and only using subkeys during normal operation. Thus, the master key only needs to be able to certify new keys:

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --expert --full-gen-key
gpg (GnuPG) 2.2.23; 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? 11

Possible actions for a ECDSA/EdDSA key: Sign Certify Authenticate 
Current allowed actions: Sign Certify  

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished
                                     
Your selection? s                                                         
                                                                          
Possible actions for a ECDSA/EdDSA key: Sign Certify Authenticate    
Current allowed actions: Certify                                          
                                                                          
   (S) Toggle the sign capability    
   (A) Toggle the authenticate capability
   (Q) Finished             
                                     
Your selection? q     
Please select which elliptic curve you want:                              
   (1) Curve 25519                                                        
   (3) NIST P-256                                                         
   (4) NIST P-384 
   (5) NIST P-521     
   (6) Brainpool P-256                                                    
   (7) Brainpool P-384
   (8) Brainpool P-512       
   (9) secp256k1  
Your selection? 1                    
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 Tue 26 Oct 2021 09:32:31 PM EDT
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: Nikita Pekin
Email address: personal@example.com
Comment: 
You selected this USER-ID:
    "Nikita Pekin <personal@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: key 0x4264C0ABC8A52CC1 marked as ultimately trusted
gpg: revocation certificate stored as '/home/indiv0/.gnupg/openpgp-revocs.d/37363F2B2EF6132D4DA2AECE4264C0ABC8A52CC1.rev'
public and secret key created and signed.

pub   ed25519/0x4264C0ABC8A52CC1 2020-10-27 [C] [expires: 2021-10-27]
      Key fingerprint = 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1
uid                              Nikita Pekin <personal@example.com>

We've created a master key with the fingerprint 4264C0ABC8A52CC1. Now we create the subkeys. First, list the available keys:

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --list-keys
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2021-10-27
/home/indiv0/.gnupg/pubring.kbx
-------------------------------
pub   ed25519/0x4264C0ABC8A52CC1 2020-10-27 [C] [expires: 2021-10-27]
      Key fingerprint = 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1
uid                   [ultimate] Nikita Pekin <personal@example.com>

Add the encryption key with the addkey command:

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --expert --edit-key 4264C0ABC8A52CC1
gpg (GnuPG) 2.2.23; 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  ed25519/0x4264C0ABC8A52CC1
     created: 2020-10-27  expires: 2021-10-27  usage: C   
     trust: ultimate      validity: ultimate
[ultimate] (1). Nikita Pekin <personal@example.com>

gpg> addkey
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? s

Possible actions for a RSA key: Sign Encrypt Authenticate 
Current allowed actions: Encrypt 

   (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) 1y
Key expires at Tue 26 Oct 2021 09:36:57 PM EDT
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  ed25519/0x4264C0ABC8A52CC1
     created: 2020-10-27  expires: 2021-10-27  usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x4B1922292563DA2D
     created: 2020-10-27  expires: 2021-10-27  usage: E   
[ultimate] (1). Nikita Pekin <personal@example.com>

We've created an encryption key with the fingerprint 4B1922292563DA2D. Now we repeat this process for the signing and authentication keys:

gpg> addkey
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? 11

Possible actions for a ECDSA/EdDSA key: Sign Authenticate 
Current allowed actions: Sign 

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? q
Please select which elliptic curve you want:
   (1) Curve 25519
   (3) NIST P-256
   (4) NIST P-384
   (5) NIST P-521
   (6) Brainpool P-256
   (7) Brainpool P-384
   (8) Brainpool P-512
   (9) secp256k1
Your selection? 1
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 Tue 26 Oct 2021 09:40:34 PM EDT
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  ed25519/0x4264C0ABC8A52CC1
     created: 2020-10-27  expires: 2021-10-27  usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x4B1922292563DA2D
     created: 2020-10-27  expires: 2021-10-27  usage: E   
ssb  ed25519/0xF7AD7C72CECB97FB
     created: 2020-10-27  expires: 2021-10-27  usage: S   
[ultimate] (1). Nikita Pekin <personal@example.com>

gpg> addkey
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? 11

Possible actions for a ECDSA/EdDSA key: Sign Authenticate 
Current allowed actions: Sign 

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? s

Possible actions for a ECDSA/EdDSA key: Sign Authenticate 
Current allowed actions: 

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? a

Possible actions for a ECDSA/EdDSA key: Sign Authenticate 
Current allowed actions: Authenticate 

   (S) Toggle the sign capability
   (A) Toggle the authenticate capability
   (Q) Finished

Your selection? q
Please select which elliptic curve you want:
   (1) Curve 25519
   (3) NIST P-256
   (4) NIST P-384
   (5) NIST P-521
   (6) Brainpool P-256
   (7) Brainpool P-384
   (8) Brainpool P-512
   (9) secp256k1
Your selection? 1
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 Tue 26 Oct 2021 09:41:27 PM EDT
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  ed25519/0x4264C0ABC8A52CC1
     created: 2020-10-27  expires: 2021-10-27  usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x4B1922292563DA2D
     created: 2020-10-27  expires: 2021-10-27  usage: E   
ssb  ed25519/0xF7AD7C72CECB97FB
     created: 2020-10-27  expires: 2021-10-27  usage: S   
ssb  ed25519/0x7BAD5F7DD968CD98
     created: 2020-10-27  expires: 2021-10-27  usage: A   
[ultimate] (1). Nikita Pekin <personal@example.com>

Thus, the signing and authentication key fingerprints are F7AD7C72CECB97FB and 7BAD5F7DD968CD98 respectively. Enter save to complete the process:

gpg> save

Generate a revocation certificate for the event of theft of the master key.

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --output 4264C0ABC8A52CC1.rev --gen-revoke 4264C0ABC8A52CC1

sec  ed25519/0x4264C0ABC8A52CC1 2020-10-27 Nikita Pekin <personal@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? 1
Enter an optional description; end it with an empty line:
> 
Reason for revocation: Key has been compromised
(No description given)
Is this okay? (y/N) y
ASCII armored output forced.
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!

Save all the keys.

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --export --armor 4264C0ABC8A52CC1 > 4264C0ABC8A52CC1.pub.asc
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --export-secret-keys --armor 4264C0ABC8A52CC1 > 4264C0ABC8A52CC1.priv.asc
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --export-secret-subkeys --armor 4264C0ABC8A52CC1 > 4264C0ABC8A52CC1.sub_priv.asc

4264C0ABC8A52CC1.pub.asc will contain all public keys, 4264C0ABC8A52CC1.priv.asc will contain the private keys of the master key, and 4264C0ABC8A52CC1.sub_priv.asc will contain only the private keys of the subkeys.

Next, we need to remove the private keys of the master key. First, delete all private keys:

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --delete-secret-key 4264C0ABC8A52CC1
gpg (GnuPG) 2.2.23; 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.


sec  ed25519/0x4264C0ABC8A52CC1 2020-10-27 Nikita Pekin <personal@example.com>

Delete this key from the keyring? (y/N) y
This is a secret key! - really delete? (y/N) y

Then, import only the private keys of the subkeys:

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --import 4264C0ABC8A52CC1.sub_priv.asc 
gpg: key 0x4264C0ABC8A52CC1: "Nikita Pekin <personal@example.com>" not changed
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key 0x4264C0ABC8A52CC1: secret key imported
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

Verify that only the private keys of the subkeys are present.

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --list-secret-keys
/home/indiv0/.gnupg/pubring.kbx
-------------------------------
sec#  ed25519/0x4264C0ABC8A52CC1 2020-10-27 [C] [expires: 2021-10-27]
      Key fingerprint = 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1
uid                   [ultimate] Nikita Pekin <personal@example.com>
ssb   rsa4096/0x4B1922292563DA2D 2020-10-27 [E] [expires: 2021-10-27]
ssb   ed25519/0xF7AD7C72CECB97FB 2020-10-27 [S] [expires: 2021-10-27]
ssb   ed25519/0x7BAD5F7DD968CD98 2020-10-27 [A] [expires: 2021-10-27]

The # symbol before sec indicates that the secret key of the master key no longer exists.

Personally, I also wanted to generate a separate GPG key for work, so I repreated all of the steps above again. The one major difference is that I have two work emails so I added the second one as another user ID under my work key.

gpg> adduid
Real name: Nikita Pekin
Email address: work2@example.com
Comment: 
You selected this USER-ID:
    "Nikita Pekin <work2@example.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

sec  ed25519/0x8D9358ECD4A65A32
     created: 2020-10-27  expires: 2021-10-27  usage: C   
     trust: ultimate      validity: ultimate
[ultimate] (1)  Nikita Pekin <work1@example.com>
[ unknown] (2). Nikita Pekin <work2@example.com>

gpg> uid 2

sec  ed25519/0x8D9358ECD4A65A32
     created: 2020-10-27  expires: 2021-10-27  usage: C   
     trust: ultimate      validity: ultimate
[ultimate] (1)  Nikita Pekin <work1@example.com>
[ unknown] (2)* Nikita Pekin <work2@example.com>

gpg> trust
sec  ed25519/0x8D9358ECD4A65A32
     created: 2020-10-27  expires: 2021-10-27  usage: C   
     trust: ultimate      validity: ultimate
[ultimate] (1)  Nikita Pekin <work1@example.com>
[ unknown] (2)* Nikita Pekin <work2@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

sec  ed25519/0x8D9358ECD4A65A32
     created: 2020-10-27  expires: 2021-10-27  usage: C   
     trust: ultimate      validity: ultimate
[ultimate] (1)  Nikita Pekin <work1@example.com>
[ unknown] (2)* Nikita Pekin <work2@example.com>

gpg> save

I ended up with the following key list in the end:

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --list-secret-keys
/home/indiv0/.gnupg/pubring.kbx
-------------------------------
sec#  ed25519/0x4264C0ABC8A52CC1 2020-10-27 [C] [expires: 2021-10-27]
      Key fingerprint = 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1
uid                   [ultimate] Nikita Pekin <personal@example.com>
ssb   rsa4096/0x4B1922292563DA2D 2020-10-27 [E] [expires: 2021-10-27]
ssb   ed25519/0xF7AD7C72CECB97FB 2020-10-27 [S] [expires: 2021-10-27]
ssb   ed25519/0x7BAD5F7DD968CD98 2020-10-27 [A] [expires: 2021-10-27]

sec#  ed25519/0x8D9358ECD4A65A32 2020-10-27 [C] [expires: 2021-10-27]
      Key fingerprint = 846F 4458 2952 EEC3 B275  6F1F 8D93 58EC D4A6 5A32
uid                   [ultimate] Nikita Pekin <work2@example.com>
uid                   [ultimate] Nikita Pekin <work1@example.com>
ssb   rsa4096/0x9F4B2DDF8049967B 2020-10-27 [E] [expires: 2021-10-27]
ssb   ed25519/0x05259179BC027533 2020-10-27 [S] [expires: 2021-10-27]
ssb   ed25519/0xB3A681B5B1CA424A 2020-10-27 [A] [expires: 2021-10-27]

Set the new key as the default key in your .gnupg/gpg.conf:

default-key 37363F2B2EF6132D4DA2AECE4264C0ABC8A52CC1

Publish the new keys to the keyservers:
```sh
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --send-key 4264C0ABC8A52CC1
gpg: sending key 0x4264C0ABC8A52CC1 to hkps://hkps.pool.sks-keyservers.net
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --send-key 8D9358ECD4A65A32
gpg: sending key 0x8D9358ECD4A65A32 to hkps://hkps.pool.sks-keyservers.net

Since I generated a work key, I wanted to ensure that anyone who trusts my primary key could trust that one as well, so I signed my work key with my primary key.

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --sign-key 8D9358ECD4A65A32
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --send-keys 8D9358ECD4A65A32

Next, I wanted to revoke all my existing keys to ensure that they wouldn't be used for any correspondence. Luckily I still had all my previous master keys available, so I imported them into my keyring an issued the revocation certificates:

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --import 8558129A36D54E73.priv.asc
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --import 99FA8C40093C34AC.priv.asc
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --import A63C40C35614D8D6.priv.asc
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --import 5CA3587585FEBB49.priv.asc
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --import privkeys.asc
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --edit-key 99FA8C40093C34AC
gpg (GnuPG) 2.2.23; 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/0x99FA8C40093C34AC
     created: 2016-12-14  expired: 2018-12-14  usage: SC  
     trust: unknown       validity: expired
ssb  rsa4096/0x82DC1CF7FDE3DF1F
     created: 2016-12-14  expired: 2018-12-14  usage: E   
[ expired] (1). Nikita Pekin <foo@example.com>

gpg> revkey
Do you really want to revoke the entire 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
Your decision? 2
Enter an optional description; end it with an empty line:
> Key has been superseded by new keys 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1 (personal) and 846F 4458 2952 EEC3 B275  6F1F 8D93 58EC D4A6 5A32 (work).
> 
Reason for revocation: Key is superseded
Key has been superseded by new keys 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1 (personal) and 846F 4458 2952 EEC3 B275  6F1F 8D93 58EC D4A6 5A32 (work).
Is this okay? (y/N) y

The following key was revoked on 2020-10-27 by RSA key 0x99FA8C40093C34AC Nikita Pekin <foo@example.com>
sec  rsa4096/0x99FA8C40093C34AC
     created: 2016-12-14  revoked: 2020-10-27  usage: SC  
     trust: unknown       validity: revoked
The following key was revoked on 2020-10-27 by RSA key 0x99FA8C40093C34AC Nikita Pekin <foo@example.com>
ssb  rsa4096/0x82DC1CF7FDE3DF1F
     created: 2016-12-14  revoked: 2020-10-27  usage: E   
[ revoked] (1). Nikita Pekin <foo@example.com>

gpg> save
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --edit-key 8558129A36D54E73
gpg (GnuPG) 2.2.23; 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/0x8558129A36D54E73
     created: 2015-01-13  expires: never       usage: SC  
     trust: unknown       validity: unknown
ssb  rsa4096/0xE96501EEE6721CA4
     created: 2015-01-13  expires: never       usage: E   
[ unknown] (1). Nikita Pekin <foo@example.com>

gpg> revkey
Do you really want to revoke the entire 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
Your decision? 2
Enter an optional description; end it with an empty line:
> Key has been superseded by new keys 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1 (personal) and 846F 4458 2952 EEC3 B275  6F1F 8D93 58EC D4A6 5A32 (work).
> 
Reason for revocation: Key is superseded
Key has been superseded by new keys 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1 (personal) and 846F 4458 2952 EEC3 B275  6F1F 8D93 58EC D4A6 5A32 (work).
Is this okay? (y/N) y

The following key was revoked on 2020-10-27 by RSA key 0x8558129A36D54E73 Nikita Pekin <foo@example.com>
sec  rsa4096/0x8558129A36D54E73
     created: 2015-01-13  revoked: 2020-10-27  usage: SC  
     trust: unknown       validity: revoked
The following key was revoked on 2020-10-27 by RSA key 0x8558129A36D54E73 Nikita Pekin <foo@example.com>
ssb  rsa4096/0xE96501EEE6721CA4
     created: 2015-01-13  revoked: 2020-10-27  usage: E   
[ revoked] (1). Nikita Pekin <foo@example.com>

gpg> save
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --edit-key A63C40C35614D8D6
gpg (GnuPG) 2.2.23; 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.

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
gpg: next trustdb check due at 2021-10-27
sec  ed25519/0xA63C40C35614D8D6
     created: 2018-12-16  expires: 2020-12-15  usage: C   
     trust: unknown       validity: unknown
ssb  cv25519/0xA37CBCCC793F7304
     created: 2018-12-16  expires: 2020-12-15  usage: E   
ssb  ed25519/0x29E24F74E09A5E6B
     created: 2018-12-16  expires: 2020-12-15  usage: S   
ssb  ed25519/0x293943476C14D67D
     created: 2018-12-16  expires: 2020-12-15  usage: A   
[ unknown] (1). Nikita Pekin <bar@example.com>

gpg> revkey
Do you really want to revoke the entire 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
Your decision? 2
Enter an optional description; end it with an empty line:
> Key has been superseded by new keys 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1 (personal) and 846F 4458 2952 EEC3 B275  6F1F 8D93 58EC D4A6 5A32 (work).
> 
Reason for revocation: Key is superseded
Key has been superseded by new keys 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1 (personal) and 846F 4458 2952 EEC3 B275  6F1F 8D93 58EC D4A6 5A32 (work).
Is this okay? (y/N) y

The following key was revoked on 2020-10-27 by ? key 0xA63C40C35614D8D6 Nikita Pekin <bar@example.com>
sec  ed25519/0xA63C40C35614D8D6
     created: 2018-12-16  revoked: 2020-10-27  usage: C   
     trust: unknown       validity: revoked
The following key was revoked on 2020-10-27 by ? key 0xA63C40C35614D8D6 Nikita Pekin <bar@example.com>
ssb  cv25519/0xA37CBCCC793F7304
     created: 2018-12-16  revoked: 2020-10-27  usage: E   
The following key was revoked on 2020-10-27 by ? key 0xA63C40C35614D8D6 Nikita Pekin <bar@example.com>
ssb  ed25519/0x29E24F74E09A5E6B
     created: 2018-12-16  revoked: 2020-10-27  usage: S   
The following key was revoked on 2020-10-27 by ? key 0xA63C40C35614D8D6 Nikita Pekin <bar@example.com>
ssb  ed25519/0x293943476C14D67D
     created: 2018-12-16  revoked: 2020-10-27  usage: A   
[ revoked] (1). Nikita Pekin <bar@example.com>

gpg> save
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --edit-key 5CA3587585FEBB49
gpg (GnuPG) 2.2.23; 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.

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u
gpg: next trustdb check due at 2021-10-27
sec  ed25519/0x5CA3587585FEBB49
     created: 2018-12-18  expires: 2020-12-17  usage: C   
     trust: unknown       validity: unknown
ssb  cv25519/0xBF05CC4D5C9DE040
     created: 2018-12-18  expires: 2020-12-17  usage: E   
ssb  ed25519/0x2BDD3761AD436884
     created: 2018-12-18  expires: 2020-12-17  usage: S   
ssb  ed25519/0x2AD6EDFB2D211DD4
     created: 2018-12-18  expires: 2020-12-17  usage: A   
[ unknown] (1). Nikita Pekin <baz@example.com>

gpg> revkey
Do you really want to revoke the entire 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
Your decision? 2
Enter an optional description; end it with an empty line:
> Key has been superseded by new keys 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1 (personal) and 846F 4458 2952 EEC3 B275  6F1F 8D93 58EC D4A6 5A32 (work).
> 
Reason for revocation: Key is superseded
Key has been superseded by new keys 3736 3F2B 2EF6 132D 4DA2  AECE 4264 C0AB C8A5 2CC1 (personal) and 846F 4458 2952 EEC3 B275  6F1F 8D93 58EC D4A6 5A32 (work).
Is this okay? (y/N) y

The following key was revoked on 2020-10-27 by ? key 0x5CA3587585FEBB49 Nikita Pekin <baz@example.com>
sec  ed25519/0x5CA3587585FEBB49
     created: 2018-12-18  revoked: 2020-10-27  usage: C   
     trust: unknown       validity: revoked
The following key was revoked on 2020-10-27 by ? key 0x5CA3587585FEBB49 Nikita Pekin <baz@example.com>
ssb  cv25519/0xBF05CC4D5C9DE040
     created: 2018-12-18  revoked: 2020-10-27  usage: E   
The following key was revoked on 2020-10-27 by ? key 0x5CA3587585FEBB49 Nikita Pekin <baz@example.com>
ssb  ed25519/0x2BDD3761AD436884
     created: 2018-12-18  revoked: 2020-10-27  usage: S   
The following key was revoked on 2020-10-27 by ? key 0x5CA3587585FEBB49 Nikita Pekin <baz@example.com>
ssb  ed25519/0x2AD6EDFB2D211DD4
     created: 2018-12-18  revoked: 2020-10-27  usage: A   
[ revoked] (1). Nikita Pekin <baz@example.com>

gpg> save

Then, I sent the now revoked keys to the keyservers.

[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --send-key 8558129A36D54E73
gpg: sending key 0x8558129A36D54E73 to hkps://hkps.pool.sks-keyservers.net
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --send-key A63C40C35614D8D6
gpg: sending key 0xA63C40C35614D8D6 to hkps://hkps.pool.sks-keyservers.net
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --send-key 5CA3587585FEBB49
gpg: sending key 0x5CA3587585FEBB49 to hkps://hkps.pool.sks-keyservers.net
[indiv0@apollo 2020-10-26_gpg_key_refresh]$ gpg --send-key 99FA8C40093C34AC
gpg: sending key 0x99FA8C40093C34AC to hkps://hkps.pool.sks-keyservers.net

I then re-encrypted my pass directory against my new personal key, and selectively re-encrypted my work password directory against both keys.

pass init 4264C0ABC8A52CC1
echo 4264C0ABC8A52CC1 > $PASSWORD_STORE_DIR/work/.gpg-id
echo 8D9358ECD4A65A32 >> $PASSWORD_STORE_DIR/work/.gpg-id
pass init -p $(cat $PASSWORD_STORE_DIR/work/.gpg-id)

After pushing my updated pass directory to GitHub & pulling it to my phone (along with my new keys) I needed to create a "lifeboat" to ensure that I could always have a way to re-gain access to my passwords. To do so, I created a lifeboat with my personal GPG key and a copy of my pass directory at this point in time.

[indiv0@apollo usr]$ mkdir 2020-10-27_lifeboat
[indiv0@apollo usr]$ cd 2020-10-27_lifeboat/
[indiv0@apollo 2020-10-27_lifeboat]$ cp ../2020-10-26_gpg_key_refresh/4264C0ABC8A52CC1.* .
[indiv0@apollo 2020-10-27_lifeboat]$ ls
4264C0ABC8A52CC1.priv.asc  4264C0ABC8A52CC1.rev
4264C0ABC8A52CC1.pub.asc   4264C0ABC8A52CC1.sub_priv.asc
[indiv0@apollo 2020-10-27_lifeboat]$ rsync --archive --partial --progress $PASSWORD_STORE_DIR .
[indiv0@apollo 2020-10-27_lifeboat]$ find . -type d -exec chmod 700 {} \;
[indiv0@apollo 2020-10-27_lifeboat]$ find . -type f -exec chmod 600 {} \;
[indiv0@apollo 2020-10-27_lifeboat]$ cd ..
[indiv0@apollo usr]$ tar czvf 2020-10-27_lifeboat2.tar.gz 2020-10-27_lifeboat
[indiv0@apollo usr]$ gpg --symmetric --output 2020-10-27_lifeboat.tar.gz.gpg 2020-10-27_lifeboat.tar.gz

Then, I copied this lifeboat to a separate machine to test that it worked. Since I re-use the same passphrase for symmetric encryption of the lifeboat and for the GPG keys themselves, I only need to remember one password to restore access.

[npekin@apollo ~]$ gpg --decrypt 2020-10-27_lifeboat.tar.gz.gpg | tar xz
[npekin@apollo ~]$ cd 2020-10-27_lifeboat
[npekin@apollo 2020-10-27_lifeboat]$ gpg --import 4264C0ABC8A52CC1.sub_priv.asc
[npekin@apollo 2020-10-27_lifeboat]$ PASSWORD_STORE_DIR=pass pass gpg_passphrase

To clean up my keyring a bit, I removed the keys I just revoked.

gpg --delete-secret-keys 8558129A36D54E73 A63C40C35614D8D6 5CA3587585FEBB49 99FA8C40093C34AC
gpg --delete-keys 8558129A36D54E73 A63C40C35614D8D6 5CA3587585FEBB49 99FA8C40093C34AC

To finish, I distributed my lifeboat to a variety of locations. Offline harddrives, cloud storage, email to trusted family.