Signing Git commits with SSH

How to sign Git commits with SSH on macOS.

You can sign Git commits with an SSH key.

Info

The following uses the same SSH key for GitHub authentication and signing commits.

Create a new SSH key

Create a new SSH key with a passphrase:

ssh-keygen -t ed25519 -C "<ID>+<USERNAME>@users.noreply.github.com"

Use the following file location and name:

~/.ssh/id_ed25519_github_danillouz

Note

I’m using the GitHub noreply email (which matches the noreply commit email) to keep my email private (my primary email is also marked private on GitHub, so the noreply email is also used for web-based operations).

Update SSH config

Add the following to ~/.ssh/config:

Host github.com
	AddKeysToAgent yes
	UseKeychain yes
	IdentityFile ~/.ssh/id_ed25519_github_danillouz

Add private key to the SSH agent

Add the private key to the SSH agent, to automatically manage the key, and store the passphrase in the macOS keychain (the default macOS ssh-add must be used):

ssh-add --apple-use-keychain ~/.ssh/id_ed25519_github_danillouz

Then check it was added:

ssh-add -l

Tip

All previously added SSH key(s) can be deleted with ssh-add -D.

Add public SSH key to GitHub

Copy the public key:

pbcopy < ~/.ssh/id_ed25519_github_danillouz.pub

And add it to GitHub.

Note

When using the same SSH key for GitHub authentication and signing commits, the same key must be added twice to GitHub: once with type “authentication” and once with type “signing”.

Test SSH connection

Test the SSH connection to GitHub (authentication):

ssh -T [email protected]

Note

Check that GitHub’s public key fingerprint matches before connecting.

The GitHub username is printed when it works.

Tell Git about the signing key

Update the global Git config to start using the SSH key to sign commits and tags:

git config --global user.signingkey "~/.ssh/id_ed25519_github_danillouz.pub"
git config --global gpg.format ssh
git config --global commit.gpgsign true
git config --global tag.gpgsign true

The .gitconfig should now look like this:

[user]
	name = Daniël Illouz
	email = <ID>+<USERNAME>@users.noreply.github.com
	signingkey = ~/.ssh/id_ed25519_github_danillouz.pub
[gpg]
	format = ssh
[commit]
	gpgsign = true
[tag]
	gpgsign = true

Local signature verification

SSH has no concept of trust levels like GPG does, but a file can be created that contains trusted SSH (public) keys.

For example:

~/.config/git_allowed_signers

Where each key in the file (each key must be placed on a separate line) has the format:

<EMAIL> <KEY_TYPE> <PUBLIC_KEY>

Where KEY_TYPE must be ssh-ed25519.

To get PUBLIC_KEY use:

 pbcopy < ~/.ssh/id_ed25519_github_danillouz.pub

Then update the global Git config to use the allowed signers file:

git config --global gpg.ssh.allowedSignersFile "~/.config/git_allowed_signers"

The .gitconfig should now look like this:

[user]
	name = Daniël Illouz
	email = <ID>+<USERNAME>@users.noreply.github.com
	signingkey = ~/.ssh/id_ed25519_github_danillouz.pub
[gpg]
	format = ssh
[gpg "ssh"]
	allowedSignersFile = ~/.config/git_allowed_signers
[commit]
	gpgsign = true
[tag]
	gpgsign = true

With this config, signatures can be verified locally. For example with:

git show --show-signature

Resources