GitHub SSH Remote Connection Configuration and GPG Commit Signature Verification
I just reinstalled the operating system and need to configure GitHub's SSH keys from scratch. Having to search for commands each time is quite tedious, so I decided to record the process for future reference.
About the operating system, Git version, and use case in this article:
- Windows 11 IoT Enterprise LTSC 24H2
- git version 2.47.0.windows.1
- Two GitHub accounts, both requiring SSH and GPG keys configuration
- All the commands used below are executed in Git Bash.
- Content enclosed in angle brackets
< >
in the commands needs to be adjusted according to personal circumstances, such as the filename<Filename>
. When adjusting, remove the angle brackets, meaning the commands should not contain any< >
. - Please adjust the file paths, setting options, and other content in the text according to your actual situation.
Git Installation
Since this is a freshly installed system, Git isn’t installed yet. So first, let’s download Git.
After the installation, launch Git Bash.
SSH Remote Connection Configuration
Next, configure the SSH key for remote connections.
SSH Key Generation
Use the Ed25519 algorithm to generate an SSH key, replacing <your_email@example.com>
with your primary GitHub email address.
1 | ssh-keygen -t ed25519 -C "<your_email@example.com>" |
At this point, the system will prompt Enter file in which to save the key
for selecting the key’s storage location. Press Enter to accept the default location /c/Users/<username>/.ssh/id_ed25519
.
Then, you’ll see prompts for Enter passphrase for "/c/Users/<username>/.ssh/id_ed25519" (empty for no passphrase):
and Enter same passphrase again:
to set and confirm a passphrase for the key. You can press Enter twice to skip setting a passphrase. Note that if you do set a passphrase, you’ll need to enter it each time you perform git push
, which may be inconvenient.
Finally, the system will output the key’s location, fingerprint, and other details. The complete output will look similar to the following:
1 | Generating public/private ed25519 key pair. |
Add SSH Key to Your GitHub Account
After creating the SSH key pair, you need to add the public key to your GitHub account.
First, open the public key file with Notepad and copy all its contents.
1 | notepad ~/.ssh/id_ed25519.pub |
In the ~/.ssh
directory (i.e., C:\Users\<username>\.ssh
), you’ll find two files: id_ed25519
and id_ed25519.pub
. The former is the private key, and the latter is the public key. Only the public key needs to be added to your GitHub account—do not upload the private key online.
Next, log in to your GitHub account in a browser and follow these steps: click your profile photo in the top right corner → Settings
→ SSH and GPG keys
in the sidebar → New SSH key
button in the top right.
In the Title
field, enter a name to identify the key, making it easier to distinguish keys from different devices. Keep Key type
set to the default Authentication Key
option, and paste the copied public key into the Key
field. Finally, click Add SSH Key
.
Difference Between Authentication Key and Signing Key
Generated by ChatGPT:
When adding SSH keys on GitHub, you’ll see two types of keys: Authentication Key and Signing Key. Here’s the difference between them:
- Authentication Key
- Purpose: The Authentication Key is used for identity verification. It allows you to securely connect to GitHub via the SSH protocol, enabling you to perform actions such as
git push
andgit pull
. - How It Works: When you connect to GitHub using an SSH URL (like
git@github.com:username/repo.git
), GitHub authenticates you using your SSH public key. You need to add this public key to your GitHub account to authenticate your identity through SSH. - Usage Scenarios: All actions that require access to GitHub, such as cloning, pushing, and pulling code, use the Authentication Key.
- Purpose: The Authentication Key is used for identity verification. It allows you to securely connect to GitHub via the SSH protocol, enabling you to perform actions such as
- Signing Key
- Purpose: The Signing Key is used to digitally sign commits, ensuring they were created by a specific user and adding credibility to the code.
- How It Works: When you make a commit, you can sign it using either a GPG or SSH key. This signature allows other developers to verify that the commit is genuinely from you and hasn’t been tampered with.
- Usage Scenarios: In projects where commit verification is important, especially in open-source projects, using a Signing Key adds an extra layer of security.
To verify that the configuration is successful, try connecting. If this is your first connection, you’ll see a prompt saying, Are you sure you want to continue connecting (yes/no/[fingerprint])?
Type yes
and press Enter.
1 | ssh -T git@github.com |
If configured successfully, the system will display the message: Hi <Username>! You've successfully authenticated, but GitHub does not provide shell access.
Global Git Username and Email Address Configuration
Source: GitHub Docs
You can change the name that is associated with your Git commits using the git config
command. The new name you set will be visible in any future commits you push to GitHub from the command line. If you’d like to keep your real name private, you can use any text as your Git username.Changing the name associated with your Git commits using git config will only affect future commits and will not change the name used for past commits.
You can use the git config
command to change the email address you associate with your Git commits. The new email address you set will be visible in any future commits you push to GitHub from the command line. Any commits you made prior to changing your commit email address are still associated with your previous email address.
Set the global username by replacing <Username>
with your GitHub username.
1 | git config --global user.name "<Username>" |
Set the global email address by replacing <your_email@example.com>
with your primary GitHub email address.
1 | git config --global user.email "<your_email@example.com>" |
These settings can also be modified in the file located at C:\Users\<Username>\.gitconfig
.
Commit Signature Verification
The SSH configuration is now complete, and you should be able to use commands like git push
normally. However, without commit signature verification, our commits lack the “Verified” checkmark (the small green badge in the image below). Next, I’ll add a GPG key to my GitHub account and enable signature verification for my Git commits.
Purpose of Commit Signature Verification
Generated by ChatGPT:
Q: Since we’ve already verified our identity with SSH keys during push
, why do we need to verify commit signatures separately?
A: Although SSH keys confirm your identity and authorize access to the remote repository, commit signatures serve distinct purposes:
- Identity Verification vs. Commit Source Authentication
- SSH Key Verification: When you push using SSH, GitHub or GitLab checks if you have permission to push code. This process verifies identity but is focused on access control, ensuring you are an authorized user.
- Commit Signature: Signing verifies the source of each individual commit. It ensures that a given commit was genuinely created by the holder of the private key. Even if a malicious user gains access to your remote repository (e.g., by obtaining your SSH key), they still can’t forge signatures on previous commits.
- Tamper Resistance
- SSH Key Verification: Verifies only access rights to the repository but doesn’t guarantee the integrity of each commit.
- Commit Signature: Guarantees the integrity of commit content. If a commit is altered, its signature will no longer be valid, alerting users to any modifications that compromise trustworthiness.
- Enhanced Security
- Commit signatures provide an additional security layer to defend against malicious activities, both internal and external. Even if an SSH key is compromised, signature verification helps ensure that the code’s integrity and origin remain secure.
Using SSH Keys for Signing
There’s actually a shortcut: after adding your SSH key to your GitHub account, you can repeat the steps and change the key type from Authentication Key to Signing Key. This allows the SSH key to be used for commit signature verification without needing a separate GPG key—essentially one key serving dual roles. However, GPG keys offer a bit more functionality when used for signature verification, so I still opted to set up a GPG key.
To use an SSH key for signing, you’ll need to configure a few additional settings:
- Use
git config --global gpg.format ssh
to configure Git to use SSH for commit and tag signatures. - Use
git config --global user.signingkey ~/.ssh/<your_ssh_key.pub>
to specify the signing key in Git.
GPG Key Generation
When Git is installed, GPG is typically included. You can check if GPG is installed by running the following command:
1 | gpg --version |
A successful check will return version information; if there’s an error, you may need to install GPG manually.
1 | gpg (GnuPG) 2.4.5-unknown |
Now, generate a GPG key pair.
1 | gpg --full-generate-key |
The system will guide you through the key configuration:
- Choose encryption type: Press Enter to select the default ECC (Elliptic Curve Cryptography).
- Select the encryption curve: Press Enter to choose the default Curve25519.
- Choose the key expiration date: Press Enter to keep the default “No Expiration” setting.
- Press “y” and Enter to confirm these selections.
Next, provide key information:
- Enter a name (optional), which can simply be your GitHub username.
- Enter an email address—this must be the primary email linked to your GitHub account.
- Enter a comment if desired (optional).
- Press
O
and Enter to confirm. - In the Pinentry window that appears, set a password to protect your key, then enter it again to confirm. You’ll need this password to sign commits. If you prefer not to set a password, you can leave it blank.
Add GPG Key to Your GitHub Account
Like SSH keys, we also need to add the GPG key’s public key to your GitHub account.
First, list the GPG keys on your system:
1 | gpg --list-secret-keys --keyid-format=long |
The output will look similar to this:
1 | /c/Users/<Username>/.gnupg/pubring.kbx |
It may look confusing, but the format is:
1 | sec <Algorithm>/<Key ID> <Creation Date> [<Usage>] |
The key’s usage types are:
- S: Signing
- E: Encryption
- C: Certification
We need to use the ID of the key with signing capabilities, which is the key ID from the line with the [SC]
suffix, such as 21231BBF246642D21
, or the email address, to export the ASCII-encoded public key and automatically copy it to the clipboard using the command below.
1 | gpg --armor --export <Key ID or Email Address> | clip |
Next, log in to your GitHub account and navigate as follows:
- Click on your profile photo in the upper-right corner →
Settings
- In the left sidebar, click
SSH and GPG keys
- Then click the
New GPG key
button on the right.
In the Title
field, give your new signing key a label to help identify it across devices. In the Key
field, paste the GPG public key you copied. Finally, click Add GPG key
to complete the addition.
Commit Signature Verification Configuration
Finally, we need to configure the global GPG key.
1 | git config --global user.signingkey <Key ID> |
Additionally, to sign a commit, you need to add the -S
option in the commit command. If you set a password for your GPG key earlier, a prompt will appear, asking you to enter it.
1 | git commit -S -m "<Commit Message>" |
(Optional) If you find it inconvenient to include the -S
option each time, you can set Git to sign all commits by default, so you won’t need to use -S
in each commit command.
1 | git config --global commit.gpgsign true |
(Optional) Set Up GPG-Agent Cache
If you find entering the password for each commit inconvenient, you can set up a GPG-Agent cache duration so that you only need to enter the password once within the cache period.
First, create the configuration file in the ~/.gnupg
directory and open it with Notepad:
1 | touch ~/.gnupg/gpg-agent.conf && notepad ~/.gnupg/gpg-agent.conf |
Paste the following configuration, then save and close the file:
1 | default-cache-ttl 604800 |
- The numbers are in seconds. Set reasonable values; for instance, initially setting a one-month cache duration (2,592,000 seconds) didn’t work for me, but a one-week duration (604,800 seconds) did.
default-cache-ttl n
: Set the time a cache entry is valid to n seconds. The default is 600 seconds. Each time a cache entry is accessed, the entry’s timer is reset. To set an entry’s maximum lifetime, use max-cache-ttl. Note that a cached passphrase may not be evicted immediately from memory if no client requests a cache operation. This is due to an internal housekeeping function which is only run every few seconds.max-cache-ttl n
: Set the maximum time a cache entry is valid to n seconds. After this time a cache entry will be expired even if it has been accessed recently or has been set using gpg-preset-passphrase. The default is 2 hours (7200 seconds).
(Optional) Import and Trust GitHub Public Key
When we commit directly on the GitHub website, the commit signature verification uses GitHub’s key rather than our own. If we don’t have GitHub’s signature verification public key locally, the related commits will display gpg: Can't check signature: No public key
.
You can switch to a Git repository directory and use the following command to check:
1 | git log --show-signature |
The output will look like this, where you can see that the GitHub key ID is B5690EEEBB952194
, highlighted in red.
This red box is quite uncomfortable for those with OCD, so I choose to import and trust the GitHub public key locally to get rid of this red box.
First, import GitHub’s public key locally:
1 | curl https://github.com/web-flow.gpg | gpg --import |
Then, check the existing keys:
1 | gpg --list-keys --keyid-format=long |
The output looks like this. The first one is your own GPG key, and the next two are GitHub’s (with one of them even expired).
1 | $ gpg --list-keys --keyid-format=long |
Next, edit GitHub’s public key.
1 | gpg --edit-key B5690EEEBB952194 |
Trust the key.
1 | trust |
You will be prompted to select the trust level. Enter 4
and press Enter.
Finally, save the changes.
1 | save |
The full output is as follows:
1 | Sirius@Pikachu MINGW64 ~/.gnupg |
If this key expires in the future, simply delete it and re-import the latest version.
1 | gpg --delete-key B5690EEEBB952194 |
(Optional) VS Code Configuration
If you want to use Git with commit signing in VS Code, open the settings by pressing ctrl+,
, then search for git.enableCommitSigning
, and check the option Allow GPG, X.509, or SSH commit signing
.
Multiple Account Configuration
If you only need to set up one GitHub account, all the configurations are complete up to this point. Next, I will continue configuring the SSH and GPG keys for the second account. All files, paths, and suffixes related to the second account will be named second
, but feel free to modify them according to your preferences.
SSH Key Configuration
Second SSH Key Generation
Let’s generate the second SSH key:
- Replace
<your_email@example.com>
with the preferred email address for the second account. - Replace
<username>
with your Windows account name (i.e., the name of your user folder). - Replace
<id_ed25519_second>
with the filename for the second key.1
ssh-keygen -t ed25519 -C "<your_email@example.com>" -f "/c/Users/<username>/.ssh/<id_ed25519_second>"
Since we specified the key file path in the command, the system will only prompt you to enter and confirm a passphrase. Simply press Enter twice to skip.
Generate SSH Configuration File
We can create a config
file to differentiate Git operations for different accounts using distinct Hosts.
1 | touch ~/.ssh/config && notepad ~/.ssh/config |
The configuration file format is as follows:
1 | Host <alias> |
Paste the following content, adjusting the Host alias and key file path to match your setup, then save and close the file:
1 | # Default config for main account |
The meaning of these two configuration blocks is:
- Use the
id_ed25519_second
key for Git URLs with@github.com-second
. - Use the
id_ed25519
key for all other Git URLs.
Add SSH Key to GitHub Account
The process of adding the SSH key to the second GitHub account is essentially the same as for the first account. Just replace the name of the first key with the second one in the commands.
To verify if the configuration was successful, use the following command:
1 | ssh -T git@github.com-second |
Commit Signature Verification
The process of generating the second GPG key and adding it to the second GitHub account is identical to the first account. Follow the previous steps, but do not make global settings again.
Adjust Git Commands
For the second account, when using commands like git clone
, you need to replace the default @github.com
with the Host alias you configured earlier, i.e., @github.com-second
. For example:
1 | git clone git@github.com-second:Username/Repo.git |
Multiple Accounts Distinction
I have already set the first account’s configuration as global settings. To avoid confusion between the two accounts, we need to separate their Git configurations. Below are two methods I’ve found for configuring them.
I will continue using second
as the username/filename in the examples. Please make sure to replace it with your own second account’s name.
Fast Configuration with Batch Processing
Source: Ayakaの部屋
This method involves a local Git setup for each Git repository for the second account. By using local settings with higher priority, we can differentiate the repositories for each account.
However, entering multiple commands each time can be cumbersome. Instead, you can add these commands to a batch file and then add the batch file to the system’s environment variables. This way, you can run the batch file once to complete the configuration.
First, create the ~/.gitswitch/second.bat
file in the user directory and open it with Notepad:
1 | mkdir -p ~/.gitswitch/ && touch ~/.gitswitch/second.bat && notepad ~/.gitswitch/second.bat |
Then, paste and edit the following content, save, and close the file:
1 | git config --local user.name <second account username> |
Next, run the following command in a terminal with administrator privileges to open the system environment variable settings:
1 | rundll32.exe sysdm.cpl,EditEnvironmentVariables |
Add C:\Users\<Username>\.gitswitch
to the system environment variables. Replace <Username>
with your Windows username
:
- Double-click the
Path
under system variables. - Click
New
at the top right. - Paste the path.
- Click OK to save.
- Restart the terminal to apply the changes.
Afterward, if you need to configure the second account locally for a repository, simply enter and execute the script.
1 | second.bat |
Conditional Includes
This method uses the new conditional includes introduced in version 2.13, allowing for more flexible differentiation using conditionals.
Before configuring conditional settings, create the Git configuration file for the second account in the user directory and open it with Notepad:
1 | mkdir -p ~/.gitswitch/second/ && touch ~/.gitswitch/second/.gitconfig && notepad ~/.gitswitch/second/.gitconfig |
Then, paste and modify the following configuration, save, and close Notepad:
1 | [user] |
Finally, add the conditional includes at the end of the global Git configuration file ~/.gitconfig
. I’ve summarized two common conditions: path matching and URL matching, which can be used in combination.
Note: Conditional includes should be placed below the global settings!
Path Matching
We can specify a parent directory for the second account’s Git repositories, and then all Git repositories under that directory will use the same specified configuration. Repositories outside this directory will use the global configuration by default. This eliminates the need for individually configuring each Git repository for the second account.
Add the following configuration at the end of the global Git configuration file ~/.gitconfig
. On Windows, you must include /i
to disable case sensitivity; otherwise, the configuration may not take effect due to path issues:
1 | [includeIf "gitdir/i:D:/Project/Second/"] |
With this, all projects under the path D:/Project/Second/
will automatically use the configuration in ~/.gitswitch/second/.gitconfig
, while projects in other paths will retain the original global configuration.
URL Matching
This method requires Git version 2.36 or higher and is used to set different Git configurations by determining the remote URL used. Similar to the previous method, it requires creating a separate configuration file for the second account, as outlined earlier. Then, add the following content to the global configuration file:
1 | [includeIf "hasconfig:remote.*.url:git@github.com-second:*/**"] |
This configuration means that if the remote.*.url
field in a repository’s local configuration corresponds to git@github.com-second:Username/Repo.git
, the specified configuration file ~/.gitswitch/second/.gitconfig
will be used. This method seamlessly integrates with the SSH Host configuration set earlier, making it very convenient.
The complete global Git configuration file ~/.gitconfig
would look like this:
1 | [user] |
Reference:
- GitHub Docs
- What is GitHub’s public GPG key? @StackOverflow
- Git 2.13 conditional config on windows @StackOverflow
- Git - includeIf hasconfig:remote.*.url not working @StackOverflow
- 使用GPG对你的Git提交进行签名验证 @颢天
- GPG key 实现 GitHub Verified 认证(Authenticate & Sign) @KUN’s Blog
- GitHub commit 签名指南 @Ayakaの部屋
- Git的用户信息设置 @YeèのNotes
- ChatGPT