2026 年,用最硬核的方式打开 GPG(二)

本文是《2026 年,用最硬核的方式打开 GPG》系列文章的第 2 篇,在之前的文章中,已经完成了 GPG 四合一密钥的生成和备份。本文将详细介绍如何在已有密钥完整备份的前提下,在本地电脑(包括 Windows 和 Linux 系统)导入之前备份的子密钥,和配置远程服务器(以 Linux 系统为例),进行 git commit 签名和 SSH 登录。

下一篇文章将会介绍如何使用子密钥对文件和消息进行签名,加密等操作。

本文假设一切准备工作已经完成,现在应该有以下文件以备使用:

  1. 完整的四合一私钥备份 EC_full_secret.asc
  2. 吊销证书 EC_revocation.asc
  3. 仅包含子密钥,不含主密钥的三合一私钥文件 subkeys_only.asc
  4. 用于复制到远程服务器鉴权的 SSH 公钥文件 EA_openssh.txt
  5. 用于 GitHub 和个人网站的公钥文件 EC_public.asc

其中 12 请确保离线保存在安全的存储介质中,除非万不得已,否则不会用到。本文将使用 3 4 5 这三个文件,其中 3 在导入并信任后,应当彻底删除,后续有新的机器需要配置时,再用当前已配置好的机器现场导出供其使用。

§00. Windows 11 配置

§0.1 安装 GPG

官网下载安装包:https://gpg4win.org/download.html

直接选择最新版本的 Gpg4win,一路下一步安装。

安装完成后,新开一个 PowerShell 窗口,执行 gpg --version 确认,不出意外的话应该正常输出 GPG 版本信息。

§0.2 导入密钥并添加信任

准备好本文开头提到的三合一私钥文件(subkeys_only.asc),执行以下命令:

1
2
3
4
5
6
# 导入密钥
gpg --import .\subkeys_only.asc
# 查看导入的密钥信息,记录主密钥的 Key ID(以下记为 <EC_ID>)
gpg --list-keys --keyid-format LONG
# 添加信任
gpg --edit-key <EC_ID> trust

接下来的交互式命令中,依次输入 5(添加最高等级的信任),y(确认),save(保存并退出),最后执行 gpgconf --kill all 重启 GPG 程序,即可完成本地环境对新添加密钥的信任。

注意,永远不要给其他人的密钥添加最高等级的信任!

§0.3 配置 OpenSSH 使用 EA 子密钥鉴权

这一步是为了使用 EA 子密钥作为 SSH 连接远程机器时的身份认证,避免为 SSH 专门再生成一组密钥对,更方便统一管理。

首先配置 GPG 配置文件,启用 SSH 支持:

1
2
"", "enable-ssh-support", "enable-win32-openssh-support" | Out-File -FilePath $env:APPDATA\gnupg\gpg-agent.conf -Encoding utf8 -Append
"", "use-agent" | Out-File -FilePath $env:APPDATA\gnupg\gpg.conf -Encoding utf8 -Append

然后新开一个管理员权限的 PowerShell 窗口,配置系统环境变量:

1
[System.Environment]::SetEnvironmentVariable("SSH_AUTH_SOCK", "\\.\pipe\openssh-ssh-agent", "User")

现在 OpenSSH 连接远程服务器时,会自动调用 gpg-agent 程序来获取密钥鉴权了。但是默认情况下,gpg-agent 不会把所有的子密钥都推给 OpenSSH,而是需要显式地告诉 gpg-agent,该把哪个子密钥提供给 OpenSSH 用于 SSH 认证。要做到这一点,需要将对应子密钥的 Keygrip 写入 sshcontrol 配置文件:

1
2
3
4
# 查看带 A 权限的子密钥的 Keygrip,记为 <K_EA>
gpg --list-secret-keys --keyid-format LONG --with-keygrip
# 将子密钥的 Keygrip 加入配置文件
"", "<K_EA>" | Out-File -FilePath $env:APPDATA\gnupg\sshcontrol -Encoding utf8 -Append

现在所有的配置文件都准备完毕,还需要重启 gpg-agent 来使新的配置文件生效,然后禁用 Windows 自带的 ssh-agent,避免干扰 gpg-agent. 在管理员权限的 PowerShell 窗口执行下面的命令:

1
2
3
4
5
6
# 重启 gpg-agent
gpgconf --kill gpg-agent
gpg-connect-agent "GETINFO version" /bye
# 停止并禁用 ssh-agent
Stop-Service ssh-agent -ErrorAction SilentlyContinue
Set-Service -Name ssh-agent -StartupType Disabled

最后一步,Windows 启动时不会像 Linux 那样通过 systemd 自动拉起 gpg-agent 代理,所以需要额外的配置,确保每次打开 PowerShell 时 gpg-agent 是启动的状态:

