Git supports using GPG to sign commit records, but GPG is complicated to use. Git started supporting SSH signatures in 2.34, and since we all have SSH keys, it’s time to turn on signatures. But GitHub didn’t support displaying SSH signatures for a long time after this feature was released, so I didn’t push it forward. Yesterday GitHub announced that it officially supports SSH signatures. I’m going to share a little bit about it with you today.
First, why do you need to sign your Git commits? That’s because it’s easy to fake commit identities in Git.
We know that Git requires that the author’s name and email address be specified before committing.
But these two configurations can be filled in as you wish. Anyone can claim to be “taoshu”. To protect my reputation, I can sign my commits with asymmetric cryptography and then publish my public key. This way, other people can verify that the Git changes were submitted by me based on this public key. As long as I don’t reveal my private key, it’s hard for others to pretend to be me and do bad things.
The most common tool for implementing signatures is GPG, and if you’ve played around with Linux, you’re no stranger to the fact that many distributions use GPG as a package manager to sign packages and prevent bad guys from forging them.
However, GPG is not newbie-friendly, so the average Git player rarely turns on signatures. The situation didn’t change until OpenSSH 8.0 was released. This version of OpenSSH supports signing arbitrary data.
The signature feature is broken in OpenSSH 8.7, so it is recommended to use 8.8 or later.
The tool for SSH signing is ssh-keygen, which I guess many people only use when generating SSH keys for the first time, and then never touch it again. It’s a bit strange 🤔 but the signature checking feature is also provided by ssh-keygen.
Suppose there is a file /tmp/a.txt
and we want to sign it using ~/.ssh/id_ed25519
, we can execute the following command.
|
|
The function of each parameter is as follows.
-Y sign
indicates that the signature is calculated-f
specifies the private key-n file
is to assign a type to the signature-file
is defined by us, different types of signatures do not conflict
After execution, we get a signature file /tmp/a.txt.sig
, which looks like this.
|
|
With a signature, we can verify that someone has not tampered with the contents of the file. To verify the signature a list of public keys is needed.
The first column is the identification of the public key, which we use here as the email address. The second column is the public key type, and the part after that is the content of the public key, which is the content of ~/.ssh/id_ed25519.pub
. Each public key occupies one line.
The signature check command is as follows.
|
|
The function of each parameter is as follows.
-Y verify
indicates that the signature is to be verified-f
to specify the public key list file-I
specifies that the public key identifier is to be used-n file
needs to be consistent with the signature-s
specifies the file where the signature is located
Finally, pass the contents of the file to ssh-keygen via redirection, and if the verification passes, you will get the following result.
|
|
If someone modifies the contents of the file, the following results are obtained.
Considering that most Git users have their own SSH keys, wouldn’t it be a waste not to support SSH signing? So Git 2.34 includes SSH signing.
We need to add the following configuration.
|
|
After a bit of work, you’re ready to go. All Git commits will then be signed using SSH. If you don’t have automatic signing turned on, you can temporarily turn it on at commit time with the -s
parameter.
With the default configuration, we don’t see any difference between signed commits and normal commits. If you want to show the signature information, you need to specify the -show-signature
parameter.
|
|
This time we see the Good "git" signatures ...
validation message.
If you are using tig, you should add the same parameters, or turn on the following configuration in ~/.tigrc
.
Recall our previous example, when the value of -n
is file
, the checkmark message is Good "file"...
. Now the message is Good "git"...
, which means that Git is using the -n git
argument.
But there’s a problem: Where does Git store the signature information? The answer is the object corresponding to the commit or tag.
We can use cat-file
to see what the object holds.
|
|
Look at the line gpgsig, which indicates the current submission signature information. Each line after that has a space in front of it, which means that it’s the same line as gpgsig.
So how does Git sign? I didn’t look at the source code. But my guess is that it removes the gpgsig signature information and then calculates a signature for the rest of the content, with a signature type of git.
So I saved the corresponding content as a file and signed it, and it’s exactly the same as the gpgsig value.
Git signatures are a good solution to the problem of disguising your identity. But it’s not cool enough if the corresponding feature doesn’t show up on GitHub. So when Git 2.34 was first released, users suggested that GitHub add support for it. It finally went live yesterday 😄
Although GitHub supports displaying SSH signatures, you need to upload the keys for signing and for authentication separately. You need to specify the type when uploading. Even if you are using the same key, you will have to upload it again.
Once you upload the public key, GitHub will display the corresponding SSH signature.
Reference.
https://www.agwa.name/blog/post/ssh_signatures
https://blog.dbrgn.ch/2021/11/16/git-ssh-signatures/
https://git-scm.com/docs/signature-format
https://taoshu.in/git/ssh-sign.html