#g33kr_

[EAtT] One GPG key pair (to rule them all)

GNU Privacy Guard (GPG), a Free Software implementation of the OpenPGP standard, is an amazing tool for keeping your stored data and communications private and secure. In this guide, I'll show you how to generate a multi-tiered GPG key pair following established best practices for key management and security.

GPG primer

Before we get into actual key generation, if you need more basic information about GPG concepts, check out my article Getting started with GPG.

Realistic security goals

First, let's layout some clear security goals and set some realistic expectations. With the GPG keys we're going to generate in this guide, we want to:

  • Have the tools to cryptographically secure stored data and/or electronic communications from unlawful access, theft, interception, or impersonation.
  • Secure the integrity of our GPG keys against compromise or loss due to negligence or malicious acts.
  • Allocate functions for GPG signing, encryption, and authentication in a manner that provides us with a sustainable, fault-tolerant GPG key management strategy for the future.

Based on available knowledge at the time of this writing, no brute force attack is currently effective against the crypto algorithms used by GPG. Barring some as-yet undetected (or unrevealed) weakness in the underlying algorithms, the odds of a malicious actor successfully breaking your bits through direct brute force attacks on your encrypted data is very near zero. It is much more likely that you would be compromised by the choice of a weak passphrase for your private key, or by allowing a keylogger or rootkit to become installed on your system, where it may capture your passphrase and/or exfiltrate key files.

That said, as cryptographically secure as they may be, every GPG key in use today is subject to attack by a $5 pipe wrench - with a high probability of successful data extraction. Be realistic about your threat model and the mitigation strategies you employ.

Primary key and subkeys (why?)

