Aman Agnihotri

A verified Git identity on macOS

Curiosity got me into this more than any need for security. GitHub will talk to you over HTTPS or SSH, and once I noticed the SSH option sitting there I wanted to know what it was like to use. One rabbit hole later, SSH was handling my authentication and a small green “Verified” badge had appeared next to each of my commits. It was never really about being safer, even if it is a little. It was about finishing the setup properly: doing the thing the way it looks like it was meant to be done. It turned out clean enough that I never went back, so this is the version I would hand a friend.

I will be honest that I cannot give you a hard technical reason to prefer SSH over HTTPS for everyday Git. They behave the same once they work. The real reasons are that I wanted to see how it went, it feels like the more professional way to do this, and at the end of it your commits can be signed. Make of that what you will.

I use two keys, one to authenticate and one to sign. A single key could do both, but SSH and GitHub treat the two jobs as separate things, and whoever drew that line presumably had reasons. Following their intent costs nothing here: it is a one-time setup, and it means the key that logs me in and the key that vouches for my commits can be replaced independently later.

Before you start

You need Homebrew and the GitHub CLI, signed in once. The CLI is what saves us from clicking around the website to upload keys:

brew install gh
gh auth login

Two keys, both Ed25519

Ed25519 is the elliptic-curve key type, and it is a small pleasure: the keys and signatures are tiny, signing is fast, and you get more security per byte than the older RSA keys. There is no real reason to reach for anything else now.

Make one key for authentication and one for signing:

ssh-keygen -t ed25519 -C "github auth"    -f ~/.ssh/github_auth
ssh-keygen -t ed25519 -C "github signing" -f ~/.ssh/github_sign

The -C text is just a label baked into the key so you can recognise it later; it can be anything. Give each key a passphrase when prompted. A passphrase is a password that locks the key file, so a copied key is useless without it. Pick one you can remember, or keep it in your password manager. You will rarely type it in practice, because macOS is about to store it in the keychain for you. And if you ever lose it, nothing is broken: an SSH key is disposable, so you delete the old one on GitHub, generate a fresh one, and carry on. Anything you already signed stays Verified: GitHub records that at push time, so rotating a key leaves your past commits untouched.

Authenticate over SSH

Authentication here means your Mac proving it is allowed to push, without typing a password every time. The auth key does that. Tell SSH when to use it, in ~/.ssh/config (create the file if it is not there):

Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/github_auth
  IdentitiesOnly yes
  AddKeysToAgent yes
  UseKeychain yes
  IgnoreUnknown UseKeychain

Reading it line by line:

The indentation, by the way, is for you and not for SSH, which ignores leading whitespace entirely. Two spaces, four, or a tab all read the same to it. I use two.

Now store the passphrase in your keychain and hand the public half of the key to GitHub. Only the .pub file ever leaves your machine; the private one, the file without .pub, never does:

ssh-add --apple-use-keychain ~/.ssh/github_auth
gh ssh-key add ~/.ssh/github_auth.pub \
  --type authentication \
  --title "$(hostname -s) auth"

Check it worked:

ssh -T git@github.com

The first time, SSH asks whether to trust GitHub’s host key; type yes. Then GitHub greets you by username and adds that it cannot give you a shell. That looks like a rejection but is exactly right; pushing and pulling still work.

Sign your commits

Signing is the other half. A signature is a commit saying “this really came from me,” in a way others can check. When it checks out GitHub shows the green badge, and, more to the point, nobody can quietly push a commit under your name and have it look genuine.

Upload the signing key. GitHub keeps signing keys in a list of their own, just below the SSH auth keys on the same page:

gh ssh-key add ~/.ssh/github_sign.pub \
  --type signing \
  --title "$(hostname -s) signing"

Then tell Git to sign with it, in ~/.gitconfig:

[user]
  name = Your Name
  email = you@example.com
  signingkey = ~/.ssh/github_sign.pub
[gpg]
  format = ssh
[gpg "ssh"]
  allowedSignersFile = ~/.ssh/allowed_signers
[commit]
  gpgsign = true
[tag]
  gpgsign = true

A few of those lines earn a word:

Add your signing key to that allowed-signers file. Each line is an email, the namespace it applies to, and the public key; namespaces="git" keeps the key scoped to Git rather than anything else you might sign:

echo "you@example.com namespaces=\"git\" $(cat ~/.ssh/github_sign.pub)" \
  >> ~/.ssh/allowed_signers

See that it worked

Make a commit and look at it:

git commit -m "First signed commit"
git log --show-signature -1

Locally, git log now vouches for the signature using that allowed-signers file. On GitHub, the commit shows as Verified. That green badge is the part you can see; the useful part is everything under it.

If a commit comes back Unverified instead, it is almost always the email: the address in git config user.email has to match one your GitHub account has verified. And only commits made after you turned signing on are signed; the earlier ones stay as they were.

Clone over SSH

From here on, clone with the SSH address rather than the HTTPS one:

git clone git@github.com:you/repo.git

The config you wrote earlier quietly sends it through your auth key. Day to day, this is the bit that feels identical to HTTPS, which is exactly the point.

All of it, in one script

The same steps in one place, what I run on a new machine and what you can hand to a friend. It is an alternative to the walkthrough above, not an addition, so do one or the other. Fill in the two values at the top and run it; it pauses to ask for a passphrase as it makes each key.

#!/usr/bin/env bash
set -euo pipefail

name="Your Name"
email="you@example.com"   # an address GitHub has verified for you

host="$(hostname -s)"
auth="$HOME/.ssh/github_auth"
sign="$HOME/.ssh/github_sign"

# two Ed25519 keys
ssh-keygen -t ed25519 -C "github auth"    -f "$auth"
ssh-keygen -t ed25519 -C "github signing" -f "$sign"

# teach SSH about the auth key
cat >> "$HOME/.ssh/config" <<CONFIG

Host github.com
  HostName github.com
  User git
  IdentityFile $auth
  IdentitiesOnly yes
  AddKeysToAgent yes
  UseKeychain yes
  IgnoreUnknown UseKeychain
CONFIG
ssh-add --apple-use-keychain "$auth"

# hand both keys to GitHub
gh ssh-key add "$auth.pub" --type authentication --title "$host auth"
gh ssh-key add "$sign.pub" --type signing        --title "$host signing"

# sign every commit with the signing key
git config --global user.name "$name"
git config --global user.email "$email"
git config --global gpg.format ssh
git config --global user.signingkey "$sign.pub"
git config --global gpg.ssh.allowedSignersFile "$HOME/.ssh/allowed_signers"
git config --global commit.gpgsign true
git config --global tag.gpgsign true

# trust your own signature locally
echo "$email namespaces=\"git\" $(cat "$sign.pub")" \
  >> "$HOME/.ssh/allowed_signers"

# confirm the connection
ssh -T git@github.com || true

Two keys, a little config, and your Git identity is both authenticated and signed. The “Verified” badge is just the part that shows.