使用 SSH 代替 GPG 密钥对
我吊销了我的 GPG 密钥,并从今天开始全面切换到基于 SSH 密钥对的方案。这篇文章会解释 How & Why。
1. 复杂的 GPG
在最开始,我们必须承认,GPG 确实是安全的,而且在承诺的三个领域,一直表现得还凑合。
那 GPG 的问题在于哪儿呢?它太老了。由于它太老,GPG 有非常多的累积问题:
- 历史包袱繁多,比如因为各种已经说不清的原因,光包长度在 GPG 种就有至少 2^3 = 8 种编码方式。
- 新旧社区分化:2022 年仍然有教程在推荐使用 RSA 算法来生成 Key,即使在 best practice(包括默认)都强烈推荐使用 Ed25519(ECC + Curve25519) 生成新的 Key。
- GPG 因为各种兼容性问题,在操作中支持非常多已经废弃,甚至在现代是认为已经失效的密码学原语,这让整体操作和维护难度直线上升。
如果你想要用好 GPG,你需要先成为一个密码学“专家”:搞清何为非对称加密只是入门课,你还需要知道 4 种密钥类型是干什么用的,为什么直接使用 Certify 类型的密钥不安全,ASE 三种类型究竟是什么场景,吊销密钥又是一个什么过程。而即使你已经成为了密码学高手,在操作复杂的 GPG cli 过程中,你还是会经常遇到各种操作失误导致的失败配置,以及解决调配各种 GPG 的环境的麻烦。是的,GPG 套件也跟 OpenSSL 一样,非常难“用对”。
如果你对为什么我不再喜欢 GPG 感兴趣,而恰好你又有一定程度的密码学基础,可以阅读下面的文章:
- https://www.latacora.com/blog/2019/07/16/the-pgp-problem/
- https://words.filippo.io/giving-up-on-long-term-pgp/
- https://blog.cryptographyengineering.com/2014/08/13/whats-matter-with-pgp/
我同样建议阅读对立面的,对 Latacora 批评的反驳: https://articles.59.ca/doku.php?id=pgpfan:tpp
而实际上,我们需要使用 GPG,核心就是为了满足上面提到的三个诉求:加密(Encryption),签名(Sign)和认证(Authentication)。幸运的是,现在是 2024 年,我们在现代密码学的加持下,已经有了完美支持上面三个需求,且很难用错的基础设施:SSH。
本文就将探讨如何使用 SSH 来达成前两个效果。由于认证是 SSH 协议的基石,本文不再详细展开。
2.1. 签名
一切的开始,首先确定你的 OpenSSH 版本超过 8.0:
|
|
如果你使用的是现代操作系统(包括 Windows 10+),你应该都有一个符合要求的 OpenSSH 版本。
签名的过程非常简单,假定你需要对 about.txt
进行签名,你可以直接执行:
|
|
其中,-f
指向了用于签名的私钥,-n
是一个任意字符串,用于指定文件的命名空间。为何要使用命名空间?防止有人对签名做篡改。
如果你点开签名文件 about.txt.sig
,你会看到类似于如下的内容:
|
|
验证这个签名也非常简单,可以使用:
|
|
其中,你需要提供一个 allowed_signer
文件,维护<signer> <public key>
的映射,类似于:
|
|
然后在 -f
指定文件,-I
指定签名者,就可以进行验证了。你可以尝试调整 -I
,-n
或者修改文件和签名,看看不同情况的输出如何。
如果需要自定义签名验证流程的话,格式可以参考官方描述,可以看到这个签名的格式非常简单,解析和验证应该较为简单。
特别地,Git 也能使用 SSH 密钥进行签名了,首先先确定你的 Git 版本 >= 2.34,然后参考 Github 的教程 进行配置。至于如何在各个托管仓库中进行展示,可以自行寻找教程,比如 Github 的教程在这。
2.2. 加密
虽然我们可以使用 OpenSSL 来加密,但是我推荐使用 age
来完成这项工作。
age
是一个非常现代的工具,所有加密原语都是(至少截至 2024-04-13)业界最好的,同时标准里面的设计也基本都是业界最佳实践。
age
的使用很简单,我就不在本文过多赘述了,感兴趣的欢迎自行查看项目主页。
同样,根据惯例,我也会在本文发布我的公钥。欢迎使用上面提到的方式使用 SSH 公钥给我发一封邮件。
|
|
这也是我用于签名 Git commit 的密钥,所以你也可以在 https://api.github.com/users/lxdlam/ssh_signing_keys 看到。