Assuring the authenticity of work submitted to GitHub has become increasingly important. One of the common policies that organizations have used to secure the commits made by developers has been to require the use of GPG signatures that are embedded into the Git commits.
Both GitHub and Git have long natively supported cryptography signed comments:
When commits are signed by each of their respective authors it is much harder for an attacker to successfully pull-off an impersonation attack.
My Experience
When I was asked to follow Nautilus policy of having GPG signatures for commits, I followed the GitHub and Git guidelines blind without putting much thought into it. Later after some internal discussion from my colleagues, it became evident that there are some additional aspects to be considered when using GPG for Git, GitHub or any other use.
In this post I will walk you through:
- How the default GPG keys are set up when you create the
- Why this practice can be improved
- Recommended Best practices
- How to do this
- How to use them with Git or GitHub
- Some other recommendations (expiration date, key rotation, etc.)
GPG Keys
Like all asymmetric cryptographic keys, GPG keys are made in two parts: “Private Key”, and the “Public Key” (that is derived from the Private Key).
With GPG, the common practice is to generate a set of keys that are grouped together with an extensive set of meta-data into a so-called OpenPGP key.
An OpenPGP key typically consists of:
- Keys
- Primary Key (Certify, and optionally other capabilities)
- Supplementary Keys (Any of: Authentication, Signing, Encrypting)
- User-ID [Name, Email, Comment, etc]
- Primary IDs
- Additional IDs
- Key Capabilities, signed metadata that is included in the public key, are listed in the brackets.
- All Keys can be set with expiry dates.
- Sub-Keys and User-ID can be independently revoked or retired
- If the primary key is Revoked, then entire OpenPGP Key is considered compromised.
GPG Defaults
There are many arrangement and possible combinations of keys, sub-keys, user-id’s and so on. When you use GPG to generate your keys, by default it generates your keys following a standard template:
- Keys
- Primary Key (Certify and Signing)
- Supplementary Key (Encrypting)
- User-ID
- Primary ID (Name, Email, and Comment)
You can notice that the primary key has been set with the dual-capabilities of Certifying (to make new supplementary keys) and Signing (such as signing a Git commit).
This basis structure was chosen upon the thought that the keys used for Encryption need to be (or at least should be) rotated regularly, however Signing can remain constant over the lifetime of the OpenPGP Key.
However, in many cases this is not what the user would want if given the choice.
Why is this not optimal?
The default set-up leaves still some space for improvement. This is because it does not take advantage of the possibility to create individual sub-keys for each capability.
The idea is that you essentially disconnect all the rights of your choice from your primary key and just use your sub-keys to avoid using your primary key. The only times you then use your primary is to cancel (revoke) existing sub-keys or to generate new sub-keys.
It is very advanced to separate the primary key from the supplementary keys.
The advantage of this approach is that if any of these sub-keys gets compromised, you can revoke individually and generate a new key, all while keeping your primary key valid.
If you do not do this, you probably will end up someday with your primary key compromised and will have to regenerate a new primary key, etc.
Recommended Best Practices
How to Create Further Sub-Keys
In order to create additional sub-keys, you need to use the GPG command-line interface.
A colleague of mine, Jose Celano wrote a very clear step-by-step guide for internal use in one of our company’s repositories, here.
I base the following summary of steps in the command line interface on his work.
- Type:
gpg --list-keys --fingerprint --with-keygrip --with-subkey-fingerprints
- In the list you get an overview of all the primary key and its existing sub-keys. You will copy the second line of your public key made up of 10 pairs of 4 numbers and or letters.
- Using the noted public key type:
gpg --edit-key <public key 40 digits without spaces>
- You will get a display of their associated private key and a new prompt so type:
addkey
- Select your applicable key, most likely option (4) RSA (sign only)
- It will ask you to specify the keysize duration, I recommend 4096 and “0” for does not expire.
- Confirm the creation.
- You get a new overview of the new secret keys, seeing the newly generated sub-key and the changed rights of the primary key.
- To see the equivalent public keys for export type:
gpg --list-keys --fingerprint --with-keygrip --with-subkey-fingerprints
<public key 40 digits without spaces>
- You should now see the new sub-key and the changed primary key rights.
Removing Primary Key Rights
The last step to finish this is to remove all capabilities except the “certify” capability from the primary key. For this, you will continue using the command line but using the “expert” mode.
- Type:
gpg --expert --edit-key <public key 40 digits without spaces>
- You will get an overview of the primary key’s rights.
- Type:
change-usage
- Use the toggle option taking away the rights for which you already have created the new sub-key.
- Once you are done you get a new overview of the primary key’s rights.
- Type save and you are all set working on the keys.
- Type:
gpg --list-keys --fingerprint --with-keygrip --with-subkey-fingerprints
<public key 40 digits without spaces>
- You will see the new public key rights where you should only see the “c” option for certify at the “pub” key.
Configuring Git with Your New Key
In order to set up your new key for signing your commits you have to follow these steps:
- In the command prompt type:
git config --global --edit
- This will open the git config file in your default editor. In my case it opens it in Visual Code.
- Once here look for the following entry of the signing-key and update it with the last 16 digits of your new signing sub-key.
- Save it.
- If you are using GitHub you will need to export your new public key and import it into it, following the necessary steps as shown in their GitHub Documentation – Signing commits.
Always use a Passphrase
When creating the set of keys you are asked for a passphrase. Set it and remember it or even better write it down somewhere. This is another safety measure but it is essential.
Backing up Your Revocation Certificate
Make sure that you keep a backup of your revocation certificate or that you print it out and store it somewhere safe in case that you were to have to use it.
Rotating Your Encryption Keys
This being one of the most used capabilities. It is recommended that you rotate these keys to prevent anyone to have access to any of your encrypted information, creating for example new keys in events such as computer change, etc. It is important though to back these up in the event that you were to have files encrypted with these.
Setting an Expiration Date
Another good idea is to set an expiration date not too far in the future in case that you were to not be able to revoke your certificate due to having lost your revocation certificate.
Some After Thoughts
In a way using GPG is good for security, but if you work yourself through all these steps to do things properly, I am convinced that you may agree with me that it could be more user friendly.
Things could be made easier especially with the default key setup which could already have all keys separate, and avoid the need to have to do all this.
If this is too advance you can just go back to the basic as in my previous post.