How to sign your git commits
TL;DR The integrity of the software supply chain can be compromised if a trusted person's identity is spoofed and potentially malicious code is committed to a repository. Signing git commits enables you to distinguish between verified and unverified changes made to a GitHub repository by using cryptographic keys to attest identity. This blog post walks you through the steps to enable this feature.
Why sign Git commits?
When pushing commits to GitHub, you have to provide both a username and an email address that represents you when committing to a repository. Whether this is your actual GitHub account or not, GitHub doesn't know the difference. You can easily overwrite these details set up on your local computer using:
$ git config --global user.name "<Name>"
$ git config --global user.email "<Email Address>"
The reason behind being able to do this is so that it's possible to protect repositories on other channels, it doesn't have to carry all the data of the old accounts accounting for the previous commits. For example if someone were to delete an account, what would happen to the commit information.
If anyone can use any email address and any name, then how do you know who is really committing to your repositories? If someone is able to push a change with malicious content to a repository under a name of a regular contributor, the chances are high that the change could be missed and not questioned.
By signing your commits, other contributors can see, using the verification button that it was you committing, and not someone pretending to be you. When you have your key set up in Github, there is a verification button that will attest that it is really you committing. Below are the steps to help you get a GPG key set up with Github.
An example of when there was nearly a massive malware attack was discovered by Stephan Lacy on Twitter, it is written about here which explains how the attacker was using spoofing of real people's Github accounts to commit malicious code to repositories, thousands of repos were hit by this.
To prove that you are the committer on git we can use GNU Privacy Guard (GnuPG). GnupG is a free tool which allows you to encrypt and sign your data using keys. It can be freely used and is distributed under the terms of the GNU General Public License. GnuPG is an implementation of the OpenPGP standard which is an email encryption standard. You can use GnuPG to create a GPG keys. GPG keys are used to verify that the origin of data or communication is genuine and then creates a digital signature. They create a key pair, a public key and a private key, that can be used with Github for the encryption and signing commits.
In GitHub under Settings then SSH and GPG keys you'll see a setting called 'Vigilant mode' which flags unsigned commits as unverified. This means you can clearly see who is making verified commits and who isn't. If your whole team sign their commits, then a commit that isn't signed can be easily spotted amongst them and red flags could be raised.
There are a few different methods to sign keys. The first, by using GPG keys, the oldest and most commonly used method. There are tools currently being developed to make this an easier process, without the need for GPG or SSH keys, like the project gitsign: a feature of the project sigstore. Whilst this method reduces the need to have keys to sign commits, it is not yet recognised by GitHub as a trusted verification, so commits won't show as verified.
Recently, Git released a new feature allowing you to sign your commits using your SSH key, which has shown to be popular as more people have SSH keys than GPG keys. There were many problems when setting up GPG keys and getting them to run inside an IDE such as Visual Studio Code. It is possible, however it is much easier to use GPG keys as they are much simpler to set up and use.
Listed below is the manual method to setting up your own GPG keys. Here is a tool to guide you through the process that will make creating and setting up GPG keys much quicker.
Using GPG Keys - Helper tool method
You can download the tool from GitHub repository Endjin.GitSignCommits here. To run the file, clone the repository locally and open up a PowerShell terminal.
Then run the following command:
./gitsigning.sh
The script will then take you through each command. Where you need to input information there will be a prompt and if specific values are needed, these will be specified in green.
Using GPG Keys - Manually installing
Step 1: Installing GPG
Firstly, you have to install GPG if it is not installed already (gpg --version):
- If you have Git BASH then it should already be installed
- Head to GnuPG to install
Step 2: Check for existing keys
Important note: Create your keys inside Git bash, otherwise there could be trouble with the PATHs to your keys when running the command outside Git Bash.
Use the command below to check to see if you have any existing keys:
$ gpg --list-secret-keys --keyid-format=long
If you have existing keys and you want to use one to sign commits then display the public key using the command below and then you can skip to part 5: (Note: The long key at the end is an example one that you need to sub out for your own)
$ gpg --armor --export HZTX5CQ0Y3WCAI7V
If you don't have any keys run the command below:
$ gpg --full-generate-key
- Select 1 for the first option
- For key size enter 4096
- For how long the key should last should be 0
- And then verify by entering
y
Then you need to add your:
- Name
- Email address (the one that you have registered to sign into GitHub)
- A comment (optional)
- Enter
o
to confirm
It will then ask you for a passphrase to protect the key. Make sure to remember it! It will ask you for this every time you commit.
Step 3: Seeing the keys
Now that the keys are created we can see them using:
$ gpg --list-secret-keys --keyid-format=long
And the output will look like this:
$ gpg --list-secret-keys --keyid-format=long
/Users/hubot/.gnupg/secring.gpg
------------------------------------
sec 4096R/HZTX5CQ0Y3WCAI7V 2016-06-13 [expires: 2017-03-10]
uid Test
ssb 4096R/KATQK38JUIHHBH29 2016-06-13
Step 4: Exporting the keys
Then using your GPG Key ID (which in the example above is HZTX5CQ0Y3WCAI7V) paste it into this line:
$ gpg --armor --export HZTX5CQ0Y3WCAI7V
Or you could save it to a file and read it from there instead:
$ gpg --export --armor HZTX5CQ0Y3WCAI7V > ./gpg-key.pub
You can back up your secret keys:
$ gpg --export-secret-keys --armor NIDC7OOPDJ84JIYDMS7QYK8VPJPJNKMWVFS5N1E0 > ./gpg-key.asc
Step 5: Linking to GitHub
Now we need to link it to Github. To do this we need to use the result of the of the armor
command. Copy your GPG key, beginning with -----BEGIN PGP PUBLIC KEY BLOCK-----
and ending with -----END PGP PUBLIC KEY BLOCK-----
Go to settings in Github and then SSH and GPG Keys and create a new GPG Key. Insert the GPG key, including the lines that show the beginning and the end of the key block.
Now we need to tell Git about the GPG signing key. Using your own GPG key ID instead of the example one, run the line below
$ git config --global user.signingkey HZTX5CQ0Y3WCAI7V
Step 6: Enabling Git sign
To enable Git signing:
git config --global commit.gpgsign true
Step 7: Signing a commit
To sign a commit, you git commit as usual but it should show up with the window to enter in your passphrase. It also should work if committing using the buttons within an IDE (for example VS Code).
Troubleshooting
If you run into a problem that looks something like this:
gpg: skipped "HZTX5CQ0Y3WCAI7V": No secret key
gpg: signing failed: No secret key
error: gpg failed to sign the data
fatal: failed to write commit object
If this is the case, you may need to recreate your key in Git Bash, as it can't find the path to your key.
You can check if GPG is in your PATH by going to environment variables and checking if it's in your system variables path. If not then add C:\Program Files\GnuPG
as it may not be able to find your GPG program.
If you aren't going to be developing in a dev container you can just point the .config file in your user files point straight to the gpg folder, e.g:
git config --global gpg.program "/c/Program Files (x86)/GnuPG/bin/gpg.exe"