Signing Git Commits with GPG and SSH Keys
Set up GPG and SSH commit signing for Git. Verify commit authenticity, configure GitHub verified badges, and enforce signed commits across your team.
What you'll learn
- ✓Why commit signing matters for security and trust
- ✓Setting up GPG key generation and Git configuration
- ✓Using SSH keys for simpler commit signing
- ✓Adding your key to GitHub for verified badges
- ✓Enforcing signed commits across a team
Prerequisites
- •Basic Git workflow (commit, push)
- •Command line familiarity
- •A GitHub, GitLab, or Bitbucket account
Why Sign Your Commits
Git identifies authors by name and email, both of which are self-reported. Anyone can configure Git to use your name and email:
git config user.name "Linus Torvalds"
git config user.email "torvalds@linux-foundation.org"
git commit -m "totally legit commit"
This commit shows up in git log as if Linus wrote it. There is no built-in verification. Commit signing solves this by cryptographically proving that you — the holder of the private key — authored the commit. Signed commits display a “Verified” badge on GitHub, GitLab, and Bitbucket.
abc1234 Verified feat: add payment processing Alice (signed with GPG)
def5678 fix: update dependencies Bob (unsigned)
ghi9012 Verified feat: add user dashboard Carol (signed with SSH) Option 1: GPG Signing
GPG (GNU Privacy Guard) is the traditional method for commit signing. It uses public-key cryptography: you sign with your private key, and anyone can verify with your public key.
Step 1: Install GPG
# macOS
brew install gnupg
# Ubuntu/Debian
sudo apt install gnupg
# Windows (comes with Git for Windows, or install Gpg4win)
winget install GnuPG.GnuPG
Step 2: Generate a GPG key
gpg --full-generate-key
When prompted:
- Key type: RSA and RSA (default)
- Key size: 4096 bits
- Expiration: Choose based on your security policy (1-2 years is common, or 0 for no expiry)
- Name and email: Must match your Git config and GitHub account email
gpg: key 3AA5C34371567BD2 marked as ultimately trusted
public and secret key created and signed.
pub rsa4096 2026-07-08 [SC]
AB12CD34EF56GH78IJ90KL12MN34OP56QR78ST90
uid Alice Developer <alice@example.com>
sub rsa4096 2026-07-08 [E]
Step 3: Get your GPG key ID
gpg --list-secret-keys --keyid-format=long
Output:
sec rsa4096/3AA5C34371567BD2 2026-07-08 [SC]
AB12CD34EF56GH78IJ90KL12MN34OP56QR78ST90
uid [ultimate] Alice Developer <alice@example.com>
ssb rsa4096/1234567890ABCDEF 2026-07-08 [E]
Your key ID is the part after rsa4096/: 3AA5C34371567BD2.
Step 4: Configure Git to use your GPG key
# Set the signing key
git config --global user.signingkey 3AA5C34371567BD2
# Enable signing for all commits
git config --global commit.gpgsign true
# Enable signing for all tags
git config --global tag.gpgsign true
Step 5: Export your public key for GitHub
gpg --armor --export 3AA5C34371567BD2
This outputs your public key in ASCII format. Copy the entire output including the -----BEGIN PGP PUBLIC KEY BLOCK----- and -----END PGP PUBLIC KEY BLOCK----- lines.
Step 6: Add the key to GitHub
- Go to Settings > SSH and GPG keys > New GPG key.
- Paste your public key.
- Click Add GPG key.
Making a signed commit
If you enabled commit.gpgsign, all commits are signed automatically:
git commit -m "feat: add payment processing"
Or sign a specific commit manually:
git commit -S -m "feat: add payment processing"
Verifying a signed commit
git log --show-signature -1
Output:
commit abc1234def5678 (HEAD -> main)
gpg: Signature made Tue Jul 8 10:30:00 2026 CDT
gpg: using RSA key AB12CD34EF56GH78IJ90KL12MN34OP56QR78ST90
gpg: Good signature from "Alice Developer <alice@example.com>" [ultimate]
Author: Alice Developer <alice@example.com>
Date: Tue Jul 8 10:30:00 2026 -0500
feat: add payment processing
Troubleshooting GPG
“gpg failed to sign the data”:
This usually means the GPG agent cannot prompt for your passphrase. Fix it by setting the GPG_TTY variable:
# Add to ~/.bashrc or ~/.zshrc
export GPG_TTY=$(tty)
On macOS, install and configure pinentry-mac:
brew install pinentry-mac
echo "pinentry-program $(which pinentry-mac)" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent
Key expired:
# Edit the key to extend expiration
gpg --edit-key 3AA5C34371567BD2
gpg> expire
# Follow prompts to set new expiration
gpg> save
Option 2: SSH Signing (Simpler)
Since Git 2.34, you can sign commits with SSH keys. This is simpler than GPG because most developers already have SSH keys for pushing to GitHub.
Step 1: Check your Git version
git --version
# Must be 2.34 or later
Step 2: Use an existing SSH key or generate one
# Generate a new Ed25519 key (recommended)
ssh-keygen -t ed25519 -C "alice@example.com"
# Or use an existing key at ~/.ssh/id_ed25519.pub
Step 3: Configure Git for SSH signing
# Tell Git to use SSH for signing
git config --global gpg.format ssh
# Point to your SSH key
git config --global user.signingkey ~/.ssh/id_ed25519.pub
# Enable signing for all commits
git config --global commit.gpgsign true
# Enable signing for all tags
git config --global tag.gpgsign true
Step 4: Add the key to GitHub as a signing key
- Go to Settings > SSH and GPG keys > New SSH key.
- Set Key type to Signing Key (not Authentication Key).
- Paste the contents of
~/.ssh/id_ed25519.pub. - Click Add SSH key.
You can use the same SSH key for both authentication and signing, but you need to add it twice: once as an Authentication Key and once as a Signing Key.
Step 5: Set up local verification
To verify SSH signatures locally, create an allowed signers file:
# Create the allowed signers file
echo "alice@example.com $(cat ~/.ssh/id_ed25519.pub)" > ~/.ssh/allowed_signers
# Tell Git about it
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
Add your team members’ public keys to the file:
alice@example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... alice@example.com
bob@example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... bob@example.com
Verifying SSH-signed commits
git log --show-signature -1
Output:
commit abc1234def5678 (HEAD -> main)
Good "git" signature for alice@example.com with ED25519 key SHA256:abc123...
Author: Alice Developer <alice@example.com>
Date: Tue Jul 8 10:30:00 2026 -0500
feat: add payment processing
Signing Tags
Signed tags provide verified release points:
# GPG-signed tag
git tag -s v2.4.0 -m "Release 2.4.0"
# Verify a signed tag
git tag -v v2.4.0
With SSH signing configured, tags are signed with your SSH key automatically:
git tag -s v2.4.0 -m "Release 2.4.0"
GPG vs SSH Signing: Which to Choose
| Factor | GPG | SSH |
|---|---|---|
| Setup complexity | Higher (key generation, agent config) | Lower (reuse existing SSH key) |
| Key management | Rich (subkeys, expiry, revocation) | Simple (one key file) |
| Web of trust | Supported (key signing parties) | Not supported |
| Git version required | Any | 2.34+ |
| GitHub support | Full | Full |
| Best for | Security-sensitive projects, open source | Most teams and individual developers |
Recommendation: Use SSH signing unless you need GPG’s advanced key management features. SSH signing is simpler to set up, simpler to maintain, and uses keys developers already have.
Enforcing Signed Commits
Branch protection on GitHub
- Go to Settings > Branches > Branch protection rules.
- Select the branch (e.g.,
main). - Enable Require signed commits.
Now all commits pushed to the protected branch must have a verified signature.
Pre-receive hook (self-hosted Git)
For self-hosted Git servers, use a pre-receive hook:
#!/bin/bash
# pre-receive hook: reject unsigned commits
while read oldrev newrev refname; do
if [ "$oldrev" = "0000000000000000000000000000000000000000" ]; then
commits=$(git rev-list "$newrev")
else
commits=$(git rev-list "$oldrev..$newrev")
fi
for commit in $commits; do
sig=$(git verify-commit "$commit" 2>&1)
if [ $? -ne 0 ]; then
echo "ERROR: Commit $commit is not signed."
echo "Please sign your commits with: git commit -S"
exit 1
fi
done
done
Team onboarding script
Create a script that sets up signing for new team members:
#!/bin/bash
# setup-signing.sh
echo "Setting up SSH commit signing..."
# Check Git version
GIT_VERSION=$(git --version | awk '{print $3}')
echo "Git version: $GIT_VERSION"
# Configure SSH signing
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
git config --global tag.gpgsign true
echo ""
echo "Done! Your commits will now be signed with your SSH key."
echo ""
echo "Next steps:"
echo "1. Go to https://github.com/settings/keys"
echo "2. Click 'New SSH key'"
echo "3. Set type to 'Signing Key'"
echo "4. Paste your public key:"
echo ""
cat ~/.ssh/id_ed25519.pub
Verifying Commits in CI
Add signature verification to your CI pipeline:
# GitHub Actions
- name: Verify commit signatures
run: |
git log --format='%H %G?' origin/main..HEAD | while read hash status; do
if [ "$status" != "G" ] && [ "$status" != "E" ]; then
echo "Unsigned or unverified commit: $hash"
exit 1
fi
done
The %G? format shows signature status:
G— Good signatureB— Bad signatureU— Good signature with unknown validityE— Signature cannot be checked (e.g., expired key)N— No signature
Wrap-Up
Commit signing adds a layer of trust to your Git history. It proves that commits come from who they claim to come from, not just someone who configured the right name and email. SSH signing is the easiest path forward for most teams: reuse your existing SSH key, add a few Git config lines, and upload the key to GitHub as a signing key. Enable “Require signed commits” on your protected branches, and every commit in your main history is verified. Start with SSH signing today — it takes five minutes and the security benefit is immediate.
Related articles
- Linux SSH Tunneling and Port Forwarding Explained
Learn how to use SSH local, remote, and dynamic port forwarding to securely access services across networks.
- DevOps SSH Keys and Secure Server Access
Generate strong SSH keys, configure your ~/.ssh/config, lock down sshd, and use agent forwarding and jump hosts to access servers safely without passwords.
- Git Advanced Git Rebase Techniques: Interactive, Autosquash, and Rebase Onto
Master interactive rebase, autosquash, fixup commits, and rebase --onto for clean Git history. Advanced techniques for rewriting, reorganizing, and cleaning up commits.
- Git Finding Bugs with Git Bisect: A Practical Guide
Use git bisect to binary search through your commit history and pinpoint the exact commit that broke your code. Covers manual, automated, and advanced bisect workflows.