Typically, GPG keys are created with a primary key used for both certification and signing, and a separate subkey for the purpose of encryption. This is simple and expedient, but it creates a problem in that the key used for certification (signing other people's keys) is also the key used for digitally signing files and messages. Signing keys is a relatively infrequent task, while signing messages and files is something someone may do many times in a day. But the same private key is required for both tasks, and so must be available (and in risk of compromise) at all times.

Further, UIDs (key owner identities) are assigned to the primary key. In the event that the private key for the primary were to be lost or compromised, the public key must be revoked and a new key pair created. Any signatures on UIDs associated with the previous public key would have to be re-signed on the new public key. And any signatures made by the old primary key on the UIDs of others would likewise need to be re-signed. This creates a significant amount of administrative overhead in the event of a key compromise. It also creates a discontinuity in key history, where a person now has an "old key" and a "new key" with the same UIDs assigned, and other people have to determine which key to use.

The certification capability and UID holding function of the primary key serves a vital (but infrequently used) role. So why not isolate the private key for the primary key pair from the more frequently used (but easier to replace) key functions?

We're going to create a GPG primary key with an offline private key, that will only be used for certification. We will then create online subkeys for each of the capabilities of signing, encryption, and authentication. This setup will bestow the following benefits:

  • We will assign only one role to each key for clear separation of function.
  • The primary key will only be used for certification.
  • The private key for the primary will only be required to be available in order to perform infrequent key-management tasks:
    • performing or revoking key signatures for others
    • updating expiration dates on the primary key and subkeys
    • adding or revoking UIDs on the primary key
  • At all other times, the private key for the primary will remain securely stored offline.
  • Should any subkey become compromised, that key can be readily revoked and replaced - without impact to any signatures made with the primary key, or to UIDs associated with the primary key that have been signed by others.
  • The key owner should be able to maintain a single public key in perpetuity, as long as the private key for the primary remains secure.

Assess your degree of paranoia

You can make your key generation as simple or as complicated as you see fit, based on your own level of security concern. You can choose to:

  1. Generate keys on your usual laptop or desktop while connected to the Internet, or
  2. Boot from a live disk image and generate your keys while disconnected from the network, or
  3. Use a burner computer that you will destroy after generating your keys in a Faraday cage located in the basement of a high-security facility surrounded by guard towers and barbed wire (or say, in the basement of your mom's house).

I leave those particulars up to you. I do however, recommend the following secure storage methods for your primary private key:

  • Prepare two USB flash drives, formatted with a LUKS filesystem and encrypted with a strong passphrase. Each thumb drive should hold a copy of the primary private key and the revocation certificate you will generate, as well as backups of your private subkeys.

  • These two flash drives should be stored in a locked (and preferably, fire-rated) box kept in a secure location.

  • One additional flash drive (also encrypted) should be stored in a secondary secure location, such as a bank safe deposit box.

  • (Optionally) create two printed copies of both your private key and your revocation certificate on standard weight copier paper with at least a 1 inch (2.5cm) page margin for the text. Place one copy of each (without folding) in two sealed heavy-weight clasp envelopes. Write your signature across the seals of both envelopes, and place one envelope in your fire box with your flash drives and the other in your safe deposit box with the offsite drive. These are your backups of last resort in the event of bit rot, EMP, strong magnetic fields, solar flares, whatever.

Which do you think would stand a better chance of surviving a house fire, a flash drive or an envelope of tightly packed paper? Commonly, "fire safes" are rated based on the protection of documents, not computer storage media. The average house fire can burn at 1100°F or hotter, and the interior of a fire safe can reach 350°F or more after one hour in such a fire. That temperature is well below the 480°F at which paper will combust in absence of flame (sorry, the late, great Ray Bradbury was famously off by about 30°F). But electronic media - particularly NAND-based solid state storage devices, like flash drives? After much searching, I couldn't find conclusive data either way as to whether or not a USB flash drive would survive such extreme temperatures (and humidity) with data intact. If anybody does some home testing with their oven and a flash drive, let me know the results, please. For now we can say, paper: almost certainly. Flash drive: maybe so, maybe not.

Create your config file

Your GPG configuration is located in the file ~/.gnupg/gpg.conf. Add the following settings to your config:

#default-key <key ID> #uncomment after keygen
no-emit-version
no-comments
keyid-format 0xlong
with-fingerprint
list-options show-uid-validity
verify-options show-uid-validity
use-agent
require-cross-certification
personal-cipher-preferences AES256 AES192 AES CAST5
personal-digest-preferences SHA512 SHA384 SHA256 SHA224
cert-digest-algo SHA512
s2k-digest-algo SHA512
s2k-cipher-algo AES256
charset utf-8
fixed-list-mode
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed

These settings are based on most of the OpenPGP best practices presented by riseup.net.

Verify your GPG version

Make sure you are using GPG v2.1 or greater:

$ gpg --version

If your distribution has not converted the default GPG from v1.x to v2.x you will need to explicitly install GPG v2.x and alias gpg to gpg2:

$ sudo apt install gpg2
$ alias gpg='gpg2'

Generating sufficient entropy

In order to create secure RSA keys, GPG will need a supply of sufficiently random numbers. This randomness is derived from hardware 'noise' bits that feed the entropy pool used by /dev/random to generate random numbers. On some systems with limited hardware activity (and particularly in the case of virtual machines) entropy generation (and therefore GPG key generation) may take an unusually long time (hours, even days). A solution to this problem is to install haveged, an entropy generator that can drastically speed up key generation.

To install haveged just use the command:

$ sudo apt install haveged

After key generation is complete, you can remove haveged if you no longer have a need for it:

$ sudo apt remove haveged

Generate your primary key

To start the key generation, you're going to issue the command:

$ gpg --expert --full-gen-key

Because we are using --expert GPG will give us more key options than are usually presented in the key generation dialog. You will choose option '8':

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
Your selection? 8

The key will default to having the Sign, Certify, and Encrypt capabilities. We only want Certify, so we will toggle the other two off and then continue by hitting 'Q':

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

Now we'll choose a key length for this key. I generally use and recommend 4096-bit:

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits

It is important to understand that RSA key strength in terms of resistance to brute force attacks does not correlate to key length in bits as a linear function. RSA-4096 is not twice as strong as RSA-2048 - but it is stronger. It also requires much more computational power to factor than RSA-2048. A few years ago, this was a concern and could limit the useful application of RSA-4096 keys on low CPU devices (like smartcards). These days, CPU power is rarely a concern even on mobile platforms and there are smartcards on the market today that support RSA-4096 keys. My position on key length is, if CPU is not a limiting factor for your application - why not use the strongest key option available that is widely supported?

Note that others, including the GnuPG project itself, recommend using elliptic-curve cryptography (ECC) ciphers instead of RSA-4096. ECC theoretically provides much stronger security with far less computation. My take on that is admittedly basic (because I am not a cryptographer): RSA is a proven algorithm that has withstood challenge for 40 years, and ECC is a relative newcomer for whom the jury is still out. As with all things, Your Mileage May Vary. Choose the algorithm you think is appropriate for your own use.

Now GPG will ask you to set an expiration date on your key. You want to set this. You can extend your expiration date whenever you need to, this is just a precaution. Should you ever lose your private key and cannot revoke your public key, at least your key will expire at some point and people will know not to use it. As long as you have access to your private key, you may extend your expiration date indefinitely.

Since we're creating the primary key and the private key will be stored offline, you can set a relatively long expiration date (2 to 3 years is fine):

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) 3y
Key expires at Wed 28 Oct 2020 02:52:03 PM CDT
Is this correct? (y/N) y

Now you'll create your first UID (name and email address). This should be the identity you will use with the GPG key:

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

Real name: Rick Demo
Email address: rick@demo.com
Comment:
You selected this USER-ID:
    "Rick Demo <rick@demo.com>"

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

GPG will now prompt you for a passphrase for the new key. Enter a strong passphrase that you will be capable of remembering. It is important that you remember this passphrase. If you forget your passphrase, you will no longer be able to use the GPG key. There is no way to reset a lost passphrase.

After you choose your password, GPG will generate your key. If you've installed haveged this should occur pretty quickly. If not, you can type on the keyboard, move the mouse, or switch to another window and do work. This will help create entropy and speed the key generation process.

When the keys are generated, GPG will print out the information for your new key.

public and secret key created and signed

pub   rsa4096/0x7CD6ACA4C84DA8B3 2017-10-29 [C] [expires: 2020-10-28]
      Key fingerprint = 4C64 592C 9611 E531 1A81  8824 7CD6 ACA4 C84D A8B3
uid                   [ultimate] Rick Demo <rick@demo.com>

Generate a revocation file

Before going any further, you should generate a revocation certificate for your new key. You do that with the --gen-revoke option:

$ gpg --gen-revoke -o rdemo.rc.asc rick@demo.com

The -o option sends the ASCII armored output to a file. GPG will ask you for a reason for the revocation, choose '1':

sec  rsa4096/0x7CD6ACA4C84DA8B3 2017-10-29 Rick Demo <rick@demo.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

When the revocation certificate is generated, GPG will confirm. You should then check the contents of the file it created:

Revocation certificate created.

$ cat rdemo.rc.asc

-----BEGIN PGP PUBLIC KEY BLOCK----- 
Comment: This is a revocation certificate

iQI2BCABCgAgFiEETGRZLJYR5TEagYgkfNaspMhNqLMFAln2Si0CHQIACgkQfNas
pMhNqLPugQ/9FKtd7NwIv8cBQ5rHj5U3MoAWtg2KkbLZrOUIV8L+D4n2mJJW6B+a
FVL23Qns/c9VAnwrw0BJiFOpmdiC5rGRVxfVIA+Q7WaBNdsS1yZef+N3vbruqlJx
hP+ztaiia76IlH+si0Pt0SSgl+jNboR5EyLMuKQz9QOyiNpEosPciDqpYZ6g50/t
...
Z6xw90aXG/fphFyuUH94Zwx2fLIhOec0OTftfdP79FE9hhfulS1FeSg=
=mHwc
-----END PGP PUBLIC KEY BLOCK-----

This file is very sensitive. Be sure to secure it properly. If anyone else is able to obtain this file, they can revoke your public key.

Generate your subkeys

Now we're going to generate your subkeys for signing, encryption and authentication. Use the --edit-key option to begin (be sure to use --expert also):

$ gpg --expert --edit-key rick@demo.com

Secret key is available.

sec  rsa4096/0x7CD6ACA4C84DA8B3
     created: 2017-10-29  expires: 2020-10-28  usage: C
     trust: ultimate      validity: ultimate
[ultimate] (1). Rick Demo <rick@demo.com>

gpg>

Now use the addkey command to create a new subkey. The process is very similar to creating the primary key. For this first subkey, we'll assign the 'Sign' capability. We'll also set the expiration date to be just 1 year:

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
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? E

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

   (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 Mon 29 Oct 2018 04:51:53 PM CDT
Is this correct? (y/N) y
Really create? (y/N) y

You'll be prompted to enter your private key passphrase to create your new key. When the key generation is complete, save the new subkey:

Secret key is available.

sec  rsa4096/0x7CD6ACA4C84DA8B3
     created: 2017-10-29  expires: 2020-10-28  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x582CBC299B4C2718
     created: 2017-10-29  expires: 2018-10-29  usage: S
[ultimate] (1). Rick Demo <rick@demo.com>

gpg> save

Repeat the same process twice more to create the encryption [E] and authentication [A] subkeys. When completed, you should have one subkey for each role:

Secret key is available.

sec  rsa4096/0x7CD6ACA4C84DA8B3
     created: 2017-10-29  expires: 2020-10-28  usage: C   
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x582CBC299B4C2718
     created: 2017-10-29  expires: 2018-10-29  usage: S   
ssb  rsa4096/0x7C97F91B5079A317
     created: 2017-10-29  expires: 2018-10-29  usage: E   
ssb  rsa4096/0x88C4DD5D965ABA57
     created: 2017-10-29  expires: 2018-10-29  usage: A   
[ultimate] (1). Rick Demo <rick@demo.com>

Export your public key

Now let's export your public key to a file that can be distributed:

$ gpg --export -a -o rick.demo.asc rick@demo.com
$ cat rick.demo.asc
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBFn2MnABEAC1j91XkuAeFmhhrVeWduZ8lNrD/wcFsYCjHlTqVGJjlBbEyAza
28IFUiWfLt3a/ThjapEOdHt4CRD7tfb0H7Tt3QHxMRr3osTTjK4IR7gv9u/lypgO
ZN/ukFk7OOG2KW/0PwqccgaMe+hwA86CCEx8kgUElLHFV7O4BjyjLxCIu/o9YgQK
9PJM/MujjShnenmLf7S5nlD44AYabcfHd/58O21bVQQsbZ6jKczRY+POHXhvv971
HfxfVARK6tWCFYksOq5LASd86t8djchhXZBHbLqji2YyAJteW80rqUdsX46TDLcG
HzVBwAWCtOJld0MdE5IrCYdUtgMtscchj1RZH/RIz5/SUcX1Tu5KQRUeRxiyNgdD
UCzmL1rNNbsgTTsnb/bsnPEScwkRQcs+NnQKS6t93VuC/upHIyP027g7Kakxu0lH
L0lvih2wBFNsZoN5MnnQrMTBajlUfLopn9ZlSn1nRsv+luwkmdUfY1FfN5hz4qsI
JnWfoACFd7F2GGdtxPh50rHmOrxYkheA4bVdE4mnOlkHZ4EjUF2Y0AvnXCVg6C6I
q/cPFsgYvIfxaansXVweLnWXpY2FuuVkhNib2oMMf2IOZM+AVe9qp0Qz+Q3yk9Gi
jqhd5BkVRiVbqQYZ1o4CVj4zJS65QgOwA6jrOC79m6sw2e1o/zxaV36GHwARAQAB
tBlSaWNrIERlbW8gPHJpY2tAZGVtby5jb20+iQJUBBMBCgA+FiEETGRZLJYR5TEa
gYgkfNaspMhNqLMFAln2MnACGwEFCQWjmoAFCwkIBwMFFQoJCAsFFgIDAQACHgEC
...
XPu2GYFsl5CXKaaS1eGSnyqnRQOh6lzBZqX+yYR+3rfR9A5w6Pvzt42mHa/DRiqF
wv3kiieGxOrQpUDG7Rn3XYZ6EWKyDjLSxWpVQtQnV2SIisFhKCOAQ7WzKx2x7NoM
GcGgOaP1adaL+eedyO9zjQJ9YYMVIqwY++QVTSoVocrQOaj4yw==
=LdJ2
-----END PGP PUBLIC KEY BLOCK-----

While we're at it, let's export your authentication public key to a file in OpenSSH format that can be added to the authorized_keys file of remote systems for SSH access:

$ gpg --export-ssh -o rick.demo.pub rick@demo.com
$ cat rick.demo.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1K97bgn+y72uMtmgFdp50Gis+I
YD3B2IRFINZVA/IgqFe2Y3XPAtK+S6cX19elO2r54iqNsqT7lrw0umGxiD4eOJTJJ
cHmCK+uFI1VkgKM075hUNslX6ogpQcJ55F8tcnly6MepXHQxPCJ4MskCaH03CFOjG
RxdBfwJk8ImOlNYvupcAKOPTErbIWJ5uHyH/+avS/w28229R5M3hrQi5khcdqziKU
...
OJwSCOe+IuuCBF5eO3CYdDQ== openpgp:0x965ABA57

For more information about using your authentication subkey for SSH access, see SSH key authentication with GPG.

Export your private keys

Now we'll export your primary private key and your subkey private keys. These key files must be secured carefully against both data loss and theft:

$ gpg --export-secret-keys -a -o rick.demo.sec.asc rick@demo.com
$ gpg --export-secret-subkeys -a -o rick.demo.ssb.asc rick@demo.com

You will be prompted for your key passphrase for each command. Check the contents of the output files to make sure they contain the exported keys! Also remember that these files are useless without your private key passphrase. Don't forget your passphrase.

Now STOP! Go back up your keys (I mean it)

Seriously, before you proceed any further stop now and backup your key export files. In the next step we're going to delete your secret keys. Before you do that, make sure you have you have the following backed up to multiple secure locations:

  • the primary private key export file
  • the subkey private keys export file
  • the revocation certificate file

Secure the primary key (by deleting it)

Now we'll secure the private key for your primary key by deleting it from your keyring:

$ gpg --delete-secret-keys rick@demo.com

sec  rsa4096/0x7CD6ACA4C84DA8B3 2017-10-29 Rick Demo <rick@demo.com>

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

Unfortunately, deleting the primary private key will also delete the private keys for the subkeys. No problem, though. We'll just re-import them from your subkey backup file (you did make a backup of your subkeys, right?):

$ gpg --import rick.demo.ssb.asc

GPG will prompt you for the private key passphrase, and then import your keys. If you do a gpg -K again, you'll now see that you have private keys for the subkeys, but the primary key is denoted with 'sec#' which means that a private key does not exist for that key:

$ gpg -K
/home/rdemo/.gnupg/pubring.kbx
------------------------------
sec#  rsa4096/0x7CD6ACA4C84DA8B3 2017-10-29 [C] [expires: 2020-10-28]
      Key fingerprint = 4C64 592C 9611 E531 1A81  8824 7CD6 ACA4 C84D A8B3
uid                   [ultimate] Rick Demo <rick@demo.com>
ssb   rsa4096/0x582CBC299B4C2718 2017-10-29 [S] [expires: 2018-10-29]
ssb   rsa4096/0x7C97F91B5079A317 2017-10-29 [E] [expires: 2018-10-29]
ssb   rsa4096/0x88C4DD5D965ABA57 2017-10-29 [A] [expires: 2018-10-29]

Your public keyring looks like this:

$ gpg -k
/home/rdemo/.gnupg/pubring.kbx
------------------------------
pub   rsa4096/0x7CD6ACA4C84DA8B3 2017-10-29 [C] [expires: 2020-10-28]
      Key fingerprint = 4C64 592C 9611 E531 1A81  8824 7CD6 ACA4 C84D A8B3
uid                   [ultimate] Rick Demo <rick@demo.com>
sub   rsa4096/0x582CBC299B4C2718 2017-10-29 [S] [expires: 2018-10-29]
sub   rsa4096/0x7C97F91B5079A317 2017-10-29 [E] [expires: 2018-10-29]
sub   rsa4096/0x88C4DD5D965ABA57 2017-10-29 [A] [expires: 2018-10-29]

Publishing your new key

You can now publish your new public key by sending it to the public keyserver network:

$ gpg --send-keys rick@demo.com

Or you may publish the public key file we exported previously on a network share or website.

Use and ongoing maintenance

Your new GPG key is complete and you can now use it like any other key pair, except for performing the following tasks:

  1. Signing UIDs on the public keys of others.
  2. Modifying the expiration dates for either your primary key, or your subkeys.
  3. Adding or revoking UIDs or subkeys.

To perform these tasks, you will need access to the private key of the primary key.

Typically, the way to handle this is to keep an offline .gnupg directory on your backup disks. This directory should hold your complete keyring with all private keys installed. When it's time to extend your expiration dates, mount your backup disk at some location (/mnt/tmp for example). Extend your expiration dates with the --edit-key option, but use the --homedir option to specify the temporary location where the offline .gnupg directory is mounted:

$ gpg --homedir /mnt/tmp/.gnupg --edit-key rick@demo.org

Secret key is available.

sec  rsa4096/0x7CD6ACA4C84DA8B3
     created: 2017-10-29  expires: 2020-10-28  usage: C
     trust: ultimate      validity: ultimate
ssb  rsa4096/0x582CBC299B4C2718
     created: 2017-10-29  expires: 2018-10-29  usage: S
ssb  rsa4096/0x7C97F91B5079A317
     created: 2017-10-29  expires: 2018-10-29  usage: E
ssb  rsa4096/0x88C4DD5D965ABA57
     created: 2017-10-29  expires: 2018-10-29  usage: A
[ultimate] (1). Rick Demo <rick@demo.com>

gpg> key 1
gpg> key 2
gpg> key 3

Note that by selecting the keys with the key command, there is now an * next to each subkey. This will allow you to set the expiration date on all of these subkeys at once. You could also choose to do them individually if you wanted to set different expiration dates for each.

sec  rsa4096/0x7CD6ACA4C84DA8B3
     created: 2017-10-29  expires: 2020-10-29  usage: C
     trust: ultimate      validity: ultimate
ssb* rsa4096/0x582CBC299B4C2718
     created: 2017-10-29  expires: 2018-10-29  usage: S
ssb* rsa4096/0x7C97F91B5079A317
     created: 2017-10-29  expires: 2018-10-29  usage: E
ssb* rsa4096/0x88C4DD5D965ABA57
     created: 2017-10-29  expires: 2018-10-29  usage: A
[ultimate] (1). Rick Demo <rick@demo.com>

Now just change the expiration dates with the expire command:

gpg> expire
Are you sure you want to change the expiration time for multiple subkeys? (y/N) Y
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) 18m
Key expires at Mon 22 Apr 2019 08:08:26 PM CDT
Is this correct? (y/N) Y

sec  rsa4096/0x7CD6ACA4C84DA8B3
     created: 2017-10-29  expires: 2020-10-29  usage: C
     trust: ultimate      validity: ultimate
ssb* rsa4096/0x582CBC299B4C2718
     created: 2017-10-29  expires: 2019-04-23  usage: S
ssb  rsa4096/0x7C97F91B5079A317
     created: 2017-10-29  expires: 2019-04-23  usage: E
ssb  rsa4096/0x88C4DD5D965ABA57
     created: 2017-10-29  expires: 2019-04-23  usage: A
[ultimate] (1). Rick Demo <rick@demo.com>

And then save the changes:

gpg> save

Now export the public key from the offline keyring and import it into your live keyring:

$ gpg --homedir /mnt/tmp/.gnupg --export -a -o rick.demo.pub.asc rick@demo.com
$ gpg --import rick.demo.pub.asc

Check your online keyring to ensure that the change was applied:

$ gpg -K
sec#  rsa4096/0x7CD6ACA4C84DA8B3 2017-10-29 [C] [expires: 2020-10-29]
      Key fingerprint = 4C64 592C 9611 E531 1A81  8824 7CD6 ACA4 C84D A8B3
uid                   [ultimate] Rick Demo <rick@demo.com>
ssb   rsa4096/0x582CBC299B4C2718 2017-10-29 [S] [expires: 2019-04-23]
ssb   rsa4096/0x7C97F91B5079A317 2017-10-29 [E] [expires: 2019-04-23]
ssb   rsa4096/0x88C4DD5D965ABA57 2017-10-29 [A] [expires: 2019-04-23]

After you update your live keyring, don't forget to publish your updated key:

$ gpg --send-key rick@demo.com

And re-export a file copy if you share your key in other ways:

$ gpg --export -a -o rick.demo.pub.asc rick@demo.com

Performing other operations that require the offline private key (like key signing or UID changes) is done in the same way, by using the --homedir option to point to the offline copy. If you make any changes to your own key, such as adding or revoking UIDs, be sure to export your public key from the offline copy and import it into your online keyring. Then publish the changes in the manner in which you normally share your key.

Security vs. convenience

All security brings with it some trade-off of convenience. In the end, it's about finding the middle ground. This GPG key management solution brings with it some small inconvenience of having to manage offline keyrings and encrypted backup disks. But it also provides a much-heightened level of security by ensuring that the private key for your primary key pair (your "master key") remains safely out of reach from those with malicious intent - even if they were to gain access to your computer itself. Any keys that can be accessed are now also more easily replaceable with less impact, thus minimizing the damage and inconvenience a compromise of your keyring might cause.

I hope you have found this guide useful. If you have comments, corrections, or questions, feel free to send me an email. GPG-signed emails get priority handling.

RELATED: If you want to take the security of your GPG keys to the next level by moving them to a cryptographic smartcard, check out Secure your GPG keys with a Yubikey 4.