1
2
3
4
# 修改 PowerShell 配置文件
"", "# 确保 gpg-agent 启动", "gpg-connect-agent --quiet /bye" | Out-File -FilePath $PROFILE -Encoding utf8 -Append
# 使配置文件生效
. $PROFILE

现在一切配置完成,下面验证一下:

1
2
3
4
# 首先验证 OpenSSH 能否通过 gpg-agent 获取到子密钥
ssh-add -l
# 比较 OpenSSH 获取到的子密钥哈希与之前导出的公钥哈希是否一致
$GPG = gpg --export-ssh-key <EC_ID> | ssh-keygen -l -f -; $SSH = ssh-add -l | Select-String "ED25519"; echo "GPG: $GPG", "SSH: $SSH"

不出意外的话,第一个命令应当正确输出 256 SHA256:<SHA256_HASH> (none) (ED25519) ,其中 <SHA256_HASH> 是密钥的 SHA256 哈希值;第二条命令会输出两行内容,比较两行输出中间的 <SHA256_HASH> 部分是否一致,一致则说明配置完成。如果不一致,或第一个命令报错 / 没有输出哈希,那么需要自行 Google 一下是哪里出了问题。

§0.4 配置 git 使用 ES 子密钥签名 commit

这部分参考下文的 Linux 部分自行配置,俺本人没有 Windows 环境下的 git.

§01. Linux 配置

绝大部分正常的 Linux 发行版都自带了 GPG 套件,所以这部分直接从导入密钥开始。

如果你的发行版确实没有自带,那么你首先需要自行研究如何安装 GPG. (都用这种发行版了,相信安装 GPG 对你来说应该不是问题)

§1.1 导入密钥并添加信任

准备好本文开头提到的三合一私钥文件(subkeys_only.asc),执行以下命令:

1
2
3
4
5
6
# 导入密钥
gpg --import ./subkeys_only.asc
# 查看导入的密钥信息,记录主密钥的 Key ID(同上文,以下仍记为 <EC_ID>)
gpg --list-secret-keys
# 添加信任
gpg --edit-key <EC_ID> trust

与上文 Windows 部分的操作相同,在接下来的交互式命令中,依次输入 5(添加最高等级的信任),y(确认),save(保存并退出),最后执行 gpgconf --kill all 重启 gpg 程序,即可完成本地环境对新添加密钥的信任。

注意,永远不要给其他人的密钥添加最高等级的信任!

§1.2 配置 OpenSSH 使用 EA 子密钥鉴权

这一步是为了使用 EA 子密钥作为 SSH 连接远程机器时的身份认证,避免为 SSH 专门再生成一组密钥对,更方便统一管理。

配置过程与上文 Windows 部分相似,但是 Linux 系统下会更简单一些。

首先配置 GPG 配置文件,启用 SSH 支持:

1
echo 'enable-ssh-support' >> ~/.gnupg/gpg-agent.conf

然后配置系统环境变量:

1
2
3
4
5
# 在 .zshrc 中添加环境变量
echo 'export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)' >> ~/.zshrc
echo 'gpgconf --launch gpg-agent' >> ~/.zshrc
# 使用新的配置文件重启 zsh,使新的环境变量生效
source ~/.zshrc

如果你用的不是 zsh,那么需要自行在你的终端配置文件中添加上面两个环境变量,并使其生效。

接下来将 EA 子密钥的 Keygrip 写入 sshcontrol 配置文件:

1
2
3
4
# 查看 EA 子密钥的 Keygrip,记为 <K_EA>
gpg --list-secret-keys --keyid-format LONG --with-keygrip
# 将子密钥的 Keygrip 加入配置文件
echo "<K_EA>" >> ~/.gnupg/sshcontrol

现在所有的配置文件都准备完毕,重启 gpg-agent 来使新的配置文件生效:

1
2
3
4
# 重启 gpg-agent
gpg-connect-agent updatestartuptty /bye
gpgconf --kill gpg-agent
gpgconf --launch gpg-agent

现在一切配置完成,下面验证一下:

1
2
3
4
# 首先验证 OpenSSH 能否通过 gpg-agent 获取到子密钥
ssh-add -l
# 比较 OpenSSH 获取到的子密钥哈希与之前导出的公钥哈希是否一致
diff <(gpg --export-ssh-key <EC_ID> | ssh-keygen -l -f -) <(ssh-add -l | grep ED25519)

同 Windows 部分类似,不出意外的话,第一个命令应当正确输出 256 SHA256:<SHA256_HASH> (none) (ED25519) ,其中 <SHA256_HASH> 是密钥的 SHA256 哈希值;第二条命令会输出两行内容,比较两行输出中间的 <SHA256_HASH> 部分是否一致,一致则说明配置完成。如果不一致,或第一个命令报错 / 没有输出哈希,那么需要自行 Google 一下是哪里出了问题。

§1.3 配置 git 使用 ES 子密钥签名 commit

先查看 ES 子密钥的 Key ID, 记为 <ES_ID>:

