Windows上使用GPG配置Git和SSH

记录一下在Windows上如何将GPG和Git,SSH结合起来使用。

环境

系统为Windows 11 23H2。

SSH是Windows系统自带的OpenSSH,不是自行安装的,也不是Git Bash里的ssh。

GPG是gnupg-w32,由scoop安装,不是Git Bash里的gpg。

SSH和GPG的版本如下:

> ssh -V
OpenSSH_for_Windows_8.6p1, LibreSSL 3.4.3
> gpg --version
gpg (GnuPG) 2.4.3
...
Home: C:\Users\liwenshen\scoop\apps\gpg\current\home
...

注意这里的GPG的Home文件夹,下文涉及的GPG的所有配置文件都放在这个Home文件夹中。

准备工作

GIT 配置

Git在默认情况下,使用Git Bash里自带的ssh和gpg程序。 我们需要强制其使用Windows内置的SSH和gnupg-w32的gpg程序。

先在PowerShell查看当前Windows下的程序路径:

> (Get-Command ssh).Path
C:\Windows\System32\OpenSSH\ssh.exe
(Get-Command gpg).Path
C:\Users\liwenshen\scoop\apps\gpg\current\bin\gpg.exe
PS C:\Users\liwenshen>

然后根据上面的路径,指定Git使用的程序:

git config --global core.sshCommand C:\\Windows\\System32\\OpenSSH\\ssh.exe
git config --global gpg.program C:\\Users\\liwenshen\\scoop\\apps\\gpg\\current\\bin\\gpg.exe

密钥情况

查看密钥的基本情况:

> gpg -k
C:\Users\liwenshen\scoop\apps\gpg\current\home\pubring.kbx
----------------------------------------------------------
pub   ed25519/82EF9F38F02BCA14 2023-06-04 [C]
      Key fingerprint = 7460 79A9 2447 E38D 1AF8  A7ED 82EF 9F38 F02B CA14
      Keygrip = 11C9D151D94FD5C4F5612CF1D79FB735E9EE08C6
uid                 [ultimate] liwenshen (git) <git@liwenshen.com>
sub   ed25519/1B3B3B59E5427BF0 2023-06-04 [SA] [expires: 2025-06-03]
      Keygrip = E57DFA139A46760699AB482039CC3443EB0EAB22
sub   cv25519/9556AA6A5BCDE6EC 2023-06-04 [E] [expires: 2025-06-03]
      Keygrip = 4DCA3FE1E8779FF9D1A6C773BD66E6CF8BE0FA63

在Git签名中,需要使用S(Signing签名)类型子密钥。 在SSH连接认证中,需要使用A(Authenticating认证)类型子密钥。

可以看到,在我的情况下,一个主密钥下挂两个子密钥。 其中第一个子密钥符合签名和认证的要求。因此就用这个子密钥进行SSH认证和Git签名。

这个子密钥对应的ID是1B3B3B59E5427BF0,Keygrip是E57DFA139A46760699AB482039CC3443EB0EAB22。 还需要记一下主密钥的ID是82EF9F38F02BCA14,后面需要用到这3个值。

如果密钥ID和Keygrip显示不完全。 可以在gpg.conf中加上下列内容后,重新执行gpg -k

keyid-format long
fingerprint
with-keygrip

或者直接执行

gpg -k --keyid-format long --fingerprint --with-keygrip

SSH 认证

GPG子密钥的SSH公钥

使用子密钥的ID,导出子密钥的SSH格式公钥,将其粘贴至Github或者服务器等SSH远程服务器上。

> gpg --export-ssh-key 1B3B3B59E5427BF0
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXESXaFJse2EE4IJ8BVbwSoSLezT2uAHIDVz4N3mkhi openpgp:0xE5427BF0

GPG agent

GPG Agent是SSH Agent的实现,可以将SSH登录的过程中的认证环节,委托给GPG进行认证。 因此想要在SSH连接中使用GPG密钥,需要先配置GPG agent。

配置GPG agent

