本文是《2026 年,用最硬核的方式打开 GPG》系列文章的第 1 篇。本文将详细介绍如何从头开始,创建属于自己的 GPG 密钥对,并生成对应的吊销证书,以及导出对应的备份文件。
下一篇文章将会介绍如何使用生成的密钥。
§00. 前言
关于 GPG,PGP,非对称加密等等这些概念,各种百科和科普文章已经很多了,本文不再赘述。
这里简单解释一下俺为什么要折腾这个:
- 基于可靠的数学原理,它有能力在赛博空间证明“俺是谁”(安全的鉴权)。
- 基于可靠的数学原理,它有能力证明俺的文件未经篡改(安全的签名)。
- 基于可靠的数学原理,它有能力避免俺不想公开的文件被窃取(安全的加密)。
本系列暂不会涉及 Yubikey 等硬件的配置(因为俺没有……),如果后续有涉及,会更新新的文章来记录。
本文较长,各位可以根据目录自行跳转到感兴趣的部分查看。
§01. 生成密钥对
在开始操作之前,可能有比较帅的网友要问了:“哎,你不是在 《使用 GPG 签名 Git Commit》 这篇文章中已经生成过 GPG 密钥对,而且已经用它来给 GitHub 上的 commit 签名了吗?”
确实,但是:
- 当时生成的密钥对是 RSA4096 算法,性能较差,而且公钥导出时很长,不方便粘贴。
- RSA 算法依赖随机数生成器的质量,安全性不够。
- 当时图方便,只生成了一个全功能的主密钥,没有分离权限,不够安全。
Key ID 不够靓,不能装逼,而且换电脑时忘了备份,找不到了……
所以这次从头开始,使用性能更好安全性也更高的 Ed25519 算法,生成一个四合一的密钥对,进行一次充分兼顾当下环境的安全和效率的最佳实践。
通过本小节的操作,将会生成一个四合一的密钥:
- 主密钥(后续称为
EC):Ed25519 算法,具有 C(Certify,认证)权限 - 签名子密钥(后续称为
ES):Ed25519 算法,具有 S(Sign,签名)权限 - 鉴权子密钥(后续称为
EA):Ed25519 算法,具有 A(Authenticate,身份验证 / 鉴权)权限 - 加密子密钥(后续称为
CE):Cv25519 算法,具有 E(Encrypt,加密)权限
如果你对密钥的指纹(40 位十六进制数字,末 16 位作为 Key ID)没有特殊要求,或者你看不懂这句话,那么直接按照【普通操作】这一小节的步骤操作,然后跳过【高端操作】小节,从【生成吊销证书】继续阅读。
如果你也想像俺一样使用类似 2BB2666666666666 这样 炫酷 的指纹,那么可以跳过【1.1 普通操作】这一小节,详细研究【1.2 高端操作】小节。
§1.1 普通操作
- 终端输入
gpg --full-generate-key. - 选择 ECC (sign and encrypt), 俺这里是输入
9, 回车确认。 - 选择 Curve 25519, 俺这里是输入
1, 回车确认。 - 选择密钥的过期时间,俺选择
0也就是永不过期,各位可以按需选择。 - 输入
y后回车确认。 Real name不一定使用真名,可以随便填写一个想要展示给别人看的名字。Email address输入邮箱地址。Comment个性签名或者别的想展示给别人看的信息,没什么想写的可以留空,直接回车跳过。- 此时会将 uid 信息再展示一次,确认无误后输入
O确认。 - 设定一个高强度密码,用键盘的方向键切换选项,输入两次后选择
OK保存。
此时 gpg 会输出类似下面的信息:
| |
这里的 67992DB2CEA0D623DC761430E379BEB325A8928D 就是主密钥和公钥的指纹,最后 16 位 E379BEB325A8928D 就是 Key ID,也就是最后展示给别人的公钥 ID.
注意到这里生成的密钥对,主密钥同时具有 S 和 C 权限,而且只有一个 E 权限的子密钥,不符合权限分离的要求。所以接下来需要去掉主密钥的 S 权限,然后新增 ES 和 EA 子密钥:
- 编辑主密钥:
gpg --edit-key <EC_ID>, 其中<EC_ID>是主密钥的 Key ID. - 修改主密钥的权限,去掉
S, 只保留C.- 输入
change-usage后回车,然后输入S回车,仔细查看Current allowed actions:这行,后面只有Certify说明S权限去掉了。 - 输入
Q回车,输入刚才设定的密码后回车确认。
- 输入
- 添加
ES子密钥。输入addkey回车确认。- 选择 ECC (sign only), 俺这里是输入
10, 回车确认。 - 选择 Curve 25519, 俺这里是输入
1, 回车确认。 - 选择子密钥的过期时间,俺选择
0也就是永不过期,各位可以按需选择。 - 输入两次
y回车,输入刚才设定的密码后回车确认。
- 选择 ECC (sign only), 俺这里是输入
- 添加
EA子密钥。因为A权限的子密钥没法直接添加,所以先添加一个S子密钥,然后再修改它的权限(因为这两种权限的密钥,加密算法都是 Ed25519,所以生成时不需要区分,只需生成后修改权限)。- 先按上一步的操作,再添加一个
S子密钥。 - 然后输入
key 3选择第 3 个子密钥,也就是最后添加的,第 2 个S子密钥,此时这个子密钥的 Key ID 旁会多出一个*号。 - 输入
change-usage, 回车。 - 先输入
S后回车,接着输入A后再回车(顺序不重要,这一步目的是去掉原本的S权限,添加A权限)。 - 输入两次
y回车,输入刚才设定的密码后回车确认。
- 先按上一步的操作,再添加一个
- 最后输入
save保存并退出。
最后执行 gpg --list-keys --keyid-format LONG 确认一下生成的密钥,输出应该类似这样:
| |
其中 E379BEB325A8928D 就是 EC 的 Key ID, 98D55538F914D4A9 是 CE 的 Key ID, 84B2DB48987F8778 是 ES 的 Key ID, 030F72DCC734450B 是 EA 的 Key ID.
- 公开的公钥信息,别人看到的是主密钥的
<EC_ID>. - 加密时,对方看到的加密指纹是
<CE_ID>. - 签名时,比如 git commit, 对方看到的是
<ES_ID>. - 鉴权时,对方看到的是
<EA_ID>.
§1.2 高端操作
计算 GPG 靓号的原理部分,可以参考 @DejavuMoe 大佬的 某科学的 PGP 算号指南 这篇文章,本文就不再赘述。
至于具体用来算号的工具,这里要特别感谢 @TransparentLC 大佬的 GPU 算号项目:
这两个项目原理是一样的,只是实现方式不同。webgl-vanity-gpg 只需要浏览器打开一个网页就可以开始算,通过 WebGL 调用显卡的图形能力;而 opencl_vanity_gpg 提供了编译好的二进制可执行文件,支持 Windows, Mac, Linux 三个平台的命令行环境,通过 OpenCL 调用显卡的计算能力。
基准性能对比可以参考两个项目的 README,俺这里提供一下自己的实测值(系统均为 Windows 11 LTSC 2024 IoT 版):
显卡 WebGL 速率 (hash/s) OpenCL 速率 (hash/s) AMD RX 7700 XT 7.14B 13.67B AMD Radeon 680M 1.12B 2.41B
实测下来 OpenCL 版的效率明显要更高,算号速度是 WebGL 版的 2 倍左右。如果你条件有限,只能用 WebGL 版,那么也是可以的,不过有条件的话还是推荐使用 OpenCL 版,指定靓号格式更加方便,性能也显著更好。
§1.2.1 算号
这部分最为枯燥,任选上面提到的两个项目之一,参考项目文档,指定你喜欢的靓号格式,开始挂机。
俺这里使用的是 OpenCL 版,在 PowerShell 中开始算号:
| |
命令执行后,会开始不断循环算号,每个生成的靓号对应的私钥文件都会保存在当前路径下的 opencl-keys 文件夹内,文件名都是类似 8A01DEE8BE638FCE17191021799BBBBBBBBBBBBB-sec.asc 的形式,其中 -sec.asc 之前的部分,即 8A01DEE8BE638FCE17191021799BBBBBBBBBBBBB 是完整的 40 位密钥指纹,这部分的最后 16 位,即 799BBBBBBBBBBBBB 是 Key ID,也就是期待中的靓号。定期查看其中有没有中意的,生成了满意的靓号之后就可以回到 PowerShell 窗口 Ctrl + C 结束算号程序了。
中间等待的时长取决于你指定的靓号格式,以及一点运气。一般指定的位数越多,时间越长。
WebGL 版查看项目说明即可,操作上更为简单直观,此处不再赘述。如果你使用其他算号项目,那么自行研究。
最终会得到 4 个密钥文件:
- 准备用作主密钥的
EC-sec.asc - 准备用作签名子密钥的
ES-sec.asc - 准备用作鉴权子密钥的
EA-sec.asc - 准备用作加密子密钥的
CE-sec.asc
由于算号程序生成的密钥都是一个 Ed25519 主密钥和一个 Cv25519 子密钥成对生成的,所以这 4 个密钥文件其实是 4 个密钥对:
ECESEA这三个密钥对,其中的 Ed25519 主密钥的 Key ID 都是靓号,而他们绑定的 Cv25519 子密钥的 Key ID 都是随机的。后续会通过一些操作剥离这三个 Cv25519 子密钥,仅保留三个 Ed25519 靓号。CE这个密钥对,比较特殊。它的 Ed25519 主密钥的 Key ID 是随机的,而 Cv25519 子密钥的 Key ID 是后续待使用的靓号(当然了,这是因为生成它时,将筛选靓号的规则仅应用在了子密钥上)。后续操作会剥离它的 Ed25519 主密钥,仅保留 Cv25519 靓号。- 不过这会导致一个不便的情况,密钥对的文件名是主密钥的指纹,只看文件名是看不出来 Cv25519 子密钥的 Key ID 是否是想要的靓号的。俺个人建议:
- 要么生成
CE子密钥时使用 WebGL 版本的算号程序,网页内点击每个主密钥指纹时,下方会同时显示主密钥和子密钥的指纹,方便查看。 - 要么查看 PowerShell 内输出的日志,确定哪个密钥对中的 Cv25519 子密钥是准备使用的靓号。
- 要么生成
- 不过这会导致一个不便的情况,密钥对的文件名是主密钥的指纹,只看文件名是看不出来 Cv25519 子密钥的 Key ID 是否是想要的靓号的。俺个人建议:
保留好这 4 个密钥文件,下面开始缝合操作,把他们合并为一个完整的密钥对。
§1.2.2 缝合密钥
消耗一些电费和时间后,终于得到了 4 个满意的靓号。下面来将它们合并起来,将三个 Ed25519 类型的靓号的其中一个作为主密钥,另外两个和 Cv25519 类型的靓号一起作为子密钥组合起来,最终得到一个四合一的密钥。
首先必须确认每个密钥的创建时间,作为主密钥的
EC必须是创建时间最早的那个,三个子密钥的创建时间则互不影响。
下面先合并 EC 和 ES,其中 EC 作为主密钥,ES 作为子密钥:
- 导入两个密钥:
gpg --import EC-sec.asc ES-sec.asc. - 查看密钥的时间戳信息:
gpg --list-keys --keyid-format LONG --with-colons.- 输出的信息中,在 16 位 Key ID 右侧的 10 位数字就是时间戳,找到子密钥
ES的时间戳,记为T_ES.
- 输出的信息中,在 16 位 Key ID 右侧的 10 位数字就是时间戳,找到子密钥
- 查看密钥的 keygrip:
gpg --list-keys --keyid-format LONG --with-keygrip.- 输出的信息中,找到子密钥
ES的 keygrip, 记为K_ES.
- 输出的信息中,找到子密钥
- 编辑主密钥:
gpg --expert --faked-system-time="T_ES\!" --ignore-time-conflict --edit-key <EC_ID>.- 注意
T_ES后面紧跟着的\!,一定不能漏掉。 <EC_ID>是主密钥EC的 Key ID.
- 注意
- 首先删除不需要的子密钥。由于当前只有一个子密钥,所以输入
key 1选中第 1 个子密钥,如果有多个,按照子密钥的序号修改数字来选中,选中后的子密钥旁会有一个*号。输入delkey删除这个子密钥,回车确认。询问是否确认时,输入y回车确认删除。 - 然后输入
addkey添加子密钥,回车确认。- 选择从现有密钥导入(Existing key),俺这里是输入
13, 回车确认。 - 输入第
3步记录的K_ES, 回车确认。 - 此时需要选择导入的子密钥的权限,因为
ES是 Ed25519 算法,可选的有S或者A权限,默认是S,不需要修改,输入Q后回车完成。- 如果此时缝合的是
EA, 那么需要先输入S后回车,接着输入A后再回车(顺序不重要,这一步目的是去掉默认的S权限,添加A权限)。 - 如果此时缝合的是
CE, 因为是 Cv25519 算法,权限只能是E, 不需要修改)。
- 如果此时缝合的是
- 选择子密钥的过期时间,俺选择
0也就是永不过期,各位可以按需选择。 - 输入两次
y确认。
- 选择从现有密钥导入(Existing key),俺这里是输入
- 修改主密钥的权限,默认是
S和C, 这里需要去掉S, 只保留C.- 输入
change-usage后回车,然后输入S回车,仔细查看Current allowed actions:这行,后面只有Certify说明S权限去掉了。 - 输入
Q退出。
- 输入
- 修改 uid 信息。算号程序默认的 uid 是
Dummy <[email protected]>, 需要修改成自己的信息。- 输入
adduid后回车。 Real name不一定使用真名,可以随便填写一个想要展示给别人看的名字。Email address输入邮箱地址。Comment个性签名或者别的想展示给别人看的信息,没什么想写的可以留空,直接回车跳过。- 此时会将 uid 信息再展示一次,确认无误后输入
O确认。此时应该有两个 uid 信息,第一个是默认的Dummy <[email protected]>, 第二个是刚刚添加的自己的信息。- 如果有想修改的,可以根据提示输入字母修改对应的信息。
- 输入
uid 1选中第 1 个 uid, 此时Dummy <[email protected]>左侧会多出一个*号,输入deluid删除这个 uid, 再输入y确认删除即可。
- 输入
- 输入
save保存并退出。
这样 EC 和 ES 就合并好了,接下来参考上面的步骤,分别将 EA 和 CE 也作为子密钥合并给 EC.
全部缝合完后,执行 gpg --list-keys --keyid-format LONG 查看主密钥和三个子密钥的 Key ID 和权限信息是否正确无误,输出应该类似下面这样:
| |
忽略下面的三个密钥对信息,后续会清理掉。只关注 <EC_ID> 的权限,uid, 子密钥及其权限。确认无误后:
- 导出合并完成的四合一密钥:
gpg --armor --export-secret-keys <EC_ID> > EC.asc, 其中<EC_ID>是主密钥的 Key ID. - 删除整个 gpg 数据库,清理掉无用的中间数据:
sudo rm -rf ~/.gnupg. - 重新导入四合一密钥:
gpg --import EC.asc.
再次执行 gpg --list-keys --keyid-format LONG 查看密钥信息,应该只有很干净的四合一密钥了,类似这样:
| |
§1.2.3 编辑密钥
缝合完密钥后,四合一密钥还不能直接使用。因为刚才 DIY 出来的密钥对,主密钥和子密钥没有交叉签名,后续使用时会导致子密钥无法被信任,而且还没有给密钥配置密码,缺少一道安全保险。下面来分别善后。
首先来看交叉签名:
- 终端输入
gpg --edit-key <EC_ID>, 其中<EC_ID>是 EC 主密钥的 Key ID. - 输入
toggle, 回车确认,进入私钥编辑模式。 - 观察
ES子密钥的顺序,假设是第 1 个,那么输入key 1(同理,如果 ES 子密钥是第 2 个,那么这一步输入key 2, 以此类推), 选中ES子密钥,此时 ID 旁会出现一个*号。 - 输入
cross-certify, 进行交叉签名。如果之前已经配置过了密码,此时会要求验证。 - 完成后输入
save保存并退出。
这一步是必需的,如果没有交叉签名,有可能会出现 ES 子密钥被盗,然后挂在别人的主密钥下面,冒充你来签名的情况。而且没有交叉签名的 ES 子密钥,在签名 git commit 时,git log 中会警告 gpg: WARNING: signing subkey <ES_ID> is not cross-certified.
然后是配置密码:
- 终端输入
gpg --edit-key <EC_ID>, 其中<EC_ID>是 EC 主密钥的 Key ID. - 然后输入
passwd, 回车确认。 - 在接下来的交互式界面中,依次给主密钥和子密钥配置密码。可以给主密钥和每个子密钥配置相同的密码,也可以各不相同。使用键盘的方向键来切换选项。
- 配置完成后输入
save保存并退出。
这一步理论上不是必需的,如果不担心私钥文件泄露的问题,或者确信私钥文件不会被盗,那么可以不配置密码,不影响使用。但是风险在于,万一日后私钥文件真的泄露了,没有密码的私钥,攻击者是可以直接使用的。
而给密钥配置密码后,万一私钥文件泄露,攻击者在不知道密码的情况下是无法使用的。设置一个高强度的密码,可以在私钥文件泄露后,密码被黑客破解前,争取到一段足够长的时间,来发布吊销声明,或者做一些别的补救措施。
特别地,如果是给已有的没有密码的密钥配置密码,或者需要修改密码的话,需要确保主密钥是导入的状态,配置好密码后,需要重新导出私钥,旧的私钥文件是不包含新的密码信息的。
- 吊销证书,公钥和 SSH 公钥不需要重新导出,它们不包含密码信息。
§02. 生成吊销证书
现在来做最后一份保险措施。万一日后因为某种原因,主密钥被盗了或者丢了,需要废弃整个密钥对,那么就需要一个吊销证书:
| |
然后 GPG 会提示选择一个理由,按需选择即可。然后输入主密钥的密码(如果有的话)。
最终生成的 EC_revocation.asc 是明文的文本文件,一定要离线保存在安全的介质中。
后续如果有一天(希望永远不要有这么一天……),电脑丢了或者私钥泄露了,需要用到吊销证书:
- 在已导入了待吊销密钥的公钥的电脑上,导入吊销证书:
gpg --import EA_revocation.asc. - 分发通告,将更新后的公钥上传到密钥服务器:
gpg --keyserver hkps://keys.openpgp.org --send-keys <EC_ID>.
这样就完成了密钥的吊销。之后当别人尝试使用你之前分发出去的公钥加密,或验证你的签名时,他们的 GPG 客户端会自动收到“该密钥已作废”的警告。
这一步生成的吊销证书是用来吊销主密钥的,吊销主密钥后,整个密钥对(包括主密钥对应的所有子密钥)都会被宣布作废。
如果只想作废某个子密钥,那么不需要用到吊销证书:
- 在导入了主密钥的电脑上,编辑密钥:
gpg --edit-key <EC_ID>.- 选中子密钥:
key 2(假设要吊销第 2 个子密钥),被选中的子密钥 ID 旁会有个*号。- 输入
revkey吊销选中的子密钥。- 输入
save保存并退出。
§03. 导出备份
珍贵的四合一密钥至此已经准备好了,但是最好不要直接开始使用,先做好备份措施:
- 导出完整的四合一私钥备份:
gpg --armor --export-secret-keys <EC_ID> > EC_full_secret.asc. - 上一节生成的吊销证书
EC_revocation.asc. - 导出仅包含子密钥,不含主密钥的三合一私钥文件:
gpg --armor --export-secret-subkeys <EC_ID> > subkeys_only.asc. - 导出用于复制到远程服务器鉴权的 SSH 公钥:
gpg --export-ssh-key <EC_ID> > EA_openssh.txt. 这个命令会自动提取出EA子密钥并转为 OpenSSH 格式,然后写入EA_openssh.txt文件。 - 导出完整公钥,用于 GitHub 或个人网站等地方公开:
gpg --armor --export <EC_ID> > EC_public.asc.
§04. 清理善后
确认备份完成后,直接删除本地的 GPG 数据库:
| |
这一步是为了:
- 确保本地数据库的干净,避免中间缝合密钥时,伪装的时间戳信息残留导致的一些奇怪 bug.
- 剥离主密钥。后续使用时,仅导入子密钥使用,主密钥始终离线保存,只在需要时才导入使用(比如添加或吊销子密钥),更加安全。
然后彻底删掉除了【03. 导出备份】小节提到的 5 个文件之外的所有文件,比如算号程序生成的原始靓号文件,中间缝合密钥过程中导出的 EC.asc 等。
§05. 结语
经过本文的操作,现在本地的 GPG 数据库应该是还未使用的干净状态,且有以下文件以备使用:
- 完整的四合一私钥备份
EC_full_secret.asc - 吊销证书
EC_revocation.asc - 仅包含子密钥,不含主密钥的三合一私钥文件
subkeys_only.asc - 用于复制到远程服务器鉴权的 SSH 公钥文件
EA_openssh.txt - 用于 GitHub 和个人网站的公钥文件
EC_public.asc
其中 1 和 2 请确保离线保存在安全的存储介质中,除非万不得已,否则不会用到。
下一篇文章将会具体介绍如何使用 3 4 5 这三个文件。
参考链接: