← Back to DevBytes

Git with SSH Keys and GPG Setup

Introduction to Git Authentication and Commit Signing

When working with Git repositories, two critical aspects of your workflow are authentication (proving you have permission to push and pull code) and commit integrity (proving that commits attributed to you actually came from you). This tutorial covers SSH keys for secure, password-free authentication and GPG keys for cryptographically signing your commits. Both are considered industry best practices and are required by many organizations.

Part 1: SSH Keys for Git Authentication

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

What SSH Keys Are and Why They Matter

SSH (Secure Shell) keys are a pair of cryptographic keys—a private key and a public key—that can be used to authenticate with remote Git servers instead of typing a username and password each time. The public key is stored on the remote server (like GitHub, GitLab, or Bitbucket), while the private key stays securely on your local machine. During authentication, the server challenges your client to prove it possesses the private key without ever seeing the key itself. This method is:

Step 1: Check for Existing SSH Keys

Before generating a new key, check whether you already have SSH keys on your machine. Look for files named id_rsa, id_ed25519, id_ecdsa, or similar, with corresponding .pub files.

# List existing SSH keys in the default .ssh directory
ls -la ~/.ssh

# Common output might show:
# id_ed25519     (private key)
# id_ed25519.pub (public key)
# known_hosts    (trusted host fingerprints)

If you see key pairs listed and want to use them, you can skip generation. Otherwise, proceed to create a new key.

Step 2: Generate a New SSH Key Pair

Modern best practice is to use the Ed25519 algorithm, which is faster and more secure than RSA. If your Git provider or system doesn't support Ed25519, fall back to RSA with a 4096-bit key.

# Recommended: Ed25519 (fast, secure, compact)
ssh-keygen -t ed25519 -C "your.email@example.com"

# Alternative: RSA 4096-bit (widely compatible, slower)
ssh-keygen -t rsa -b 4096 -C "your.email@example.com"

When prompted, you can specify a custom file path or accept the default. You will also be asked for an optional passphrase. Always set a strong passphrase on your private key—this adds a layer of protection if your private key file is ever compromised.

# Example full interaction
ssh-keygen -t ed25519 -C "your.email@example.com"
# Generating public/private ed25519 key pair.
# Enter file in which to save the key (/home/you/.ssh/id_ed25519): [Press Enter]
# Enter passphrase (empty for no passphrase): [Type a strong passphrase]
# Enter same passphrase again: [Confirm passphrase]
# Your identification has been saved in /home/you/.ssh/id_ed25519
# Your public key has been saved in /home/you/.ssh/id_ed25519.pub

Step 3: Add Your SSH Key to the SSH Agent

The SSH agent runs in the background and holds your decrypted private keys in memory, so you don't have to type your passphrase every time you use the key. Start the agent and add your key.

# Start the SSH agent (the syntax varies by shell)
eval "$(ssh-agent -s)"

# Add your private key to the agent
ssh-add ~/.ssh/id_ed25519

# Verify the key is loaded
ssh-add -l
# Output: 256 SHA256:abc123... your.email@example.com (ED25519)

On macOS, you can also store the passphrase in the Keychain so it persists across reboots:

# On macOS, add the key with Keychain integration
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

# Or configure this permanently in ~/.ssh/config:
# Host *
#   UseKeychain yes
#   AddKeysToAgent yes

On Linux, you may want to ensure the agent auto-starts. Many distributions already do this. You can add this to your ~/.bashrc or ~/.zshrc:

# Auto-start ssh-agent if not running
if [ -z "$SSH_AUTH_SOCK" ]; then
  eval "$(ssh-agent -s)" > /dev/null
fi

Step 4: Add Your Public Key to Your Git Provider

Copy your public key to the clipboard, then add it to your Git hosting service.

# Display your public key for copying
cat ~/.ssh/id_ed25519.pub

# Or copy it directly to the clipboard:
# macOS:
pbcopy < ~/.ssh/id_ed25519.pub

# Linux (requires xclip or wl-copy):
xclip -selection clipboard < ~/.ssh/id_ed25519.pub

# Windows (in PowerShell):
Get-Content ~/.ssh/id_ed25519.pub | Set-Clipboard

Now navigate to your provider's SSH key settings:

Paste the entire contents of the public key file, give it a recognizable title (like "Work Laptop - Feb 2025"), and save it.

Step 5: Test Your SSH Connection

Verify that authentication works before relying on it for actual Git operations.

# Test GitHub connection
ssh -T git@github.com
# Expected: Hi username! You've successfully authenticated, but GitHub does not provide shell access.

# Test GitLab connection
ssh -T git@gitlab.com
# Expected: Welcome to GitLab, @username!

# Test Bitbucket connection
ssh -T git@bitbucket.org
# Expected: authenticated via ssh key. You can use git to connect to Bitbucket.

