Introduction
“Hey, Stefan, can you please push your changes to the Git repository?” - asked my colleague.
“and make sure you sign your commits with GPG!” - he added.
“Sure, I will do that!” - I replied. But wait a minute, what is GPG signing? And why should I use it?
PGP (Pretty Good Privacy
) was initially developed by Phil Zimmermann in 1991 as a commercial product and was designed
as a way to encrypt and decrypt email messages. In 1997, it was released as an open-source standard called OpenPGP
and
since then the OpenPGP became industry standard specification that was implemented by many software vendors, with the most
popular implementation being GPG which stands for GNU Privacy Guard
. GPG became popular mainly because it was free
and open-source, had a community fixing bugs, it was available on multiple platforms (Linux, Windows, MacOS, etc.) and
supported a range of cryptographic operations (encryption, decryption, signing, verification, key management, etc.)
Today, we will use GPG for signing Git commits and tags in Git, so that we can verify that the commit or tag was made by you and not someone else. So to say that the code was not tampered with. This is a great way of making sure that the code you are working on is not modified by someone else without your knowledge.
How GPG keys work
GPG uses a public-key cryptography, which means that you have a pair of keys: a public key and a private key.
The public key can:
- encrypt the data, that means that by using public key you can lock the data.
- can verify signatures signed by the private key
The public key is meant to be shared with others in order for them to encrypt the message/data/signature. The public key can’t decrypt (unlock) the message back that was encrypted by the very same public key. In fact nobody except the private key can do that.
The private key:
- can decrypt (unlock) the data encrypted by public key.
- is kept secret by owner and should never be shared with anyone.
- can sign the data/message.
For our purpose, like signing git commits, we will use the private key to sign the commit and the public key to verify the signature of commits and we will not send encrypted data. The signature is nothing else than a hash of the commit encrypted by the private key. So when you commit you changes to the Git repository, only the signature (hash of the changes in commit) is added to the commit by you. The public key should be provided to Git Server, so that Git Server can verify the signature. If the signature is valid, then it means that the commit was made by you and not someone else. And if the signature provided in git commit doesn’t match the signature generated by the public key on the Git server, then it means that the commit was modified by someone else, and therefore it is not valid. Let’s see how it works in practice with pseudocode:
dev = Developer()
git_server = GitServer()
gnupg = GnuPG()
(private_key, public_key) = gnupg.generate_gpg_key_pair()
dev.set_private_key(private_key)
dev.send_to(git_server).key(public_key)
git_server.set_public_key(public_key)
commit = dev.commit("Hello World")
signature = dev.sign(commit) // "abc123"
dev.send_to(git_server).commit(commit).signature(signature)
calculated_signature = git_server.get_signature(commit) // "abc123"
if calculated_signature == received_signature:
git_server.accept(commit)
else:
git_server.reject(commit)
Generating GPG keys
To use GPG on Ubuntu 22.04, you need to install gnupg
package with the following command:
sudo apt install gnupg
after that we are ready to generate a new GPG key with command
gpg --full-generate-key
The cli will ask you a few questions
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)
Select 1 for RSA and RSA (default)
as it is the most popular option
What keysize do you want? (2048)
Select 4096
because it is the most secure option
Key is valid for? (0)
Select 1y
(it means that the key will expire in 1 year)
Is this correct?
Type y
and press Enter
You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"
Real name:
Type your name and press Enter
Email address:
Type your email address and press Enter
Comment:
Type your comment and press Enter
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?
Type O
and press Enter
You need a Passphrase to protect your secret key.
Enter passphrase:
Type your passphrase and press Enter
. This passphrase is used to protect your private key, and you will be asked to
provide it every time you will sign or encrypt anything. So make sure that it is strong enough.
Repeat passphrase:
Type your passphrase again and press Enter
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.
Move your mouse randomly and then type y
and press Enter
gpg: key XXXXXXXX marked as ultimately trusted
This means that you trust this key to be used for signing
gpg: revocation certificate stored as '/home/user/.gnupg/openpgp-revocs.d/XXXXXXXX.rev'
This means that the revocation certificate was generated successfully
public and secret key created and signed.
This means that the public and private keys were generated successfully. Great, now we have a GPG key pair. Let’s see our GPG keys:
gpg --list-secret-keys --keyid-format=long
The output should look like this:
/home/stefan/.gnupg/pubring.kbx
-------------------------------
sec rsa4096/EF1438B66E632694 2023-11-29 [SC] [expires: 2024-11-28]
9951D14964BE4BE94BB09192EF1438B66E632694
uid [ultimate] Stefan Kecskes (GPG is cool) <info@skey.uk>
ssb rsa4096/ED96BCFBE0D4584B 2023-11-29 [E] [expires: 2024-11-28]
Exporting GPG public key
Amazing, our key is ready waiting for us to export it. To get the GPG public key I will need the KEY_ID
, which is
the part after /
from the sec
line, so in my case it is EF1438B66E632694
. Now we can export the public key with
the following command:
gpg --armor --export EF1438B66E632694
The output will be the public key in ASCII format, which can be shared with others and also added to GitHub, GitLab, etc.
To set GPG key on GitHub, you need to go to GitHub website and into Settings
-> SSH and GPG keys
-> New GPG key
and paste the public key that you exported.
GUI for GPG
There is also a graphical user interface for GPG, that is called Seahorse. Seahorse is a graphical GNOME application
for managing encryption keys (SSH, GPG, etc.) and passwords. We will not use it in this blog post, but it is good to
know that it exists. so if you prefer that, you can install seahorse
package with the following command:
sudo apt install seahorse
Note: seahorse is installed by default on Ubuntu. It is called “Passwords and Keys” in Ubuntu.
Transfer GPG keys to another machine
The private key is stored in ~/.gnupg/private-keys-v1.d/
folder and should never be shared with anyone, but you might
want to use the same GPG key on another machine, so that you can commit changes to git from your laptop and desktop and
the Git server will still be able to validate your commit signatures. To do that, you need to export the private key
with the following command:
gpg --export-secret-keys --armor EF1438B66E632694 > private.key
and then import it on another machine with the following command:
gpg --import private.key
Set GPG key to work with Git
There are two ways of setting GPG key to work with Git. The first way is to set it globally and the second way is to set it per repository.
In most cases you will have only one GPG key, so you can set it globally with the following command:
git config --global gpg.program gpg
git config --global commit.gpgsign true
git config --global user.signingkey EF1438B66E632694
The first command will set the GPG program to gpg
and the second command will enable GPG signing for every repository
on your computer and the third command will set the GPG key that should be used by Git client on your computer globally.
This is helpful, so that you don’t have to set it manually for every repository, you don’t have to remember the GPG
key ID and you also don’t have to add -S
flag to every git commit
command. After that, your GPG key will be
automatically used to sign commits and tags.
If you have multiple GPG keys in your system, then you can override the global setting with GPG key per repository. I use it all the time because I work on multiple projects and I have different GPG keys for work and personal projects. My personal GPG key is set globally and I override it for work projects. To do that, you need to go into the work repository folder and set the GPG key per repository with the following command:
git config user.signingkey YOUR_WORK_GPG_KEY_ID
See, that this time we didn’t use --global
flag, so the GPG key will be applied to .gitconfig
for this repository
only. This setting is actually stored in .git/config
file in the repository folder, and you can edit the file
manually if you want to or use the above command to set it. I prefer to use the above command, because it is less
error-prone.
If we wouldn’t set the commit.gpgsign
to true
, we would have to use the -S
flag with every git commit
command,
like this:
git commit -S -m "My commit message"
git tag -s v1.0.0 -m "My tag message"
but because we set commit.gpgsign
to true
, we don’t have to use -S
flag to sign commits, and we continue to use
git commit
and git tag
commands as usual.
If you forgot to sign the last commit, you can sign it with the following command:
git commit --amend -S --no-edit
and if you already pushed it to the remote repository, you will need to force push it with the following command:
git push --force
But be careful with force pushing, because it can cause problems to your colleagues who already pulled the changes from the remote repository. Please, avoid force pushing if possible.
Conclusion
That’s it for today. In this blog post we learned how to generate GPG keys, how to export/import them and how to use them with Git. I hope that you found this blog post useful and that you will start using GPG keys for signing your commits and tags. Thank you for reading and if you have any questions, please send me a message.