You may find it dirty. I just think it's neat.

Following a recent discussion with friends of mine who have made a habit of exchanging files in chats by encrypting them with GPG (for the fun (yes I have weird friends)) I thought I would make a little tutorial to explain the way that I generate and keep my keys now, because they told me they would be interested in reading that.

Reasoning

The inspiration for this methodology came from another friend whose programming hobbies are on the kind of scale that you need a PGP key to verify your identity (again, I have weird (but very cool) friends). The idea is to view your key as having multiple components:

  • A central private key, which is the key used to certify that your identity/identities is tied to other keys, signatures, etc.
  • Sub-keys, certified with the private one, which are used for the day-to-day operations you perform with gpg.

Side-note: I will do the best to distinguish PGP as a standard for keys, and the gpg implementation. At some point though, I will only talk about how you can generate a similar setup as mine using gpg2 (specifically gpg2 version 2.2.41).

The idea is that your central private key should stay the same as long as possible. You will, of course, keep a revocation certificate on hand to be deployed at any time, in a printed sheet of paper slid in a vault in your home, in your bank, in a bunker 100 kilometers away, and so on. With your main key never changing, you will never change key ID, and, aside from having to reupload your whole key every now and then, you do not have to bother yourself with re-verifying your identities among your web of trust.

To maintain the idea that you should not use keys eternally, notably so that attackers do not have time to attack it, you will make your sub-keys expire after a given period. Sub-keys have capabilities among three possible choices: Authenticate, Encrypt and Sign. When you do gpg --list-keys, those are the meanings of the letters you see in brackets in the core key and the sub-keys.

Generating the Private Key

The beginning of the whole process starts with generating the core key. I am also going to use Ed25519/Cv25519, which is considered to be a more modern standard, needing less data and bits of entropy to remain secure.

By default, gpg does not give you the option to use Ed25519 when you generate keys. To have that option available, as well as tweak key capabilities, you need to add the --expert flag to gpg. In addition, to have the capabilities prompt, we need to use --full-generate-key:

~$ gpg --expert --full-generate-key
gpg (GnuPG) 2.2.41; Copyright (C) 2022 g10 Code GmbH
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

I select elliptic curve algorithms, but with the prompt to set my own capabilities.

The core key should only be able to Certify, and that is what we will change these to:

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

There are actually multiple elliptic curve algorithms, but I tend to prefer Ed25519. The debate as to which one you should chose is raging in the cryptography community. I would honestly recommend you check out people who know these things better than I do for a more informed opinion. If you ask for my reasoning around using Ed25519... I like how it feels under my fingers when I keep typing it on my keyboard. There.

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

As discussed earlier, the core private key should stay valid as long as needed, and only die when you need it revoked for emergency reasons.

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

And then you provide all of the usual information. gpg will ask you for your name, email, comment, etc. Usual stuff. The revocation certificate is stored in a path given in the sea of text at the bottom. Remember to export the private secret of your core private key to a removable medium, etc etc (these are honestly things I do not personally do, but my threat model does not involve actors that powerful).

Adding the Other Sub-Keys

Now, if you try and call gpg --list-keys and check out your ID, you will notice you have your certification core key, and that's it. Of course, this is not very useful. We are going to edit the key to add sub-keys that will let us do everything else we want. Once again, we need the --expert flag or else gpg will not give us the prompts we need.

As an example, I will generate the Authentication key. All of the other keys are created the same way (except for the fact that the other two types of keys have a dedicated prompt so you don't have to "set your own capabilities":

~$ gpg --expert --edit-key ${ID}
[snip]
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? A

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

   (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: 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

The sub-keys would not be eternal. In my setup, I would consider that an authentication key (which you can use if you wire up your GPG agent to serve as an SSH authentication again, something I may spend another blog post explaining) can be replaced every couple years, for example. So I would say that it should expire in two years. Of course, set your own limit based on your threat model and infrastructure.

All other sub-keys would tend to last longer. In my setup, Signing and Encryption keys last 5 years. That is a long time, but, again, not a lot of people are interesting in busting up my stuff.

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 Tue 01 Apr 2025 01:55:05 CEST
Is this correct? (y/N) y
Really create? (y/N) y

Contemplating the Result

With all of the sub-keys created, you should have something like this:

pub   ed25519 2023-03-12 [C]
      <ID>
uid           [ultimate] Lux A. Phifollen (Limefox) <contact@vulpinecitrus.info>
sub   ed25519 2023-03-19 [S] [expires: 2028-03-17]
sub   cv25519 2023-03-19 [E] [expires: 2028-03-17]
sub   ed25519 2023-03-19 [A] [expires: 2025-03-18]

Now, when you need to provide a key for signature and such, you can provide the main ID of the key. gpg will look into the key and find the first sub-key that is capable of performing the operation you want from it (signing, encrypting; authentication is different but that is another blog post for later).

If you happen to have multiple valid sub-keys that perform the same operation at some point, you can disambiguate which one should be used by providing the key-grip (identifier) of the individual sub-key, and not the main key. You can obtain it by listing the keys with --with-keygrip in your options.

And that's it! You can honestly use this key pretty much the way you would use any other. It just feels more convenient, because it completely separates all of the operations you can use it for, giving you both the security that your identity is valid as long as your key is secure, the freedom to renew/destroy individual components, and the comfort of not having to renew your key (until you accidentally publish the private key/password somewhere, for example).

A Small Addendum

But, wait, why are neither of your keys like that-

Shush. I know. It's not even "do as I say, not as I do", it is legitimately just that I generated both of my keys a while back, before I knew as much about gpg/PGP as I do now. They are, in fact, more of a legacy from a period where I mostly ""needed"" (with very heavy quotes) them to sign commits on Gitlab/GitHub. Some day, I will officially retire them and start using a new one, that I already generated with this new methodology. I just have to figure out a couple things regarding user IDs (uids). As it turns out, it's useful at times to have your professional identity untied from the fact you're pretending to be a green anthropomorphic fox on the internet.