If you see a warning about host authenticity the first time, type yes to add the host to your known_hosts file.

Step 6: Configure Git to Use SSH for Remote URLs

If you previously cloned repositories using HTTPS, you can switch them to SSH. There are two approaches:

Option A: Update existing remotes individually

# Check current remote URL
git remote -v

# Change from HTTPS to SSH (GitHub example)
git remote set-url origin git@github.com:username/repository.git

# Verify the change
git remote -v
# origin  git@github.com:username/repository.git (fetch)
# origin  git@github.com:username/repository.git (push)

Option B: Configure Git to always use SSH for certain domains

# Replace HTTPS with SSH for all GitHub repos globally
git config --global url."git@github.com:".insteadOf "https://github.com/"

# For GitLab
git config --global url."git@gitlab.com:".insteadOf "https://gitlab.com/"

# This rewrites URLs automatically. Now even "git clone https://github.com/user/repo.git"
# will actually connect via SSH.

Step 7: Managing Multiple SSH Keys (Optional)

If you use different keys for personal and work projects, or for different Git providers, create an SSH config file to map keys to specific hosts.

# Edit or create ~/.ssh/config
# Example configuration for multiple identities:

Host github-personal
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_personal
  IdentitiesOnly yes

Host github-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
  IdentitiesOnly yes

Host gitlab-work
  HostName gitlab.com
  User git
  IdentityFile ~/.ssh/id_ed25519_work
  IdentitiesOnly yes

Then clone repositories using the alias hostname instead of the default domain:

# Clone using the alias defined in ~/.ssh/config
git clone git@github-personal:yourusername/yourrepo.git
git clone git@github-work:yourorg/company-repo.git

SSH Key Best Practices Summary

Part 2: GPG Keys for Commit Signing

What GPG Commit Signing Is and Why It Matters

GPG (GNU Privacy Guard) allows you to cryptographically sign your Git commits and tags. When you sign a commit, you attach a digital signature that proves the commit was made by the holder of a specific private key. This solves a critical trust problem: Git's default author metadata (user.name and user.email) can be set to anything without verification. An attacker could easily create commits that appear to come from you. Signed commits provide:

Step 1: Install GPG

Most systems come with GPG pre-installed. Verify or install it:

# Check if GPG is installed
gpg --version

# Install if missing:
# macOS
brew install gnupg

# Ubuntu/Debian
sudo apt update && sudo apt install gnupg

# Fedora/RHEL
sudo dnf install gnupg

# Windows (via Chocolatey)
choco install gnupg

# Or download from https://www.gnupg.org/download/

Step 2: Generate a GPG Key Pair

Generate a new GPG key. Modern GPG versions (2.2+) use Ed25519 by default when available. You'll be prompted for your name, email, and a passphrase.

# Generate a new GPG key (interactive)
gpg --full-generate-key

# For GPG 2.1+ you can use the quick generation method:
gpg --quick-generate-key "Your Full Name " ed25519 default never

# If you need RSA (more compatible with older systems):
gpg --quick-generate-key "Your Full Name " rsa4096 default never

The interactive process looks like this:

gpg --full-generate-key
# Please select what kind of key you want:
#    (1) RSA and RSA (default)
#    (2) DSA and Elgamal
#    (3) DSA (sign only)
#    (4) RSA (sign only)
#    (9) ECC and ECC
#    (10) ECC (sign only)
# Your selection? 9  # Choose ECC for modern Ed25519

# Please select which elliptic curve you want:
#    (1) Curve 25519
# Your selection? 1

# Please specify how long the key should be valid.
#   0 = key does not expire
#     = key expires in n days
#   w = key expires in n weeks
#   m = key expires in n months
#   y = key expires in n years
# Key is valid for? 2y  # Set an expiration date

# Real name: Your Full Name
# Email address: your.email@example.com
# Comment: Work identity for Company X
# Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
# Enter passphrase: [strong passphrase]
# Confirm passphrase: [repeat]

Important: Set an expiration date. Expired keys can be renewed, and this limits damage if a key is lost. Use the email address that matches your Git configuration exactly.

Step 3: List Your Keys and Find the Key ID

After generating, you need your key's fingerprint or long ID to configure Git.

# List all GPG keys with their fingerprints
gpg --list-secret-keys --keyid-format LONG

# Example output:
# sec   ed25519/3AA5C343715EEF27 2025-01-15 [SC] [expires: 2027-01-15]
#       89F4A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8
# uid                 [ultimate] Your Full Name 
# ssb   cv25519/5BB8E920A3F47C1D 2025-01-15 [E]

# The long key ID is after the algorithm prefix: 3AA5C343715EEF27
# The full fingerprint is the 40-character hex string below it

Note the full fingerprint (the 40-character hex string). You'll need it for Git configuration and for your Git provider.

Step 4: Configure Git to Use Your GPG Key