网络上许多文章,都有用到wsl-ssh-pageant,还要书写各种的powershell启动脚本进行配置。 实际上没有必要,gnupg-w32已经内置了对Windows OpenSSH的支持。 只需要在gpg-agent.conf中加上下列内容就可以开启支持。

enable-ssh-support
enable-putty-support
enable-win32-openssh-support

然后将用来进行SSH认证的密钥的Keygrip写入sshcontrol文件,如:

E57DFA139A46760699AB482039CC3443EB0EAB22
 

警告:sshcontrol文件里新增Keygrip后务必换行,且新一行不能有空格!!!

运行GPG Agent

以下两条命令,任选一条,启动GPG agent

gpgconf --launch gpg-agent
gpg-connect-agent /bye

以下两条命令,任选一条,可以关闭GPG agent

gpgconf --kill gpg-agent
gpg-connect-agent killagent /bye

为了自动启动GPG agent,可以将GPG agent启动命令,加入计划任务中,并设置开机启动。

测试SSH

启动GPG agent后,执行命令,查询SSH能否正常使用GPG密钥。

> ssh-add -L
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJXESXaFJse2EE4IJ8BVbwSoSLezT2uAHIDVz4N3mkhi (none)

可以看到,这个公钥和上文gpg --export-ssh-key导出的公钥是一致的,说明SSH已经可以使用GPG的子密钥了。

再测试一下SSH连接Github或者其他远程服务器,出现下列相似内容说明SSH已经认证成功。

> ssh -vT git@github.com
...
debug1: Next authentication method: publickey
debug1: Offering public key: (none) ED25519 SHA256:6aRZPwhjxDNgVIaXwaE9US9ZvUON3kzepoNH976w5vY agent
debug1: Server accepts key: (none) ED25519 SHA256:6aRZPwhjxDNgVIaXwaE9US9ZvUON3kzepoNH976w5vY agent
debug1: Authentication succeeded (publickey).
...
Hi liwenshen2000! You've successfully authenticated, but GitHub does not provide shell access.
...

如果出现下列报错,请优先检查上文涉及的配置文件内容,如换行符类型,末尾是否换行等。

> ssh-add -L
error fetching identities: agent refused operation

Git 签名

GPG密钥的GPG公钥

将GPG密钥的公钥导出。 然后将输出内容粘贴至Github或者其他远程代码库等GPG配置中。 就可以让代码库在推送代码后,认可这个GPG密钥签名的commits和tags。

> gpg --armor --export 82EF9F38F02BCA14
-----BEGIN PGP PUBLIC KEY BLOCK-----
...
-----END PGP PUBLIC KEY BLOCK-----

导出时是导出整个GPG密钥的公钥,包括主密钥和子密钥,因此无论使用的是主密钥ID还是子密钥ID,输出结果都是一样的。

配置Git

配置Git的GPG签名较为简单,首先在Git里设置用于签名的密钥的ID:

git config --global user.signingkey 1B3B3B59E5427BF0!

我这里指定了一个具体的GPG密钥进行签名,因此需要在在密钥ID的末尾加上感叹号。 也可以使用主密钥,让Git自动选择其下的签名密钥用于签名,那么可以设置成主密钥的ID,不必加感叹号:

git config --global user.signingkey 82EF9F38F02BCA14

配置默认对所有commits和tags签名

git config --global commit.gpgsign true
git config --global tag.gpgSign true

延长密钥TTL

在日常使用各种git软件中的过程中,后台会使用ssh定期pull仓库,这时候频繁会弹出密钥过期的提示,可以在gpg-agent.conf加上以下配置,延长密钥的TTL。

输入一次密码有效1天,7天强制要求输入一次密码。

# default-cache-ttl 86400
# max-cache-ttl 604800
default-cache-ttl-ssh 86400
max-cache-ttl-ssh 604800

后记

实际上在Windows上使用GPG密钥进行Git签名和SSH认证,还是较为繁琐的。

推荐使用GpgFrontend等GUI前端进行GPG密钥生成和管理。

如果仅仅是为了在Github上给自己加上一个小绿勾,可以使用更为简单易用的SSH密钥进行签名和认证1