This guide walks through best practices of creating a gpg key and signing subkey for the purposes of signing git commits and some brief GitHub instructions.
First, create the primary key with the following config parameters: Ed25519, certify-only, 5-year expiry This primary key is only used to certify/renew subkeys; this key will be moved off the workstation and secured)
$keyUid="Your Name <[email protected]>"
gpg --quick-generate-key $keyUid ed25519 cert 5y
Get the primary key fingerprint (40 hex chars) and copy to variable [string]$primaryFingerprint)
gpg --list-keys --keyid-format=long
$primaryFingerprint = 'put fingerprint here'
This next step will create an Ed25519 signing subkey with a 2-year expiry
gpg --quick-add-key $primaryFingerprint ed25519 sign 2y
❗🔑 Important Security Note - Store these backup files on another device / in a password manager, secrets store, etc)
Export the Primary Secrets and Owner Trust
gpg -a --export-options backup --export-secret-keys $primaryFingerprint > primary-secrets-backup.asc
gpg --export-ownertrust > ownertrust.txt
Export subkeys-only (for daily use)
gpg -a --export-secret-subkeys $primaryFingerprint > subkeys-only.asc
Delete the primary secret key
gpg --delete-secret-keys $primaryFingerprint
Ensure subkeys-only are present (re-import if needed)
gpg --import subkeys-only.asc
Validate that the primary eret key offline; daily machine keeps only signing/encryption subkeys)
Export PRIMARY public key (which includes subkeys) and paste into GitHub
gpg --armor --export $primaryFingerprint
In GitHub, navigate to → Settings → SSH and GPG keys → New GPG key → paste / name it → Save
Find your signing subkey ID (16 characters; the one git will use)
gpg --list-secret-keys --keyid-format=long
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
Pin Git to the signing subkey (add '!' to force that exact subkey ID)
git config --global user.signingkey <SUBKEYID>!
git config --global commit.gpgsign true
On Windows (Gpg4win), ensure Git uses the right gpg.exe
git config --global gpg.program "C:\Program Files (x86)\GnuPG\bin\gpg.exe"
git commit -S -m "Test signed commit"
git log --show-signature -1
🛡️Commits signed this way should now be marked verified on GitHub
This section explains how to renew keys when they expire.
Your primary key shouldn’t live on your everyday device. You need to restore it into the keyring before you can renew the subkey.
Copy your primary-secrets-backup.asc onto your device securely, then import the primary + subkeys locally again:
gpg --import primary-secrets-backup.asc
Add a new signing subkey (Ed25519; extend 2 years)
gpg --quick-add-key <PRIMARY-FPR> ed25519 sign 2y
Export updated public key (now includes the new signing subkey) ---
gpg --armor --export <PRIMARY-FPR>
Paste into GitHub → Settings → SSH and GPG keys → New GPG key
Next, Update Local Git to use the New signing subkey Get the new subkey ID:
gpg --list-secret-keys --keyid-format=long # get NEW_SUBKEY_ID
git config --global user.signingkey <NEW_SUBKEY_ID>!
🔑 SECURITY! Export New Subkey to
Recreate a fresh subkeys-only bundle (optional housekeeping)
gpg -a --export-secret-subkeys <PRIMARY-FPR> > subkeys-only.asc
Delete the primary secret key from this workstation again
gpg --delete-secret-keys <PRIMARY-FPR>
Ensure subkeys-only are imported
gpg --import subkeys-only.asc
Securely delete local backup file once it's secured
Notes:
- Old commits remain verified because their public key stays on GitHub.
- New commits sign with the new subkey and verify as usual.
You do not create a new primary key in normal rotation. You simply extend its expiration, re‑export the public key, and keep operating. Old commits remain verified regardless, as long as the key stays uploaded to GitHub
# --- Bring the primary private key online temporarily (from offline backup) ---
gpg --import primary-secrets-backup.asc
# --- Extend the PRIMARY key expiration (e.g., add another 5 years) ---
gpg --edit-key <PRIMARY-FPR>
# gpg> expire
# (Choose new expiration, e.g., 5y)
# gpg> save
# (This is the standard way to extend key expiry in GnuPG)
# --- Re-export and update public key on GitHub ---
gpg --armor --export <PRIMARY-FPR>
# Paste into GitHub → Settings → SSH and GPG keys → New GPG key
# (Multiple keys are fine; keeping old/expired keys preserves verification)
# --- Re-offline the primary key again (recommended) ---
gpg -a --export-secret-subkeys <PRIMARY-FPR> > subkeys-only.asc
gpg --delete-secret-keys <PRIMARY-FPR>
gpg --import subkeys-only.asc
# Securely remove any temporary copies of your offline backup.
# Notes:
# - No need to change your Git config unless you also rotated the signing subkey.
# - GitHub will continue verifying all old commits (expired keys are fine).