Kleopatra & GPG: Git Commit Signing on Windows
This guide sets up a Windows PC so Git signs every commit with a GPG key managed in Kleopatra, and GitHub marks those commits Verified.
Overview
This guide sets up a Windows PC so Git signs every commit with a GPG key managed in Kleopatra, and GitHub marks those commits Verified.
A signature proves who authored a commit and that it hasn't been altered since. GitHub shows the green badge when the signing key is on your account and the commit's email matches a verified email on that account.
Important: Git for Windows ships its own gpg with a separate keyring, and it is what runs when you type gpg in Git Bash. Kleopatra's keys live in Gpg4win's keyring. Run the gpg commands in this guide from PowerShell or Command Prompt, where Gpg4win's gpg is on the PATH, and point Git at Gpg4win's binary (Part 4).
Prerequisites
- Git for Windows (includes Git Bash).
- Gpg4win from gpg4win.org. It bundles Kleopatra (the key manager), GnuPG (
gpg) and the pinentry passphrase dialog. - A GitHub account with at least one verified email address (Settings → Emails).
- Know which email your commits use: run
git config --global user.email. It must match the email you put on the GPG key.
Part 1 — Generate a GPG key pair in Kleopatra
- Open Kleopatra.
- Go to File → New OpenPGP Key Pair… (older versions: File → New Key Pair → Create a personal OpenPGP key pair).
- Enter your name and the email that matches a verified email on your GitHub account and your Git
user.email. - Tick Protect the generated key with a passphrase.
- Open Advanced Settings and check the key type and expiry:
- Recent Gpg4win versions default to ECC (ed25519 / cv25519), which GitHub supports. Choose RSA 4096 instead only if your organisation requires it.
- Set an expiry date that fits your key-rotation policy (1–2 years is common).
- Click Create (or OK) and enter your passphrase when the pinentry dialog asks.
- Kleopatra generates the key and offers to make a backup. Accept it, or follow Key maintenance below.
Part 2 — Find your key ID and export the public key
Option A — Kleopatra: right-click the key → Export… and save it. Kleopatra writes an ASCII-armored .asc file; open it in Notepad to copy the text.
Option B — command line (PowerShell or Command Prompt, not Git Bash):
gpg --list-secret-keys --keyid-format=long
Look for the sec line, for example:
sec ed25519/3AA5C34371567BD2 2026-10-22 [SC] [expires: 2028-10-21]
3AA5C34371567BD2 is the long key ID (yours will differ). The 40-character fingerprint on the next line also works anywhere a key ID is asked for. Export the public key:
gpg --armor --export 3AA5C34371567BD2
If the list comes back empty, you are almost certainly running Git's bundled gpg instead of Gpg4win's. Check which one runs with where.exe gpg (Part 4.2).
Part 3 — Add the public key to GitHub
- Copy the whole exported block, including the
-----BEGIN PGP PUBLIC KEY BLOCK-----and-----END PGP PUBLIC KEY BLOCK-----lines. - In GitHub go to Settings → SSH and GPG keys → New GPG key.
- Give it a title (e.g. the machine name), paste the block and click Add GPG key.
Only ever paste the public key here. A block that starts with PRIVATE KEY BLOCK must never leave your machine.
Part 4 — Configure Git to sign commits
These git config commands work in Git Bash, PowerShell or Command Prompt.
4.1 Set your identity, key and signing defaults
git config --global user.email "you@example.com"
git config --global user.signingkey 3AA5C34371567BD2
git config --global commit.gpgsign true
git config --global tag.gpgsign true
user.email must match the email on the GPG key. tag.gpgsign is optional and signs annotated tags too.
4.2 Point Git at Gpg4win's gpg
Without this, Git uses its bundled gpg, which cannot see Kleopatra's keys. Find the real path first:
where.exe gpg
Type where.exe, not where. In PowerShell (the Windows 11 Terminal default), where is an alias for Where-Object and prints nothing. where.exe works in both PowerShell and Command Prompt; Get-Command gpg is the PowerShell alternative. If nothing is found, Gpg4win's bin folder isn't on your PATH, so check the folders listed below directly.
Then set the path with forward slashes:
git config --global gpg.program "C:/Program Files (x86)/GnuPG/bin/gpg.exe"
Forward slashes are deliberate. Git for Windows accepts them, they behave the same in every shell, and in .gitconfig a backslash is an escape character: a hand-edited C:\Program Files\... makes Git fail with a bad config error. Backslashes only work doubled (C:\\Program Files\\...), so forward slashes are simpler.
The path depends on your Gpg4win version: older 32-bit releases use C:/Program Files (x86)/GnuPG/bin/gpg.exe, newer 64-bit releases may use C:/Program Files/GnuPG/bin/gpg.exe. Use the path where.exe gpg returned, with its backslashes changed to forward slashes.
4.3 Make a test commit and verify it
git commit --allow-empty -m "Test signed commit"
git log --show-signature -1
The pinentry dialog asks for your passphrase, and git log should report a Good signature. Push, then check the commit on GitHub for the green Verified badge.
With commit.gpgsign true set, every commit is signed automatically; -S is only needed in repos that override it.
Part 5 — Troubleshooting
| Symptom | Likely cause and fix |
|---|---|
gpg failed to sign the data | gpg.program points at the wrong binary, or gpg-agent is stuck. Recheck the path with where.exe gpg, then restart the agent with gpgconf --kill gpg-agent and retry. |
secret key not available / No secret key | Git is using its bundled gpg, or user.signingkey has a typo. Set gpg.program (Part 4.2) and compare the ID with gpg --list-secret-keys --keyid-format=long. |
| Key list is empty in Git Bash | Expected: Git Bash runs Git's own gpg. Use PowerShell or Command Prompt. |
| No passphrase prompt; the commit hangs | Pinentry isn't opening. In PowerShell run echo test | gpg --clearsign to force a prompt, then gpgconf --kill gpg-agent if it still hangs. |
| GitHub shows Unverified | The commit's user.email doesn't match an email on the key, that email isn't verified on GitHub, or the public key isn't on your account. |
| Signing fails after working before | The key has expired. Extend it (Key maintenance) and re-add the public key on GitHub. |
| Works in a terminal but not in VS Code or another IDE | The IDE uses a different Git install or PATH. Make sure gpg.program is set globally, then restart the IDE. |
Part 6 — Setting up a new machine
Either generate a new key per machine (each added to GitHub as an extra key) or reuse one key by importing your backup. A key per machine limits the damage if one machine is lost.
- Install Git for Windows and Gpg4win.
- Get a key onto the machine, either way:
- New key: follow Parts 1–3.
- Existing key: Kleopatra → Import… → select your secret-key backup. When Kleopatra asks, confirm it is your own key so it is fully trusted.
- Set
user.email,user.signingkey,commit.gpgsignandgpg.program(Part 4). - Make a test commit and confirm the Verified badge.
Key maintenance
- Back up the private key. Kleopatra → right-click the key → Backup Secret Keys… (older versions: Export Secret Keys). Store the file offline or in a password manager, never in a repo. Without it you can't sign new commits as this identity and must create and register a new key.
- Keep the revocation certificate. GnuPG creates one automatically in
%APPDATA%\gnupg\openpgp-revocs.d\. Copy it alongside your backup so you can revoke the key if it is ever compromised. - Before expiry, set a calendar reminder. Once the key expires,
gpgrefuses to sign and commits fail. To extend: Kleopatra → right-click → Change Expiry Date…, then delete and re-add the public key on GitHub so it sees the new date. - Old commits stay Verified. GitHub records verification when a commit is pushed, so commits signed while the key was valid keep their badge after it expires.
- #git
- #windows