Signing Git Commits Using S/MIME x509 Certificates
Because of my work with the DoD and their PKI environment, I’ve become accustomed to using S/MIME, or x509 certificates for nearly everything that requires some sort of encryption and digital signatures.
GitLab has a very friendly guide on signing with x509 certs, but will summarize here.
Import Private Keys
Linux
On Linux, import your S/MIME private key into the gpgsm
keychain and let git
know about this key.
1gpgsm --import myPrivateKey.pfx
2
3# enter decryption password (if password protected)
4# enter a password to protect upon every signing attempt
5
6signingkey=$( gpgsm --list-secret-keys | egrep '(key usage|ID)' | grep -B 1 digitalSignature | awk '/ID/ {print $2}' )
7git config --global user.signingkey $signingkey
8git config --global gpg.format x509
Windows
If using WSL, just follow the above instructions. Otherwise, install the private key by double-clicking the .pfx
private key file, use smimesign
, and let git
know it exists.
Install smimesign
:
1choco install smimesign
Install the private key and check if Windows and smimesign
can see it:
1PS C:\Users\user> smimesign --list-keys
2 ID: c961e80210a9abb335fcb07d12b95279e98bec3b
3 S/N: 2efcadea455508fcc3aa4e248343891e
4Algorithm: SHA256-RSA
5 Validity: 2020-09-06 22:16:26 +0000 UTC - 2021-09-06 22:16:26 +0000 UTC
6 Issuer: CN=Actalis Client Authentication CA G3,O=Actalis S.p.A.,L=Ponte San Pietro,ST=Bergamo,C=IT
7 Subject: CN=########@####
8 Emails: #######@####, ########@####
9
10 ID: 3cfe348cfa5a5e50988140c34deabaa182f3ff13
11 S/N: 650000004cd458d5200b177b0c00000000004c
12Algorithm: SHA256-RSA
13 Validity: 2020-09-14 21:13:03 +0000 UTC - 2022-09-14 21:23:03 +0000 UTC
14 Issuer: CN=
15 Subject: CN=Andrew Tec,OU=Users+OU=Users
16 Emails:
17
18 ID: 2e40d9d6b835f0d2da93cbc87c0098a3508742d4
19 S/N: 4c891642042d877f8b4f3449f1fd401a
20Algorithm: SHA256-RSA
21 Validity: 2019-09-05 00:00:00 +0000 UTC - 2021-09-04 23:59:59 +0000 UTC
22 Issuer: CN=Sectigo RSA Client Authentication and Secure Email CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB
23 Subject:
24 Emails: #######@######.com, ######@######.com
25
26 ID: 10e2816286ccf19ff775a7193d63aa40b8e51a72
27 S/N: 971c337bb967a32a4d5a408a3e604379
28Algorithm: SHA256-RSA
29 Validity: 2021-09-01 00:00:00 +0000 UTC - 2023-09-01 23:59:59 +0000 UTC
30 Issuer: CN=Sectigo RSA Client Authentication and Secure Email CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB
31 Subject:
32 Emails: ######@#####.com, #####@#####.com
33
34 ID: 03273015b548733da94ab4f82606c8572f1fe605
35 S/N: 650000008888cb5826339aab42000000000088
36Algorithm: SHA256-RSA
37 Validity: 2021-06-28 17:45:07 +0000 UTC - 2023-06-28 17:55:07 +0000 UTC
38 Issuer: CN=TrabusNet-TRABUSSERV-CA
39 Subject: CN=Andrew Tec\, Dev,OU=Users+OU=Admins+OU=Wireless Local Admin
40 Emails:
Copy over the value of ID:
of the cert you want to sign with into your global config file:
git config --global user.signingkey <VALUE_OF_ID>
Finally, let git
know to use smimesign
for x509 certs.
1git config --global gpg.x509.program smimesign
2git config --global gpg.format x509
## Sign Git Commits
If everything is setup properly signing commits should just work by using the `-S` flag, or `-s` when signing tags and merges.
```bash
git commit -S -m "my commit message"
git log --show-signature
commit 01df67b4c8a75fb2c15c58b9002a178cebeba99e (HEAD -> branch, origin/branch)
gpgsm: Signature made 2021-09-30 19:22:46 using certificate ID 0xB8E51A72
gpgsm: Note: non-critical certificate policy not allowed
gpgsm: Note: non-critical certificate policy not allowed
gpgsm: Note: non-critical certificate policy not allowed
gpgsm: CRLs not checked due to --disable-crl-checks option
gpgsm: Good signature from "/EMail=#####@#####.com"
gpgsm: aka "######@#####.com"
Author: Andrew Tec <#####@#####.com>
Date: Thu Sep 30 19:22:46 2021 +0000
my commit message
commit e3e34e2558ca61ed0
If using GitLab, it should show up with a [VERIFIED] icon.
Errors
If you run into any odd errors when signing, you can prepend GIT_TRACE=1
to the git command. It should show you any subsequent gpgsm
command it was trying to invoke.
1atec@pc:workspace/$ GIT_TRACE=1 git commit -S -m "my message"
219:22:46.665646 git.c:455 trace: built-in: git commit -S -m 'update build script'
319:22:46.668998 run-command.c:666 trace: run_command: gpgsm --status-fd=2 -bsau 0xB8E51A72
419:22:47.018986 run-command.c:666 trace: run_command: git maintenance run --auto --no-quiet
519:22:47.022070 git.c:455 trace: built-in: git maintenance run --auto --no-quiet
In my case, I ran into 2 odd errors. gpgsm: error creating signature: Line too long <GPG Agent>
and gpgsm: no CRL found for certificate
.
Line to long Error
The first was caused by a malformed~/.gnupg/trustlist.txt
file. I simply deleted it and upon reattempt it prompted me to fix itself. It will mark the key as trusted within the ~/.gnupg/trustlist.txt
file.
Ref: https://www.schmut.com/cheat-sheets/s-mime-key-management
CRL Error
The second error was solved by adding the --disable-crl-checks
flag. You can make this flag permanent by adding the flag to the ~/.gnupg/gpgsm.conf
file and restarting gpg-agent
.
1echo "disable-crl-checks" > ~/.gnupg/gpgsm.conf
2gpgconf --reload gpg-agent
For more information on gpgsm
flags and the configuration file you can view the man page man gpgsm
or reference Oracle’s help site: https://docs.oracle.com/cd/E86824_01/html/E54763/gpgsm-1.html
Pinentry Errors
There are some systems where pinentry is installed, but gpg
doesn’t know it exists. These errors are seen as ioctl for device <Pinentry>
Errors or No pinentry <GPG Agent>
You designate a pinentry program with:
1echo "pinentry-program /usr/bin/pinentry" >> ~/.gnupg/gpg-agent.conf
2gpgconf --reload gpg-agent
If on macOS, brew install pinentry-mac
to install the program then designate $(brew --prefix)/bin/pinentry-mac
.
And ensure the terminal TTY environment always has the current gpg configurations by adding the following to your .bashrc
(or .zshrc
) file.
1"GPG_TTY=$(tty)"
2"export GPG_TTY"
3source ~/.bashrc
If correctly setup, you should see this: