Setup GPG signing on Windows

Let's setup commit verification using GPG on Windows. This will prompt us to sign the commit. I will be using a passphrase instead of signing the commits with my SSH token. This gives an extra layer of security to the setup.

A Visual Studio Code windows wit some code and a terminal triggering a git commit. A screen has popped up allowing the user to sign the commit using the passphrase.
Showing the GPG signing Windows when a commit is created.

Signed commits will show the green verified label in GitHub:

A list of commits on a repository on GitHub, showing the label of each commit, the user and the time on the left side. On the right side of each commit there is a green verified label, the commit hash and a copy and browse button.
Showing the green verified label on commits that were signed.

What does GPG mean?

GNU Privacy Guard, or GPG, helps with signing messages:

GPG is a collection of tools that allow signing and encrypting of data using asymmetric cryptography (with public / private keys). Git uses GPG to sign and verify commits and tags. With such a signature, you can easily verify that a commit (or tag) was really made by a specific user.

git-tower.com

If you check one of the previous screenshots you'll notice the term OpenPGP, which stands for Open Pretty Good Privacy. GPG is an implementation of OpenPGP:

One of the original implementations of PGP, also called PGP, was fully owned by the PGP Corporation. The latter is now owned by Symantec, which in turn is a part of Broadcom.

However, OpenPGP is now an international standard as defined in the latest RFC 4880 – OpenPGP Message Format.

[…]

On top of the standard and optional OpenPGP featureset, GPG provides several improvements: versatile key management system, frontend applications and libraries, and fully ported to Microsoft Windows as Gpg4win.

Baeldung

Gpg4win installation

We'll be using Gpg4win. You can install Gpg4win by downloading it, or do the install with Chocolatey from an elevated PowerShell:

choco install gpg4win
# if you want to use GPG with dev containers, run:
# gpg-disable-keyboxd

Generate GPG key

The following steps will guide you thought the UI to create a new GPG key:

  1. Start Kleopatra
  2. Click the New Key Pair button
  3. Enter your Name and Email address. Tick the Protect the generated key with a passphrase box.
  4. Optional: disable valid until by clicking the Advanced Settings... and untick the Valid until box. Click OK.
  5. Click OK again.
  6. Enter your passphrase.
  7. Done.

Add GPG key to GitHub

First, we'll export the key to the clipboard:

# get the GPG key
$key=$(
  gpg --list-secret-keys --keyid-format LONG `
  | Select-String -Pattern "ed25519/([^ ]+)" `
  | ForEach-Object { $_.Matches.Groups[1].Value } `
)
 
# export key
gpg --armor --export "$key" | Set-Clipboard; `
echo "Key exported to clipboard"

Now we can add it to our GitHub profile:

  1. Open your keys in your profile on GitHub: https://github.com/settings/profile
  2. Click the New GPG Key button.
  3. Enter a Title for the paste the clipboard into the Key field.
  4. Click Add GPG Key.

GitHub will now understand your signed commits.

Local git configuration

Now, let's configure the local git with the following PowerShell script:

# get the GPG key
$key=$(
  gpg --list-secret-keys --keyid-format LONG `
  | Select-String -Pattern "ed25519/([^ ]+)" `
  | ForEach-Object { $_.Matches.Groups[1].Value }
)

# get the location of gpg
$program=$(Get-Command gpg).Source

# config git
git config --global commit.gpgsign true
git config --global user.signingkey "$key"
git config --global gpg.program "$program"

Your commits on your dev box should now be signed with the private key you just created.

Start GPG agent at logon

I don't know why, but sometimes my GPG agent is not running. So I added a small startup script that triggers the loading of the agent.

$TaskName = "Start GPG"
$CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

# Check if the task already exists
if (Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue) {
    Write-Output "Task '$TaskName' already exists."
} else {
    $Action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument "-NoProfile -WindowStyle Hidden -Command `"echo 'Ensure GPG...'; gpg-connect-agent reloadagent /bye; sleep 5;`""
    # Create a logon trigger for the current user
    $Trigger = New-ScheduledTaskTrigger -AtLogOn -User $CurrentUser

    # Register the task
    Register-ScheduledTask -Action $Action -Trigger $Trigger -TaskName $TaskName -Description "Ensures GPG agent is reloaded at user logon."
}

When the agent is not loaded, you'll be waiting for that popup for a long time before it times out.

Bonus: Visual Studio Code GPG signing

To enable GPG signing in Visual Studio Code, you'll need to set git.enableCommitSigning to true:

  1. Press F1 to open up the all commands view.
  2. Input >Preferences: Open Settings (UI).
  3. Search git.enableCommitSigning.
  4. Enable the Git: Enable Commit Signing to enable the commit signing.

Bonus: Dev Containers

The latest version of Gpg4win will not work with Dev Containers, because they've switched keyrings (more info here). The solution is to migrate back to the "old" keychain with the following command:

gpg-disable-keyboxd

When you get the error "error: cannot run C:\Program Files (x86)\Gpg4win..\GnuPG\bin\gpg.exe: No such file or directory", you can solve this by overwriting the GPG program in your container:

# config local GPG for signing
# fixes:
# - error: cannot run C:\Program Files (x86)\Gpg4win..\GnuPG\bin\gpg.exe: No such file or directory.
git config --global gpg.program gpg

More information on the startup script and dev container can be found here.

Changelog

  • 2024-02-23: added a fix for the new Gpg4win keyring causing Dev Containers not to work.
expand_less