Skip to content
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
64d6b81
Revise GPG signing guide for macOS & Windows
chrisbloe Sep 10, 2025
9ac0b03
Additional tweaks
chrisbloe Sep 11, 2025
d122fe8
Removed erroneous = character
chrisbloe Sep 11, 2025
71b7899
Additional steps
chrisbloe Sep 11, 2025
ab0d3b6
Change username to github_handle in commit signing guide
chrisbloe Sep 11, 2025
95e4aa2
Clarify email and handle placeholders in commit signing guide
chrisbloe Sep 16, 2025
551e4e3
Changing the NOTE placement
chrisbloe Sep 16, 2025
bffd81b
Redoing a NOTE
chrisbloe Sep 16, 2025
fe12d50
NOTE changes
chrisbloe Sep 16, 2025
6851f87
Create ~/.gnupg directory if it does not exist
chrisbloe Sep 18, 2025
39bd085
Revise Windows section to include WSL
chrisbloe Sep 18, 2025
d3bb721
Change heading to 'Windows/WSL' for clarity
chrisbloe Sep 18, 2025
88aed16
Remove table of contents from commit signing guide as it's available …
chrisbloe Sep 18, 2025
c073060
Improve commit signing guide with clearer steps
chrisbloe Sep 18, 2025
504743f
Fix typos in commit-signing.md
chrisbloe Sep 23, 2025
d0c38e0
Enhance commit signing guide with SSH instructions
chrisbloe Sep 26, 2025
abeaf15
Just formatting
chrisbloe Sep 26, 2025
069f07d
Changing the recommended encryption and removing SSH instructions
chrisbloe Oct 3, 2025
9373fb5
Correct capitalization of 'public key'
chrisbloe Oct 3, 2025
8b2a05b
Suggest using @nhs.net email to enable Smart Commits
chrisbloe Oct 3, 2025
2ec33a6
Update --show-signature examples
chrisbloe Oct 7, 2025
45e0f6a
Merge branch 'main' into gpg-signing-update
chrisbloe Nov 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 181 additions & 75 deletions practices/guides/commit-signing.md
Original file line number Diff line number Diff line change
@@ -1,92 +1,200 @@
# Git commit signing setup guide

