Skip to content

Instantly share code, notes, and snippets.

@darianmiller
Last active February 26, 2026 02:31
Show Gist options
  • Select an option

  • Save darianmiller/9de8aeb1979ef2eba9ea6069c669bca1 to your computer and use it in GitHub Desktop.

Select an option

Save darianmiller/9de8aeb1979ef2eba9ea6069c669bca1 to your computer and use it in GitHub Desktop.
Use Powershell for fingerprint parsing. Verified working on Win11
echo Ensure GNU executables are in the path (typically found in C:\Program Files\Git\usr\bin)
echo Use GnuPG on Windows for Code Signing git commits
@echo off
SETLOCAL
rem useful if running batch file from Explorer with hard-coded passphrase
rem cd /d %~dp0
cls
echo SetupSignedCommits.bat
echo https://gist.github.com/darianmiller/9de8aeb1979ef2eba9ea6069c669bca1
echo Created December 5, 2021
echo Related blog article: https://www.ideasawakened.com/post/configure-git-for-signed-commits-on-windows-using-gpg
echo.
echo Author: Darian Miller
echo https://github.com/darianmiller
echo https://www.linkedin.com/in/darianm/
echo.
echo This batch file will attempt to:
echo A) Optionally, run GPG to create a new keypair (RSA 4096-bit key length) for signing purposes
echo Run once to create keypair and then run again in different directory to configure other local repos
echo Alternatively, run once to create keypair and set the Global git config for all your local repos
echo B) Optionally, navigate to GitHub or GitLab so you can paste your public key into their web interface
echo Will also launch notepad containing your exported Public Key cert
echo C) Optionally, configure your git config on your machine
echo This sets your git user info and enables code signing with your new private key on each commit
echo Settings can be configured "Global" for all repositories or "Local" for the current repository
echo NOTE: If updating LOCAL settings, ensure you run this batch file within a repository workspace folder
echo D) Optionally, extend the GPG agent TTL setting
echo Cached passwords used by the GPG agent expire after 10 minutes of inactivity and cached for a max of 2 hours
echo For ease of use, you can extend this to 1-year to prevent having to repeatedly re-input your passphrase
echo.
echo Directions before use:
echo 1. Customize your user information and certificate parameters within this batch file
echo 2. Optionally verify paths to git.exe and gpg.exe (default Git for Windows installs should work as-is)
echo 3. Comment out or delete the next EXIT /B line and save your changes when ready
echo 4. At a command prompt within a git workspace run: SetupSignedCommits.bat your-secret-passphrase-here
EXIT /B
echo.
REM ----------------------
REM 1. CUSTOMIZE YOUR INFO
REM ----------------------
set FirstName=John
set LastName=Smith
set EmailAddress=johnsmith@yourhost.com
set CertComment=For signing Git commits
set CertExpires=0
rem Note: set CertExpires = 0 for no expiration or set to specific number of seconds (or optionally use days/weeks/months/years designator)
rem #d= days, #w = weeks, #m = months, #y = years. Example: 180 means the cert will expire in 180 seconds, 1y means 1 year
rem Assume passphrase is first parameter
IF ["%~1"]==[""] (
echo.
echo Usage: SetupSignedCommits.bat passphrase
echo.
echo Please enter your passphrase
EXIT /B
)
rem If desired, remove the IF empty check block above and hard-code password here
rem Also note: passphrase is only used when creating new keypair
set Passphrase=%1
REM ----------------
REM 2. VERIFY PATHS
REM ----------------
rem if you have a custom install, set default locations manually. If git/gpg is in the system PATH, simply remove the path designation.
rem Download and install if missing: https://gitforwindows.org/
rem You can test at a command prompt by typing in the path configured here with a version parameter:
rem "%PROGRAMFILES%\Git\usr\bin\gpg.exe" --version
rem "%PROGRAMFILES%\Git\bin\git.exe" --version
SET GPGCommand="%PROGRAMFILES%\Git\usr\bin\gpg.exe"
if NOT EXIST %GPGCommand% (
echo Could not find gpg.exe, terminating
exit /b
)
SET GITCommand="%PROGRAMFILES%\Git\bin\git.exe"
if NOT EXIST %GITCommand% (
echo Could not find git.exe, terminating
exit /b
)
REM -----------------------------------------
REM 2b. DISABLE KEYBOXD IF PRESENT (GPG 2.4+)
REM -----------------------------------------
rem GPG 2.4.1+ writes "use-keyboxd" into %USERPROFILE%\.gnupg\common.conf when it
rem first initializes the .gnupg directory. keyboxd.exe is NOT included in the GPG
rem bundled with Git for Windows, so any keyring operation immediately fails with:
rem "gpg: error running keyboxd: probably not installed"
rem
rem The solution is to comment out "use-keyboxd" in common.conf before key generation.
rem However, common.conf is only created the first time GPG initializes .gnupg, which
rem would normally happen during key generation in section A - too late to intervene.
rem
rem We solve this by running a harmless --list-keys call RIGHT NOW to force GPG to
rem initialize the .gnupg directory and write common.conf immediately. We then patch
rem the file before any key generation occurs.
rem
rem IMPORTANT: If a previous run of this script already failed with the keyboxd error,
rem delete the .gnupg directory before running again so this section starts clean:
rem rmdir /s /q "%USERPROFILE%\.gnupg"
echo Initializing GPG home directory...
%GPGCommand% --list-keys >nul 2>&1
SET CommonConf="%USERPROFILE%\.gnupg\common.conf"
IF EXIST %CommonConf% (
findstr /C:"use-keyboxd" %CommonConf% >nul 2>&1
IF NOT ERRORLEVEL 1 (
echo Disabling keyboxd in common.conf (keyboxd is not included in Git for Windows bundled GPG^)...
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"(Get-Content '%USERPROFILE%\.gnupg\common.conf') -replace '^\s*use-keyboxd\s*$','#use-keyboxd' | Set-Content -Encoding ASCII '%USERPROFILE%\.gnupg\common.conf'"
echo Done. GPG will now use the traditional pubring.kbx file-based keyring.
echo.
)
)
REM -------------------------------------
REM A. OPTIONALLY GENERATE A NEW KEYPAIR
REM -------------------------------------
CHOICE /C YN /M "Create a new keypair for %EmailAddress%?"
IF ERRORLEVEL 2 GOTO NavigateOption
rem Changed: use "default" usage instead of "sign" to ensure both a primary signing key
rem and an encryption subkey are created. This is consistent across all GPG 2.1+ versions.
rem Using "sign" alone suppresses subkey creation in GPG 2.3+ which could cause an empty
rem public key export. "default" is the documented stable interface for this purpose.
%GPGCommand% --batch --pinentry-mode=loopback --passphrase "%Passphrase%" --quick-generate-key "%FirstName% %LastName% (%CertComment%) <%EmailAddress%>" rsa4096 default %CertExpires%
REM -----------------------
REM B. OPTIONALLY NAVIGATE
REM -----------------------
rem CHOICE /C HLO returns 1 for H, 2 for L, 3 for O.
rem Checks must be in descending order because IF ERRORLEVEL n means >= n.
:NavigateOption
CHOICE /N /C HLO /M "Adding new Public Key to GitHub, GitLab, or Other? [Enter H, L, or O]: "
IF ERRORLEVEL 3 GOTO SkipPublicKeyExport
IF ERRORLEVEL 2 GOTO GitLab
IF ERRORLEVEL 1 GOTO GitHub
GOTO SkipPublicKeyExport
:GitHub
rem Auto-navigate to github add GPG key page - Sign on to github as needed and then paste the public key and hit the Add gpg key button
start https://github.com/settings/gpg/new
rem Consider enabling vigilant mode
rem start https://github.com/settings/keys
GOTO ExportPublicKey
:GitLab
start https://gitlab.com/-/profile/gpg_keys
GOTO ExportPublicKey
:ExportPublicKey
set TempPKFileName=%TEMP%\MyNewPublicKey\_%RANDOM%.txt
rem GPG 2.4+ no longer creates intermediate directories implicitly when writing output
rem files, so we must ensure the directory exists before calling --export.
mkdir "%TEMP%\MyNewPublicKey" >nul 2>&1
%GPGCommand% -ao %TempPKFileName% --export %EmailAddress%
start notepad.exe %TempPKFileName%
echo.
echo Copy your PUBLIC KEY now shown in Notepad and paste into the website to add a new GPG key to your account.
echo If this temp file is empty, ensure you are using the same GPG installation that created the key.
echo Verify that "%PROGRAMFILES%\Git\usr\bin\gpg.exe" is the GPG you used, with no competing Gpg4win install
echo overriding your GNUPGHOME environment variable.
echo.
rem pause, wait to delete temp file export
pause
del %TempPKFileName%
:SkipPublicKeyExport
REM ----------------------------
REM C. OPTIONALLY CONFIGURE GIT
REM ----------------------------
rem CHOICE /C GLS returns 1 for G, 2 for L, 3 for S.
rem Checks must be in descending order because IF ERRORLEVEL n means >= n.
CHOICE /N /C GLS /M "Configure Global, Local, or Skip updating your Git config? [Enter G, L, or S]: "
IF ERRORLEVEL 3 GOTO ConfigureGitFinished
IF ERRORLEVEL 2 GOTO ConfigureLocalGit
IF ERRORLEVEL 1 GOTO ConfigureGlobalGit
GOTO ConfigureGitFinished
:ConfigureGlobalGit
SET GITScope=--global
GOTO ConfigureGit
:ConfigureLocalGit
SET GITScope=--local
GOTO ConfigureGit
:ConfigureGit
%GITCommand% config %GITScope% user.name "%FirstName% %LastName%"
%GITCommand% config %GITScope% user.email %EmailAddress%
rem tell Git where to find GPG so it can sign commits
%GITCommand% config %GITScope% gpg.program %GPGCommand%
rem ask Git to sign all commits
%GITCommand% config %GITScope% commit.gpgsign true
rem Extract the public key fingerprint to configure Git for commit signing.
rem
rem We write GPG's --with-colons output to a temp file first, then use PowerShell
rem to parse it. This avoids the nested quoting and pipe issues that occur when
rem trying to use for /F with a piped command containing a path with spaces
rem (e.g. "C:\Program Files\...").
rem
rem The fpr: line in --with-colons output is colon-delimited with the 40-character
rem fingerprint always in the 10th field (index 9). We take the first fpr: line
rem only (the primary key) and ignore any subsequent ones (subkeys).
rem
rem Example --with-colons --fingerprint output:
rem pub:u:4096:1:8F660347585C99DA:1638593497:::u:::scSC::::::23::0:
rem fpr:::::::::07024D220A0BE54695F3A4808F660347585C99DA:
rem uid:u::::1638593497::DAF799...::John Smith <johnsmith@null.com>::::::::::0:
rem sub:u:4096:1:...
rem fpr:::::::::...subkey fingerprint...:
set TempGPGOut=%TEMP%\gpgout_%RANDOM%.txt
%GPGCommand% --with-colons --fingerprint %EmailAddress% > "%TempGPGOut%"
set keysignature=
for /F %%i in ('powershell -NoProfile -Command "Get-Content \"%TempGPGOut%\" | Where-Object { $_ -match 'fpr' } | ForEach-Object { $_.Split(':')[9] } | Select-Object -First 1"') do set "keysignature=%%i"
del "%TempGPGOut%"
rem configure Git to use the key just created
%GITCommand% config %GITScope% user.signingkey %keysignature%
echo Signing key configured: %keysignature%
rem view Global git settings if desired:
rem notepad "%USERPROFILE%\.gitconfig"
GOTO ConfigureGitFinished
:ConfigureGitFinished
REM -------------------------
REM D. OPTIONALLY EXTEND TTL
REM -------------------------
rem don't change existing settings if any found
SET AgentConfigFile="%USERPROFILE%\.gnupg\gpg-agent.conf"
IF NOT EXIST %AgentConfigFile% (
rem by default you will be asked to re-input your password after 10 minutes of inactivity, or at least every 2 hours... extend this as desired
rem CHOICE /C YN returns 1 for Y and 2 for N.
rem Checks must be in descending order because IF ERRORLEVEL n means >= n.
CHOICE /C YN /M "Extend default gpg-agent timeout? "
IF ERRORLEVEL 2 GOTO AgentFinished
IF ERRORLEVEL 1 GOTO ConfigureAgent
GOTO AgentFinished
)
:ConfigureAgent
rem default-cache-ttl max age in seconds of cached passphrase (timer is reset on each use) Default is 600 seconds
rem max-cache-ttl max age in seconds of cached passphrase regardless of use (force re-entry of passhprase on next use) Default is 7200 seconds
rem https://www.gnupg.org/documentation/manuals/gnupg/Agent-Options.html
(
echo default-cache-ttl 31536000
echo max-cache-ttl 31536000
) > %AgentConfigFile%
rem to view current settings:
rem gpgconf --list-options gpg-agent
rem
rem technically, should reload the agent if it's running so it gets new config values:
rem gpgconf.exe --reload gpg-agent
:AgentFinished
REM -------------
REM E. ALL DONE!
REM -------------
rem for verifying current list of keys found in the ".gnupg" folder in your user homepath
echo.
echo Current GPG keys for %EmailAddress%:
%GPGCommand% --list-keys %EmailAddress%
rem Useful for testing purposes - delete new keyring after creating it
rem pause
rem %GPGCommand% --delete-secret-keys %EmailAddress%
rem %GPGCommand% --delete-key %EmailAddress%
rem for more info:
rem gpg commands https://www.gnupg.org/documentation/manuals/gnupg/Operational-GPG-Commands.html
rem GitHub Vigilant mode changelog: https://github.blog/changelog/2021-04-28-flag-unsigned-commits-with-vigilant-mode/
rem GitHub help page: https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification
rem GitLab help page: https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/index.html
rem GitHub desktop currently does not support signing: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-tags
rem Azure DevOps currently not supported in display: https://github.com/MicrosoftDocs/azure-devops-docs/issues/1381 also https://developercommunity.visualstudio.com/t/can-we-have-signed-commits/486953
echo Process complete!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment