Post

OpenPGP Part3: Create Your Subkeys

In this post we’re going to create our subkeys which will be our daily workhorses for accomplishing security-related tasks such as signing, encrypting, and authenticating to other services. If you landed directly here you may want to check out the first two parts of this series.

Part 2 shows how you can following along at home in Docker.

I am not, and do not claim to be a security expert. I’ve just spent a lot of time researching this and trying to implement best practices. I have made my best effort to ensure this information is accurate and up to date at the time of writing with all references provided. If you find a mistake whether small or egregious please let me know so I can improve this content.

A Brief Note On Subkeys

During my research I found many people ask “why subkeys”? Well, the good folks at Debian have done an incredible job of explaining it so I won’t repeat those keystrokes here. Head on over there and learn about them.

TL;DR Subkeys allow you to have fine-grained control of your keys and capabilities and are easily revoked and changed without affecting your root chain of trust (Primary Key).

Create Signing Subkeys

Signing keys are used for signing…well…pretty much anything. You can sign files, messages, emails, software packages, etc. SecureBoot on the ESP32 requires a signing key to sign the firmware binaries. The signing operation is performed with the private key portion of a key pair which can then be validated by anyone who has the public key portion. It allows you to validate a digital asset originated from who you think it originated from. Here is where the argument of ECC and RSA comes back up. Remember, ECC produces smaller signatures but RSA is more backward compatible with online services. Since these are subkeys we don’t have to choose. Let’s make one of each!

RSA Signing Subkey

1
gpg --edit-key {primary key id}

This will launch you into a gpg command prompt that looks like this:

1
2
3
4
5
6
7
8
9
10
Secret key is available.

sec  rsa4096/FCB1C79A7216CD97
     created: 2024-01-11  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/C1E932AAAE361CD6
     created: 2024-01-11  expires: never       usage: E
[ultimate] (1). Fake McFakerton <me@fake.com>

gpg>

From here you will be entering commands in the gpg prompt to create your subkeys. I’ve annotated it so you can see my selections.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
gpg> addkey <------------------------------ Command to add key
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
  (14) Existing key from card
Your selection? 4 <------------------------ RSA signing key
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096 <---- 4096 bit key size
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 <---------------- 2 year expiration
Key expires at Sun Jan 11 05:22:30 2026 UTC
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/FCB1C79A7216CD97
     created: 2024-01-11  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/C1E932AAAE361CD6
     created: 2024-01-11  expires: never       usage: E
ssb  rsa4096/184C04B07DD988A5
     created: 2024-01-12  expires: 2026-01-11  usage: S <------- Our new RSA key
[ultimate] (1). Fake McFakerton <me@fake.com>

gpg> save <------------------ DON'T FORGET TO SAVE THE CHANGES

During the creation process you will be prompted to enter your Primary Key passphrase to create the subkey. Enter the passphrase you created while generating your Primary Key.

Notice above that we have an additional ssb (secret subkey) entry which indicates it is RSA 4096, expires on January 11th 2026 (two years from now) and has the (S)igning capability. Perfect!

ECC Signing Subkey

To create an ECC signing key we need to add the --expert parameter when we launch gpg. This allows the ECC option to show in the menu of key options.

1
gpg --expert --edit-key {primary key id}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
gpg> addkey <------------------------------ Command to add key
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? 10 <----------------------- ECC signing only
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 <------------------------ Curve 25519 is fast and secure
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 <----------------- 2 year expiration
Key expires at Sun Jan 11 05:32:21 2026 UTC
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/FCB1C79A7216CD97
     created: 2024-01-11  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/C1E932AAAE361CD6
     created: 2024-01-11  expires: never       usage: E
ssb  rsa4096/184C04B07DD988A5
     created: 2024-01-12  expires: 2026-01-11  usage: S
ssb  ed25519/0AD26FEE266F3076
     created: 2024-01-12  expires: 2026-01-11  usage: S <---- Our new ECC key
[ultimate] (1). Fake McFakerton <me@fake.com>

gpg> save <------------------- Don't forget to save your changes

We created the ECC signing key in mostly the same manner. We just change a few options. We’re going to use this subkey later on for signing Git commits. Git supports ECC and the smaller sizes are nice for saving on bandwidth.

You’ll notice at the bottom of the output we now have 2 signing keys. One RSA, and one ECC (ed25519). For the ECC subkey we chose the Curve 25519 elliptic curve which provides 128 bit encryption strength and is good enough for our intended primary use of signing Git commits.1

Create Authentication Subkey

The last type of key we need to create is an authentication subkey which we’ll use for things like SSH. At this point you should be getting familiar with the drill. We open our key for editing in gpg, run addkey and select some options. So let’s do it.

1
gpg --expert --edit-key {primary key id}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
gpg> addkey <------------------------------- Command to add key
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 <------------------------ RSA where we set the capabilities

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 <------------------------ Turn on Authentication

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 <------------------------ Turn off Signing

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 <------------------------ Turn off Encryption

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 <------------------------ Finished setting capabilities
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096 <---- Key size of 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 <----------------- 2 year expiration
Key expires at Sun Jan 11 06:00:54 2026 UTC
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/FCB1C79A7216CD97
     created: 2024-01-11  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa4096/C1E932AAAE361CD6
     created: 2024-01-11  expires: never       usage: E
ssb  rsa4096/184C04B07DD988A5
     created: 2024-01-12  expires: 2026-01-11  usage: S
ssb  ed25519/0AD26FEE266F3076
     created: 2024-01-12  expires: 2026-01-11  usage: S
ssb  rsa4096/586B09EBFAA4A68A
     created: 2024-01-12  expires: 2026-01-11  usage: A <------ Our new subkey
[ultimate] (1). Fake McFakerton <me@fake.com>

gpg> save

The trickiest part about creating the authentication key is toggling the capabilities off. If you’re unsure, examine the output above carefully. We want our authentication subkey to have only the A usage.

Time for a Break

You’ve earned it. While key creation may seem daunting at first I hope now you feel a little more comfortable with it. At this point we have a robust, secure, baseline identity with subkeys for signing, encrypting/decrypting, and authentication. We’re almost ready to start using them. But before we do that we need to make sure they are backed up properly and securely. That’s what we’ll cover next in the series.

References

  1. NIST SP 800-186 contains NIST recommendations for choosing elliptic curves. While Curve 25519 is not recommend for general use it is acknowledged in the document as “included for implementation flexibility” and is good enough for my intended usage of signing Github commits quickly and efficiently 

© Kevin Sidwar

Comments powered by Disqus.