1
gpg --list-keys --keyid-format LONG

然后修改 git 配置:

1
2
3
4
5
# 配置 git 使用 ES 子密钥
git config --global user.signingkey <ES_ID>
# 配置 commit 时使用密钥签名
git config --global commit.gpgsign true
git config --global tag.forcesignannotated true

注意,密钥的 uid 信息中的邮箱地址需要和 GitHub 账号绑定的邮箱一致。

如果 GitHub 上设置了隐藏邮箱,或者使用了别的邮箱,可以参考 GitHub 官方文档 来配置。

§02. VPS 配置

§2.1 导出 EA 子密钥的公钥到远程服务器

首先准备好本文开头提到的 SSH 公钥文件(EA_openssh.txt),其中应该只有一行内容,即以 ssh-ed25519 开头的,EA 子密钥对应的 OpenSSH 格式的公钥,下面将其拷贝到远程服务器上。

如果本地是 Windows 11 系统:

1
2
3
4
5
# 如果服务器上之前配置过公钥
Get-Content EA_openssh.txt -Raw | ssh user@remote_server "cat >> ~/.ssh/authorized_keys"
# 如果服务器之前是用密码登录的,没有配置过公钥
# 那么使用下面这个命令,会提前创建好 .ssh 文件夹并配置好权限,避免报错
Get-Content EA_openssh.txt -Raw | ssh user@remote_server "mkdir -p ~/.ssh; chmod 700 ~/.ssh; cat >> ~/.ssh/authorized_keys; chmod 600 ~/.ssh/authorized_keys"

如果本地是 Linux 系统:

1
ssh-copy-id -i EA_openssh.txt user@remote_server

要验证是否已成功使用新的密钥连接,可以先 SSH 连接到远程服务器,然后执行:

1
sudo journalctl -u ssh | grep 'Accepted publickey' | tail -n 10

观察输出内容末尾的 <SHA256_HASH> 是否与前文中的 EA 子密钥对应的值一致。

如果确认无误,就可以按照相同的流程,将公钥复制到所有的远程服务器上。

§2.2 删除旧的 RSA 密钥对(如果没有,可以跳过本小节)

经过上一小节的配置,确认好新的 EA 子密钥鉴权没有问题后,就可以删除旧的 RSA 密钥了。

首先彻底删除本地的 RSA 密钥对:

1
2
# 彻底删除本地旧 RSA 密钥对
rm ~/.ssh/id_rsa ~/.ssh/id_rsa.pub

如果是 Windows 系统,那么这两个文件在 C:\Users\<username>\.ssh 这个文件夹下。

接下来删除远程服务器上旧的 RSA 公钥:

1
2
3
4
5
# 在远程服务器执行
# 删除 authorized_keys 中所有包含 ssh-rsa 的行
sed -i '/ssh-rsa/d' ~/.ssh/authorized_keys
# 或者编辑文件,手动删除不需要的公钥信息
vim ~/.ssh/authorized_keys

§03. GitHub 配置

§3.1 配置 SSH 公钥

准备好本文开头提到的 SSH 公钥文件(EA_openssh.txt),复制文件内容,打开 GitHub 设置中的 SSH and GPG keys ,点击 New SSH key,将刚才复制的 SSH 公钥粘贴进 Key 中,Title 可以留空,也可以任意填写,Key type 选项保持默认的 Authentication Key.

然后在本地执行 ssh -T [email protected] 验证是否能正常使用 EA 子密钥来通过 SSH 连接 GitHub 仓库,输出应该类似这样:

1
Hi <username>! You've successfully authenticated, but GitHub does not provide shell access.

§3.2 配置 GPG 公钥

准备好本文开头提到的公钥文件(EC_public.asc),复制文件内容,打开 GitHub 设置中的 SSH and GPG keys ,点击 New GPG key,将刚才复制的公钥粘贴进 Key 中,Title 可以留空,也可以任意填写。

在 GitHub 上配置好 GPG 公钥后,可以直接通过类似 https://github.com/<username>.gpg 的链接,来访问用户已配置的公钥。

例如,可以通过 https://github.com/he-sb.gpg 这个链接来访问俺的 GPG 公钥。

但这个功能好像有点问题,如果用户配置了多个公钥,那么这个链接只会提供用户最早配置的那个公钥……

§04. 结语

经过本文的配置,在主密钥保持离线状态的情况下,将三个各司其职的子密钥安全地导入了本地环境,并添加了最高等级的信任。现在最常用的 SSH 鉴权和 git commit 签名已经可以正常工作了。

下一篇文章将会介绍如何使用子密钥对邮件或文件进行签名,加密等操作。


参考链接:

  1. GPG折腾指北|拾秋的赛博屋
  2. GitHub 使用 GPG 密钥提交签名认证 | Dejavu's Blog
  3. 使用 GPG 签名 Git Commit | HE-SB-技术栈