GPG Subkeys for signing on less trusted devices

We can use GPG keys to sign and encrypt data. This type of public key cryptography is commonly used to encrypt emails, sign documents and verify the source of code contributions in shared codebases. My public key is published at keyserver.ubuntu.com. This allows someone to download my public key and encrypt data that can only be decrypted with my private key.

The security of supply chains in the delivery of software has become an increased focus of late for the CNCF, NIST, and Github. Github have added more features around signing and verifying commits to repositories with more prominent badges and extra levels for verification status.

I use my GPG key to sign commits in my personal projects in Git, and my work codebases. I’ve got my GPG private key on my personal desktop computer, which I assign more trust than a laptop, primarily because a laptop is more likely to be physically taken. I could create a new key pair but I would need to start building that identities reputation again.

Subkeys are bound to the primary key pair and can be used for signing or encryption. Most importantly they can be revoked and stored seperately from the primary keys.

Create a Subkey

This assumes you’ve already got a GPG key, but if not this Debian guide to creating a new GPG key should help.

First get your long key ID, for your user identity. Mine is [email protected].

1$ gpg --list-keys --keyid-format=long [email protected]
2pub   rsa2048/203FDC0917F7655A 2015-08-25 [SCEA]

Now we can edit the key, add create a new subkey.

1gpg --edit-key 203FDC0917F7655A

Now we can use the gpg> prompt to edit our key.

Type addkey. Choose RSA (sign only).

Choose 4096 for the bit key size.

Now choose an expiry date, I chose 1y - 1 year.

The key is created now, and we should save our changes.

Type save. This should exit the prompt.

We can list our keys again and should see another key, at the bottom with a letter [S] for signing (instead of encryption), and the expiry date we chose.

1sub   rsa4096/BEB988A256E69F67 2022-10-18 [S] [expires: 2023-10-18]

Export the Subkey

We’ve got our key, and we want to export only this key to our less trusted device. This will output the key to a file secret-subkeys.

The exclamation point exports only the subkey ID you specify.

1gpg --output secret-subkeys --export-secret-subkeys SUBKEYID!

Import the Subkey

Once you’ve transferred the subkey you can import the key.

1gpg --import secret-subkeys

Now we can list the keys in our keyring for our identity.

1$ gpg --list-secret-keys --keyid-format=long [email protected]
2sec#  rsa2048/203FDC0917F7655A 2015-08-25 [SCEA]
3      EACE86059CF7C15A3FFF16A2203FDC0917F7655A
4uid                 [ unknown] Harry Hodge <[email protected]>
5...
6ssb   rsa4096/BEB988A156E69F67 2022-10-18 [S] [expires: 2023-10-18]

You can see the primary key line starts sec# which indicates the secret key material is not present. We’ve only got our signing key! Now we can sign our commits for Github. Don’t forget to configure git to sign with your new key

1git config --global user.signingkey 203FDC0917F7655A

Add the Key to Github

Export the key in a format we can use on Github. I am exporting my key with ID 203FDC0917F7655A. This is the public key for my primary key. Since it’s linked to my subkeys, any commits signed with any of the subkeys will be marked verified in Github.

1gpg --armor --export 203FDC0917F7655A

We can add the output to our Github account.