- [Git commit signing setup guide](#git-commit-signing-setup-guide)
- [From Workstations](#from-workstations)
- [macOS](#macos)
- [Windows](#windows)
- [From Pipelines](#from-pipelines)
- [GitHub Actions](#github-actions)
- [AWS CodePipeline](#aws-codepipeline)
- [Troubleshooting](#troubleshooting)
Using GPG, SSH, or S/MIME, you can sign commits and tags locally. These commits and tags are marked as verified on GitHub so other people can be confident that the changes come from a trusted source (see the full GitHub documentation [here](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)).

## From Workstations
> You should only set up **one** of these options - **don't attempt to set up GPG and SSH commit signing**!

### macOS
The instructions on this page focus on the recommended method - GPG.

- Install the [Brew package manager](https://brew.sh)
## GPG commit signing

```bash
brew upgrade
brew install gnupg pinentry-mac
gpg --full-generate-key
```
### From Workstations

- Accept the defaults, Curve 25519 etc.
- Enter your GitHub account name as the Real Name
- Enter your GitHub account email as the Email Address
- Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below)
- You can use the privacy *@users.noreply.github.com* email address listed in the GitHub profile: *Settings > Email*
- Define a passphrase for the key and keep it in your password manager
If you have already committed and need to retrospectively sign commits, follow the instructions below, then follow the [retrospective commit signing instructions](./retrospective-commit-signing.md).

```bash
gpg --armor --export ${my_email_address} | pbcopy
```
#### macOS

- Public key is now in your clipboard - in your GitHub account add it to your profile via *Settings > SSH and GPG Keys> Add New GPG Key*
- Paste it in
1. Install `gnupg` & `pinentry-mac` with [Brew](https://brew.sh):

```bash
git config --global user.email ${my_email_address} # same one used during key generation
git config --global user.name ${my_username}
git config --global commit.gpgsign true
sed -i '' '/^export GPG_TTY/d' ~/.zshrc
echo export GPG_TTY=\$\(tty\) >> ~/.zshrc
source ~/.zshrc
PINENTRY_BIN=$(whereis -q pinentry-mac)
sed -i '' '/^pinentry-program/d' ~/.gnupg/gpg-agent.conf
echo "pinentry-program ${PINENTRY_BIN}" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent
```
```bash
brew upgrade
brew install gnupg pinentry-mac
sed -i '' '/^export GPG_TTY/d' ~/.zshrc
echo export GPG_TTY=\$\(tty\) >> ~/.zshrc
source ~/.zshrc
PINENTRY_BIN=$(whereis -q pinentry-mac)
mkdir -p ~/.gnupg
touch ~/.gnupg/gpg-agent.conf
sed -i '' '/^pinentry-program/d' ~/.gnupg/gpg-agent.conf
echo "pinentry-program ${PINENTRY_BIN}" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent
```

The first time you commit you will be prompted to add the GPG key passphrase to the macOS Keychain. Thereafter signing will happen seamlessly without prompts.
1. Create a new GPG key:

Most of the published solutions for this don't work because *brew* seems to have moved the default folder for binaries, plus many guides contain obsolete settings for *gpg-agent*.
```bash
gpg --full-generate-key
```

### Windows
1. Pick `ECC (sign and encrypt)` then `Curve 25519` ([Ed25519](https://en.wikipedia.org/wiki/EdDSA#Ed25519) offers the strongest encryption at time of writing)
1. Select a key expiry time (personal choice)
1. `Real name` = Your GitHub handle
1. `Email address` = An email address [registered against your GitHub account](https://github.com/settings/emails) - to enable [Smart Commits](https://nhsd-confluence.digital.nhs.uk/x/SZNYRg#UsingtheGitHubintegrationinJira-SmartCommits) ([Jira/GitHub integration](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/)), use your `@nhs.net` address

- Install [Git for Windows](https://git-scm.com/download/win), which includes Bash and GnuPG
- Right-click on the Desktop > *Git Bash Here*
> If instead you opt for the private *@users.noreply.github.com* email address, consider enabling `Block command line pushes that expose my email`.

```bash
gpg --full-generate-key
```
1. Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below)
1. Review your inputs and press enter `O` to confirm
1. Define a passphrase for the key

- Pick *RSA and RSA*, or *RSA (sign only)* - there is no elliptic curve cryptography (ECC) support at the time of writing
- Set key size to 4096 bit, the minimum accepted for GitHub
- Enter your GitHub account name as the Real Name
- Enter your GitHub account email as the Email Address
- Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below)
- You can use the privacy *@users.noreply.github.com* email address listed in the GitHub profile: *Settings > Email*
- Define a passphrase for the key and keep it in your password manager
1. Test the key is visible and export the PGP public key (to your clipboard):

```bash
gpg --armor --export ${my_email_address} | clip
```
```bash
gpg -k # This should list the new key
gpg --armor --export <my_email_address> | pbcopy
```

- Public key is now in your clipboard - in your GitHub account add it to your profile via *Settings > SSH and GPG Keys> Add New GPG Key*
- Paste it in
> Your PGP public key is now in your clipboard!

```bash
git config --global user.email ${my_email_address} # same one used during key generation
git config --global user.name ${my_username}
git config --global commit.gpgsign true
```
1. [Add the public key to your GitHub account](https://github.com/settings/gpg/new) (`Settings` -> `SSH and GPG keys` -> `New GPG key`)

> Note the `Key ID` as you'll need this in the next step.

1. Set your local git config to use GPG signing:

```bash
git config --global user.email <my_email_address> # same one used during key generation
git config --global user.name <github_handle>
git config --global user.signingkey <key_id>
git config --global commit.gpgsign true
git config --global tag.gpgsign true
```

1. Test it works:

1. Create a temporary branch of your favourite repository.
1. Make an inconsequential whitespace change.
1. Commit the change.
1. You will be prompted for your GPG key passphrase - optionally select to add it to the macOS Keychain.
1. Check the latest commit shows a successful signing:

```bash
$ git log --show-signature -1
...
gpg: Good signature from "<github_handle> <<my_email_address>>" [ultimate]
Author: <github_handle> <<my_email_address>>
...
```

#### Windows/WSL

1. Install (as administrator) [Git for Windows](https://git-scm.com/download/win) (which includes Bash and GnuPG)
1. Open `Git Bash`
1. Create a new GPG key:

```bash
gpg --full-generate-key
```

1. Pick `ECC (sign and encrypt)` then `Curve 25519` ([Ed25519](https://en.wikipedia.org/wiki/EdDSA#Ed25519) offers the strongest encryption at time of writing)
1. Select a key expiry time (personal choice)
1. `Real name` = Your GitHub handle
1. `Email address` = An email address [registered against your GitHub account](https://github.com/settings/emails) - to enable [Smart Commits](https://nhsd-confluence.digital.nhs.uk/x/SZNYRg#UsingtheGitHubintegrationinJira-SmartCommits) ([Jira/GitHub integration](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/)), use your `@nhs.net` address

> If instead you opt for the private *@users.noreply.github.com* email address, consider enabling `Block command line pushes that expose my email`.

1. Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below)
1. Review your inputs and press enter `O` to confirm
1. A new window called pinentry will appear prompting you to enter a passphrase.

When you commit you will be prompted to enter the GPG key passphrase into a Pinentry window.
1. Test the key is visible and export the PGP public key (to your clipboard):

## From Pipelines
```bash
gpg -k # This should list the new key
gpg --armor --export <my_email_address> | clip
```

### GitHub Actions
> Your PGP public key is now in your clipboard!

1. [Add the public key to your GitHub account](https://github.com/settings/gpg/new) (`Settings` -> `SSH and GPG keys` -> `New GPG key`)

> Note the `Key ID` as you'll need this in the next step.

1. Set your local git config to use GPG signing:

```bash
git config --global user.email <my_email_address> # same one used during key generation
git config --global user.name <github_handle>
git config --global user.signingkey <key_id>
git config --global commit.gpgsign true
git config --global tag.gpgsign true
```

1. Now your key is created, make it available within Windows:

1. Export the key:

```bash
gpg --output <GitHub handle>.pgp --export-secret-key <my_email_address>
```

1. Install (as administrator) [Gpg4win](https://www.gpg4win.org/) (which includes GnuPG and Kleopatra)

> **Ensure both `GnuPG` and `Kleopatra` are installed!**

1. Open Kleopatra -> `Import` -> Select the `<GitHub handle>.pgp` file created in the first step
1. In `cmd`, test the key is visible and set your local git config to use GPG signing:

```bash
gpg -k # This should list the new key
git config --global user.email <my_email_address> # same one used during key generation
git config --global user.name <github_handle>
git config --global user.signingkey <key_id>
git config --global commit.gpgsign true
git config --global tag.gpgsign true
```

1. Now make it available within WSL:

1. Within Ubuntu:

```bash
sudo ln -s /mnt/c/Program\ Files\ \(x86\)/GnuPG/bin/gpg.exe /usr/local/bin/gpg
sudo ln -s gpg /usr/local/bin/gpg2
```

1. Close and reopen your Ubuntu terminal

1. Test the key is visible and set your local git config to use GPG signing:

```bash
gpg -k # This should list the new key
git config --global user.email <my_email_address> # same one used during key generation
git config --global user.name <github_handle>
git config --global user.signingkey <key_id>
git config --global commit.gpgsign true
git config --global tag.gpgsign true
```

1. Test it works:

1. Create a temporary branch of your favourite repository.
1. Make an inconsequential whitespace change.
1. Commit the change.
1. You will be prompted for your GPG key passphrase.
1. Check the latest commit shows a successful signing:

```bash
$ git log --show-signature -1
...
gpg: Good signature from "<github_handle> <<my_email_address>>" [ultimate]
Author: <github_handle> <<my_email_address>>
...
```

### From Pipelines

#### GitHub Actions

A GitHub Actions workflow will by default authenticate using a [GITHUB_TOKEN](https://docs.github.com/en/actions/security-guides/automatic-token-authentication) which is generated automatically.

Expand All @@ -97,7 +205,7 @@ The workflow would then use a Personal Access Token, stored with the GPG private
```yaml
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v5
with:
token: ${{ secrets.BOT_PAT }}
ref: main
Expand All @@ -121,7 +229,7 @@ git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from GitHub Actions: $
git push
```

### AWS CodePipeline
#### AWS CodePipeline

The cryptographic libraries in the default Amazon Linux 2 distro are very old, and do not support elliptic curve cryptography. When using pre-existing solution elements updating the build container is not always an option. This restricts the GPG key algorithm to RSA. You should use RSA-4096, which is the required minimum for GitHub.

Expand All @@ -138,7 +246,7 @@ if [[ ${BOT_SSH_KEY} != "None" ]]; then
echo "StrictHostKeyChecking yes" >> ~/.ssh/config
echo "UserKnownHostsFile=~/.ssh/known_hosts" >> ~/.ssh/config
echo "${BOT_SSH_KEY}" > ~/.ssh/ssh_key
echo -e "\n\n" >> ~/.ssh/ssh_key
echo -e "\n\n" >> ~/.ssh/ssh_key
chmod 600 ~/.ssh/ssh_key
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/ssh_key
Expand Down Expand Up @@ -174,10 +282,8 @@ git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from ${SCRIPT_URL}"
git push
```

## Troubleshooting

Re-run your git command prefixed with GIT_TRACE=1
### Troubleshooting

A failure to sign a commit is usually because the name or email does not quite match those which were used to generate the GPG key, so git cannot auto-select a key. Ensure that these are indeed consistent. (If you added a comment when creating your gpg key, this *may* cause a mismatch: the comment will be visible when listing your gpg keys, e.g. `RealName (Comment) <EmailAddress>`.) You are able to [force a choice of signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key), though this should not be necessary.
Re-run your git command prefixed with `GIT_TRACE=1`.

If you have already committed and need to retrospectively sign this commit [please follow the instructions here](./retrospective-commit-signing.md).
A failure to sign a commit is usually because the name or email does not quite match those which were used to generate the GPG key, so git cannot auto-select a key. Ensure that these are indeed consistent. (If you added a comment when creating your GPG key, this *may* cause a mismatch: the comment will be visible when listing your GPG keys, e.g. `RealName (Comment) <EmailAddress>`.) You are able to [force a choice of signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key), though this should not be necessary.