Some notes on signing commits with SMIME and x509. Part of an investigation into why commits showed as unverified in GitLab.
My client-side setup is based on a discussion in a GitLab product issue and the GitLab Development Kit notes.
The purpose of this post is to add fixes and troubleshooting steps that I found helpful when it didn’t work.
Edit: in a follow-up post, I create a test CA and use that to generate verified commits.
Self-signed certificates (from the issue thread) proved to be a dead end; the certificate (details) were missing off the signed commit. One of the tests GitLab does to verify commits is that the signature has the certificate included.
Viewing git configuration
Git Client configuration changes are needed. Since I was testing this, I wanted to isolate these changes to the test repository, so I put the settings in “local” – .git/config. View the visible config with:
git config --list --show-origin --show-scope
On RHEL9 flavours:
dnf install gnupg2-smime pinentry
The first provides gpgsm, the second fixes:
$ gpgsm --import codesigning.p12 gpgsm: directory '/home/ben/.gnupg' created gpgsm: keybox '/home/ben/.gnupg/pubring.kbx' created gpgsm: total number processed: 0 gpgsm: error importing certificate: No pinentry
gpgsm: error at “bag.encryptedData”
The procedure involves creating a PKCS#12 / PFX file with the private keys and certificates insisw. I found an apparent compatibility issue between the supplied RHEL9 versions:
OpenSSL 3.0.7 1 Nov 2022 (Library: OpenSSL 3.0.7 1 Nov 2022) gpgsm (GnuPG) 2.3.3 libgcrypt 1.10.0-unknown libksba 1.5.1 Supported algorithms: Cipher: 3DES, AES128, AES192, AES256, SERPENT128, SERPENT192, SERPENT256, SEED, CAMELLIA128, CAMELLIA192, CAMELLIA256 Pubkey: RSA, ECC, ECC Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224, WHIRLPOOL
OpenSSL creates the PFX file:
# no password is set for testing purposes. Don't do this for real. openssl pkcs12 -export -inkey private.key -in public.crt \ -out codesigning.p12 -passin pass: -passout pass:
Importing it into GPG fails:
gpgsm: encryptedData error at "pkcs5PBES2-params", offset 123 gpgsm: error at "bag.encryptedData", offset 49 gpgsm: error parsing or decrypting the PKCS#12 file gpgsm: total number processed: 0
This probably isn’t the right fix, but it unblocked me – create the PFX file with -legacy:
openssl pkcs12 -export -inkey private.key -in public.crt \ -out codesigning.p12 -legacy -passin pass: -passout pass:
List imported keys:
gpgsm --list-keys
How Git uses gpgsm
When trying to sign commits, get more of an idea about what’s going on with git tracing:
$ GIT_TRACE=1 git commit -a -m 'x509 test commit' 13:19:26.059161 git.c:460 trace: built-in: git commit -a -m 'x509 test commit' 13:19:26.060550 run-command.c:655 trace: run_command: gpgsm --status-fd=2 -bsau 0xB77E3069 error: gpg failed to sign the data fatal: failed to write commit object
The gpgsm command is key to both viewing and creating signatures.
The man page for gpgsm does not document arguments –status-fd, -b, -s.
These are actually switches for the default gpg signing command, but Git doesn’t change its behaviour when the signing command is changed. This is .. odd.
The gpgsm command there specifies what key to use to sign with (this comes from one of the Git config steps) and you can run this direct:
If you’re set up correctly, it hangs because it’s expecting input.
$ gpgsm --status-fd=2 -bsau 0xB77E3069 gpgsm: CRLs not checked due to --disable-crl-checks option
root certificate is not marked trusted
If you’ve missed the step of trusting the issuer it doesn’t hang:
$ gpgsm --status-fd=2 -bsau 0xEF49BCD0 gpgsm: root certificate is not marked trusted gpgsm: fingerprint=C3:7F:F7:10:59:5D:68:A2:7C:2A:CB:71:51:11:DF:81:EF:49:BC:D0 gpgsm: DBG: BEGIN Certificate 'issuer': gpgsm: DBG: serial: 4C0F22F2E7AD1AC9D7BBA333E891047849770D59 gpgsm: DBG: notBefore: 2024-01-04 11:59:42 gpgsm: DBG: notAfter: 2025-01-03 11:59:42 gpgsm: DBG: issuer: CN=codesigning gpgsm: DBG: subject: CN=codesigning gpgsm: DBG: hash algo: 1.2.840.113549.1.1.11 gpgsm: DBG: SHA1 Fingerprint: C3:7F:F7:10:59:5D:68:A2:7C:2A:CB:71:51:11:DF:81:EF:49:BC:D0 gpgsm: DBG: END Certificate gpgsm: after checking the fingerprint, you may want to add it manually to the list of trusted certificates. gpgsm: can't sign using '0xEF49BCD0': Not trusted [GNUPG:] INV_SGNR 10 0xEF49BCD0 [GNUPG:] INV_RECP 10 0xEF49BCD0
Do this usually with:
gpgsm --import /path/to/foo.crt
Refer also to the linked documentation and populate ~/.gnupg/trustlist.txt.
Reload gpg agent a lot
If you import a certificate OR change the trust list, it won’t work right away.
You’ll just keep getting ‘Not trusted’ errors.
gpgconf --reload gpg-agent
Test that signing is working outside of Git
To check what it’s sending back to Git, do a simple test using the command git is using. If you’re not getting signatures, it might be because there’s a failure that Git isn’t reporting back.
$ echo foo | gpgsm --status-fd=2 -bsau 0xB77E3069 gpgsm: CRLs not checked due to --disable-crl-checks option [GNUPG:] SIG_CREATED D 1 8 00 20240124T193847 91416CA9C1AA6955C1BF444F8A111C69B77E3069 gpgsm: signature created -----BEGIN SIGNED MESSAGE----- <snip> -----END SIGNED MESSAGE-----
Git Trace is your friend
Additional output is also provided when showing signatures. I can’t see an obvious way to make use of the gpgsm command here, as you’d need the contents of the temporary file. But at least you can see what’s going on under the hood.
$ GIT_TRACE=1 git log --show-signature 12:07:57.566707 git.c:460 trace: built-in: git log --show-signature 12:07:57.568838 run-command.c:655 trace: run_command: unset GIT_PAGER_IN_USE; LESS=FRX LV=-c less 12:07:57.570666 run-command.c:655 trace: run_command: gpgsm --status-fd=1 --verify /tmp/.git_vtag_tmpW9ZDWl - commit b4c1a221e3f2e3a1134bb29c5bf6a12b639a5503 (HEAD -> signtest1) gpgsm: Signature made 2024-01-24 12:06:42 UTC gpgsm: using rsa2048 key C37FF710595D68A27C2ACB715111DF81EF49BCD0 gpgsm: CRLs not checked due to --disable-crl-checks option gpgsm: Good signature from "/CN=codesigning" gpgsm: aka "john.doe@example.com" Author: John Doe <john.doe@example.com> Date: Wed Jan 24 12:06:42 2024 +0000 five
Leave a comment