Tell Git which key to use for signing and enable automatic signing of commits.

# Set your GPG signing key (use the fingerprint from above)
git config --global user.signingkey 89F4A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8

# Alternatively, use the shorter key ID:
git config --global user.signingkey 3AA5C343715EEF27

# Enable automatic signing of all commits (if desired)
git config --global commit.gpgsign true

# Enable automatic signing of tags
git config --global tag.gpgsign true

# Verify your Git configuration
git config --global --list | grep -E "signing|gpgsign|name|email"
# user.name=Your Full Name
# user.email=your.email@example.com
# user.signingkey=89F4A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8
# commit.gpgsign=true
# tag.gpgsign=true

Ensure your Git user.name and user.email match the name and email you used when creating the GPG key. Mismatched information will cause verification failures.

Step 5: Export Your Public GPG Key

You need to share your public key with your Git hosting provider so it can verify your signatures.

# Export your public key in ASCII-armored format
gpg --armor --export 89F4A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8

# This outputs a block starting with -----BEGIN PGP PUBLIC KEY BLOCK-----
# Copy the entire block including the header and footer lines

# To copy directly to clipboard (macOS):
gpg --armor --export 89F4A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8 | pbcopy

# Linux with xclip:
gpg --armor --export 89F4A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8 | xclip -selection clipboard

Add this public key block to your Git provider:

Paste the entire block including -----BEGIN PGP PUBLIC KEY BLOCK----- and -----END PGP PUBLIC KEY BLOCK-----.

Step 6: Sign Commits and Tags

With commit.gpgsign set to true, all commits are automatically signed. You can also sign manually on a per-commit basis.

# Manual signing (if commit.gpgsign is not set to true)
git commit -S -m "Add feature X with security fixes"

# The -S flag tells Git to sign this specific commit

# For tags, use -s (lowercase) to sign:
git tag -s v1.0.0 -m "Release version 1.0.0"

# Verify signatures on commits in the log
git log --show-signature

# Verify a specific tag's signature
git tag -v v1.0.0

If you encounter a pinentry prompt asking for your passphrase each time, you can configure the GPG agent to cache it.

Step 7: Configure GPG Agent for Passphrase Caching

To avoid typing your GPG passphrase on every commit, configure the GPG agent to cache it for a duration.

# Edit or create ~/.gnupg/gpg-agent.conf
# Add these lines:

default-cache-ttl 3600        # Cache passphrase for 1 hour
max-cache-ttl 28800           # Maximum cache lifetime (8 hours)
pinentry-program /usr/bin/pinentry-gtk-2  # Or pinentry-mac on macOS

# On macOS, also add:
# pinentry-program /usr/local/bin/pinentry-mac

# Reload the agent configuration
gpgconf --reload gpg-agent

# Or kill and restart
gpg-connect-agent updatestartuptty /bye
gpg-connect-agent reloadagent /bye

On macOS, you may also want to install a pinentry program that integrates with the macOS Keychain:

# Install pinentry-mac for macOS Keychain integration
brew install pinentry-mac

# Add to ~/.gnupg/gpg-agent.conf:
# pinentry-program /usr/local/bin/pinentry-mac

Step 8: Troubleshoot Common GPG Issues

Here are frequent problems and their solutions:

Problem: "gpg: signing failed: Inappropriate ioctl for device"

# Add this to your ~/.gnupg/gpg.conf (create the file if needed)
use-agent
pinentry-mode loopback

# And add to ~/.gnupg/gpg-agent.conf:
allow-loopback-pinentry

# Then reload the agent
echo RELOADAGENT | gpg-connect-agent

Problem: Git can't find the GPG key

# Double-check the signing key matches what's in your keyring
gpg --list-secret-keys --keyid-format LONG

# Ensure the email in the key matches git config user.email exactly
git config --global user.email
gpg --list-secret-keys --keyid-format LONG | grep ultimate

# If emails don't match, either update git config or generate a new key
# with the correct email

Problem: Commits show as "Unverified" on GitHub despite signing

# Verify the key is uploaded to GitHub
# Check: https://github.com/settings/gpg_keys

# Ensure the email on the GPG key is verified on GitHub
# Check: https://github.com/settings/emails

# The commit email must match the GPG key email AND be verified on GitHub
git log -1 --format="%ae"  # Shows author email of latest commit

Step 9: Set Up GPG for Multiple Identities (Optional)

If you have separate identities for work and personal projects, you can use multiple GPG keys and configure Git to select the right key per repository.

# Generate a work-specific key
gpg --quick-generate-key "Work Name " ed25519 default 2y

# Generate a personal key
gpg --quick-generate-key "Personal Name " ed25519 default 2y

# Set per-repository signing keys instead of global:
# In your work repository:
git config user.signingkey [WORK_KEY_FINGERPRINT]
git config commit.gpgsign true

# In your personal repository:
git config user.signingkey [PERSONAL_KEY_FINGERPRINT]
git config commit.gpgsign true

GPG Key Best Practices Summary

Generating a Revocation Certificate

If your private key is ever compromised or lost, you need a revocation certificate to invalidate it. Generate this immediately after creating your key and store it securely.

# Generate a revocation certificate (do this right after key creation)
gpg --gen-revoke 89F4A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8 > revocation-certificate.asc

# Store this file in a secure, offline location (encrypted USB drive, safe, etc.)
# To revoke the key later:
gpg --import revocation-certificate.asc
gpg --keyserver keys.openpgp.org --send-key 89F4A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8

Part 3: Putting It All Together — A Complete Workflow

Here is a full end-to-end workflow for setting up a new development machine with both SSH and GPG keys:

# === SSH SETUP ===

# 1. Generate SSH key
ssh-keygen -t ed25519 -C "your.email@example.com" -f ~/.ssh/id_ed25519

# 2. Start agent and add key
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

# 3. Copy public key to clipboard and add to GitHub/GitLab
cat ~/.ssh/id_ed25519.pub
# [Copy output and paste into provider's SSH key settings]

# 4. Test connection
ssh -T git@github.com
ssh -T git@gitlab.com

# 5. Configure Git to prefer SSH
git config --global url."git@github.com:".insteadOf "https://github.com/"
git config --global url."git@gitlab.com:".insteadOf "https://gitlab.com/"


# === GPG SETUP ===

# 6. Generate GPG key (ensure email matches git config)
gpg --quick-generate-key "Your Full Name " ed25519 default 2y

# 7. Find your key fingerprint
gpg --list-secret-keys --keyid-format LONG
# Note the full 40-character fingerprint

# 8. Configure Git for signing
git config --global user.signingkey YOUR_FULL_FINGERPRINT
git config --global commit.gpgsign true
git config --global tag.gpgsign true

# 9. Export and upload public GPG key to provider
gpg --armor --export YOUR_FULL_FINGERPRINT
# [Copy output and paste into provider's GPG key settings]

# 10. Generate revocation certificate and store securely
gpg --gen-revoke YOUR_FULL_FINGERPRINT > ~/.gnupg/revocation-certificate.asc
# [Back up this file offline]

# 11. Test with a signed commit
git init test-signed-commits
cd test-signed-commits
echo "# Test" > README.md
git add README.md
git commit -m "Test signed commit"
git log --show-signature -1
# Should show: gpg: Signature made... and "Good signature"

# Clean up
cd ..
rm -rf test-signed-commits

Part 4: Advanced Tips and Security Considerations

Using a Hardware Token (YubiKey) for GPG

For maximum security, store your GPG key on a hardware token like a YubiKey. The private key never leaves the device, and signing requires physical touch.

# Generate a key directly on the YubiKey (requires yubikey-manager)
gpg --card-status  # Verify YubiKey is detected

# Generate key on the card (interactive)
gpg --card-edit
# Then follow the prompts to generate keys on the device

# Or use the quick method if your YubiKey supports it
gpg --quick-generate-key "Your Name " ed25519 default never

Rotating Keys Gracefully

When you need to rotate keys, use a transition period where both old and new keys are accepted:

# 1. Generate new key
gpg --quick-generate-key "Your Name " ed25519 default 2y

# 2. Add new key to Git provider alongside the old one (don't delete old yet)

# 3. Update Git config to use new key
git config --global user.signingkey NEW_FINGERPRINT

# 4. Sign new commits with new key for 1-2 weeks

# 5. After transition period, remove old key from provider
# 6. Revoke old key if desired
gpg --import revocation-certificate.asc

Verifying Signed Commits from Others

You can verify that commits from collaborators are genuinely signed by them:

# Import a collaborator's public GPG key
gpg --import collaborator-public-key.asc
# Or fetch from keyserver:
gpg --keyserver keys.openpgp.org --search-keys collaborator@email.com

# Verify signatures in a repository
git log --show-signature

# Verify a specific commit
git verify-commit COMMIT_HASH

# Verify a signed tag
git verify-tag TAG_NAME

Conclusion

Setting up SSH keys and GPG commit signing transforms your Git workflow from basic to professional-grade. SSH keys eliminate the friction and security risks of password-based authentication, while GPG signing ensures that every commit bearing your name is verifiably yours. Together, they form a foundation of trust and efficiency that modern software development demands. The initial setup takes perhaps 30 minutes, but the benefits—faster authentication, cryptographic proof of authorship, compliance with security standards, and the satisfying "Verified" badge on your commits—last throughout your career. Make these practices habitual: generate strong keys, protect them with passphrases, rotate them periodically, and back up revocation certificates. Your collaborators, employers, and future self will thank you.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles