Setup PGP with Git (Auto-sign Commits)

Setup GPG/PGP to automatically sign Git commits and tags.


Generate GPG Key for Git

 1# Generate GPG key (use same email as Git)
 2gpg --full-generate-key
 3
 4# Select:
 5# 1. RSA and RSA (or Ed25519)
 6# 4096 bits (for RSA)
 7# 2y expiration
 8# Your name
 9# Your Git email
10# Passphrase
11
12# Or quick generation
13gpg --quick-generate-key "Your Name <git@email.com>" rsa4096 sign 2y

List GPG Keys

 1# List keys
 2gpg --list-secret-keys --keyid-format=long
 3
 4# Output:
 5# /home/user/.gnupg/pubring.kbx
 6# -------------------------------
 7# sec   rsa4096/ABCD1234EFGH5678 2024-01-01 [SC] [expires: 2026-01-01]
 8#       1234567890ABCDEF1234567890ABCDEF12345678
 9# uid                 [ultimate] Your Name <git@email.com>
10# ssb   rsa4096/1234567890ABCDEF 2024-01-01 [E] [expires: 2026-01-01]
11
12# The key ID is: ABCD1234EFGH5678

Configure Git to Use GPG

 1# Set GPG key for Git
 2git config --global user.signingkey ABCD1234EFGH5678
 3
 4# Enable auto-signing for commits
 5git config --global commit.gpgsign true
 6
 7# Enable auto-signing for tags
 8git config --global tag.gpgsign true
 9
10# Set GPG program (if needed)
11git config --global gpg.program gpg
12
13# For GPG2
14git config --global gpg.program gpg2

Configure GPG Agent

 1# Add to ~/.bashrc or ~/.zshrc
 2export GPG_TTY=$(tty)
 3
 4# For longer cache time, edit ~/.gnupg/gpg-agent.conf
 5default-cache-ttl 3600
 6max-cache-ttl 86400
 7
 8# Restart GPG agent
 9gpgconf --kill gpg-agent
10gpg-agent --daemon

Sign Commits

 1# Sign a commit (if auto-sign disabled)
 2git commit -S -m "Signed commit message"
 3
 4# Sign all commits (if auto-sign enabled)
 5git commit -m "This will be signed automatically"
 6
 7# Verify commit signature
 8git log --show-signature
 9
10# Verify specific commit
11git verify-commit HEAD
12
13# Show commit with signature
14git show --show-signature HEAD

Sign Tags

 1# Create signed tag
 2git tag -s v1.0.0 -m "Version 1.0.0"
 3
 4# Create signed annotated tag (if auto-sign enabled)
 5git tag -a v1.0.0 -m "Version 1.0.0"
 6
 7# Verify tag signature
 8git tag -v v1.0.0
 9
10# Show tag with signature
11git show v1.0.0

GitHub Integration

 1# Export public key
 2gpg --armor --export ABCD1234EFGH5678
 3
 4# Copy output and add to GitHub:
 5# 1. Go to GitHub Settings
 6# 2. SSH and GPG keys
 7# 3. New GPG key
 8# 4. Paste public key
 9# 5. Add GPG key
10
11# Verify on GitHub
12# Commits will show "Verified" badge

GitLab Integration

 1# Export public key
 2gpg --armor --export ABCD1234EFGH5678
 3
 4# Add to GitLab:
 5# 1. Go to GitLab Settings
 6# 2. GPG Keys
 7# 3. Add new key
 8# 4. Paste public key
 9# 5. Add key
10
11# Verify on GitLab
12# Commits will show "Verified" badge

Gitea/Forgejo Integration

1# Export public key
2gpg --armor --export ABCD1234EFGH5678
3
4# Add to Gitea/Forgejo:
5# 1. Go to Settings
6# 2. SSH / GPG Keys
7# 3. Add GPG Key
8# 4. Paste public key
9# 5. Add Key

Sign Previous Commits

 1# Sign last commit
 2git commit --amend --no-edit -S
 3
 4# Sign multiple commits (interactive rebase)
 5git rebase -i HEAD~5
 6
 7# In editor, change 'pick' to 'edit' for commits to sign
 8# Then for each commit:
 9git commit --amend --no-edit -S
10git rebase --continue
11
12# Force push (if already pushed)
13git push --force-with-lease

Verify Repository Commits

 1# Verify all commits
 2git log --show-signature
 3
 4# Verify commits in range
 5git log --show-signature HEAD~10..HEAD
 6
 7# Show only signed commits
 8git log --show-signature | grep -B 5 "Good signature"
 9
10# Show only unsigned commits
11git log --pretty=format:"%H %an %s" | while read commit; do
12    if ! git verify-commit $(echo $commit | cut -d' ' -f1) 2>/dev/null; then
13        echo "Unsigned: $commit"
14    fi
15done

Git Config File

 1# View Git config
 2cat ~/.gitconfig
 3
 4# Example configuration:
 5[user]
 6    name = Your Name
 7    email = git@email.com
 8    signingkey = ABCD1234EFGH5678
 9
10[commit]
11    gpgsign = true
12
13[tag]
14    gpgsign = true
15
16[gpg]
17    program = gpg

Troubleshooting

"gpg: signing failed: Inappropriate ioctl for device"

1# Fix: Export GPG_TTY
2export GPG_TTY=$(tty)
3
4# Add to ~/.bashrc
5echo 'export GPG_TTY=$(tty)' >> ~/.bashrc

"gpg: signing failed: No secret key"

1# Check if key exists
2gpg --list-secret-keys --keyid-format=long
3
4# Check Git config
5git config user.signingkey
6
7# Set correct key
8git config --global user.signingkey YOUR_KEY_ID

"gpg: signing failed: Operation cancelled"

1# GPG agent issue - restart
2gpgconf --kill gpg-agent
3gpg-agent --daemon
4
5# Or use pinentry-tty
6echo "pinentry-program /usr/bin/pinentry-tty" >> ~/.gnupg/gpg-agent.conf
7gpgconf --kill gpg-agent

"error: gpg failed to sign the data"

 1# Test GPG
 2echo "test" | gpg --clear-sign
 3
 4# Check GPG agent
 5ps aux | grep gpg-agent
 6
 7# Restart GPG agent
 8gpgconf --kill gpg-agent
 9gpg-agent --daemon
10
11# Check Git GPG program
12git config --global gpg.program gpg

Multiple Keys

 1# Use different keys for different repos
 2cd /path/to/repo
 3git config user.signingkey DIFFERENT_KEY_ID
 4
 5# Use different keys per directory
 6# In ~/.gitconfig:
 7[includeIf "gitdir:~/work/"]
 8    path = ~/.gitconfig-work
 9
10# In ~/.gitconfig-work:
11[user]
12    email = work@company.com
13    signingkey = WORK_KEY_ID

Batch Sign Commits

 1#!/bin/bash
 2# sign-commits.sh - Sign all commits in a branch
 3
 4BRANCH=${1:-main}
 5BASE=${2:-origin/main}
 6
 7# Get list of commits
 8commits=$(git log --pretty=format:"%H" $BASE..$BRANCH)
 9
10# Sign each commit
11for commit in $commits; do
12    echo "Signing $commit"
13    GIT_EDITOR=true git rebase --exec "git commit --amend --no-edit -S" $commit^
14done
15
16echo "All commits signed!"

Pre-commit Hook

 1# .git/hooks/pre-commit
 2#!/bin/bash
 3
 4# Ensure GPG_TTY is set
 5export GPG_TTY=$(tty)
 6
 7# Check if commit will be signed
 8if ! git config --get commit.gpgsign | grep -q true; then
 9    echo "Warning: commit.gpgsign is not enabled"
10    echo "Enable with: git config commit.gpgsign true"
11    exit 1
12fi
13
14# Test GPG signing
15if ! echo "test" | gpg --clear-sign &>/dev/null; then
16    echo "Error: GPG signing failed"
17    echo "Check your GPG configuration"
18    exit 1
19fi
20
21exit 0
1# Make executable
2chmod +x .git/hooks/pre-commit

CI/CD Integration

 1# GitHub Actions
 2name: Verify Signatures
 3
 4on: [push, pull_request]
 5
 6jobs:
 7  verify:
 8    runs-on: ubuntu-latest
 9    steps:
10      - uses: actions/checkout@v3
11        with:
12          fetch-depth: 0
13      
14      - name: Import GPG keys
15        run: |
16          # Import trusted keys
17          gpg --import trusted-keys.asc          
18      
19      - name: Verify commits
20        run: |
21          # Verify all commits in PR
22          for commit in $(git log --pretty=format:"%H" origin/main..HEAD); do
23            if ! git verify-commit $commit; then
24              echo "Commit $commit is not signed!"
25              exit 1
26            fi
27          done          

Best Practices

  1. Key Management:

    • Use separate key for Git signing
    • Set expiration date (2 years)
    • Backup private key securely
    • Create revocation certificate
  2. Signing:

    • Enable auto-signing globally
    • Sign all commits and tags
    • Verify signatures before merging
    • Document signing policy in CONTRIBUTING.md
  3. Team Setup:

    • Share public keys via keyserver
    • Maintain list of trusted keys
    • Verify fingerprints in person
    • Use signed tags for releases
  4. Security:

    • Use strong passphrase
    • Configure GPG agent timeout
    • Don't share private keys
    • Revoke compromised keys immediately

Related Snippets