原文地址:简明 GPG 概念 - 知乎缘起我在 Git 提交了一个变更,可是 Git 只记录了我的姓名和邮箱,怎样才能确保不被坏人冒名顶替? 你可能想到了:加个数字签名呗。 没错,GPG 就可以干这个活儿。 基础GPG 的内涵要比 SSH 登录密钥丰富得多。我们…https://zhuanlan.zhihu.com/p/137801979
47 人赞同了该文章
缘起
我在 Git 提交了一个变更,可是 Git 只记录了我的姓名和邮箱,怎样才能确保不被坏人冒名顶替?
你可能想到了:加个数字签名呗。
没错,GPG 就可以干这个活儿。
基础
GPG 的内涵要比 SSH 登录密钥丰富得多。我们先看看 GPG 密钥能干什么:
可以看出来,SSH 登录密钥实际上只用到了 [A] 这一项能力,所以简单易懂。现在让我们挑战更高难度的 GPG 吧!
如果你对非对称加密没有概念,可以看看这篇通俗易懂的解释:
如何用通俗易懂的话来解释非对称加密?283 赞同 · 27 评论回答
加密与签名
▲ 加密过程中:
- 别人用你的公钥加密数据后发给你;
- 这些数据只有你的私钥能解密。
▲ 签名过程中
- 你先用摘要算法(例如 SHA-256)给数据提取出一个指纹(摘要、哈希值);
- 你用私钥,把这串哈希值加密,得到一个数字签名,和文件一起发出去;
- 别人收到文件+签名后,先计算文件的哈希值;
- 别人用你的公钥,从数字签名中解密出你给的哈希值,和他计算的对比,如果两者一致,那么签名就是有效的。
注意这两个过程中,私钥的作用是相反的:
- 加密方案中的私钥:用于解密信息
- 签名方案中的私钥:用于加密信息
GPG 密钥的能力中, [C]、[S]、[A] 均属于签名方案,只有 [E] 是加密方案。
你可能会奇怪:公私钥是可互换的吗?
- 是的!数学上来讲,它们是等价的,可以角色互换。
- 一个加密另一个就能解密。
- 但是!在具体的算法实现上,两者有很大不同。
- 我们要保证加密-解密的高效,同时保证抗破解的安全。
- 实际应用中的私钥与公钥生成,算法设计上是有偏向的。
- 这也是为什么 GPG 会为密钥分出不同用途,用户方便,算法设计者也方便。
PGP 与 GPG 是啥关系?
我们这里简化概念,你只需要了解这几点就好了:
- GPG
- 就是 GnuPG 就是 GNU Privacy Guard,是一款自由软件。
- 我们用它完成各种具体操作:生成密钥对、签名/验证、加密/解密。
- OpenPGP
- 是一套开放的互联网标准。
- GPG 是这套标准的实现。
- OpenPGP 有一套信任体系:Web of Trust(信任之网)。
- OpenPGP 有一套开放的公钥服务器池:SKS Keyservers,用户可以自由上传自己的公钥。
如今最早的商业软件 PGP(Pretty Good Privacy)只活在维基百科中,几乎所有 PGP 都指的是 OpenPGP。你知道就行,我们不咬文嚼字。
细节
GPG 的吊销证书
如果你的 SSH 登录私钥丢了,你只需要告诉对应服务器就行了,让服务器删除相应公钥。
除非你的组织要求很严格,否则你应该不太想在 SSH 上倒腾有效期、吊销证书、短期证书这些问题。
而 GPG 的公钥因为需要被广为人知,所以一旦私钥丢了就很麻烦,得通知所有人“我这个公钥不能信了”。
因此 GPG 多了一个“吊销证书”(revocation certificate),拿着这个证书你就能证明“我就是我,我的身份证丢了,你们赶紧把丢了的那个作废”。
而其他人呢,看到你在原来的证书基础之上,又发布了这么一张吊销证书,就知道原来的证书已经不可信了。
GPG 的过期时间
GPG 密钥对可以设置“过期时间”,也可以永久有效,你可以随便设置。
当你发布公钥的时候,里面就包含了过期时间信息。
别人也就知道,在这个时间过后,该证书的签名是无效的。
很多人会选择一个懒人方案:给主密钥设置一个很长的过期时间,给子密钥设置一个较短的过期时间。
GPG 的子密钥
GPG 非常明智地设计了 subkey 机制,让用户免去给自己设计证书信任链的麻烦,我们先看个例子:
- 主私钥必须有【认证 [C]】这个能力,且这项能力只能属于主私钥。
- 主私钥可以同时拥有【认证 [C]】、【签名 [S]】、【身份验证 [A]】三项能力。
- 子私钥可以同时拥有【签名 [S]】、【身份验证 [A]】两项能力。
- 【加密 [E]】能力必须属于独立的子私钥,因为加密用的是不同的算法变体(和签名不一样)。
看到这里你可能已经明白了,一个 GPG 密钥对之所以可以有这么多能力,是因为它本质上是若干密钥对的集合,只不过它们被封装到了一起。
所有子密钥的有效性,都来自于主密钥的认证。
子密钥可以在没有主密钥的情况下单独使用。
子密钥的公钥
你可能会奇怪,这些子密钥的公钥呢?
实际上子密钥的公钥是存在的,也可以导出,但 GPG 为了防止用户误用,把这项功能藏得很深。
毕竟,如果子密钥的公钥也单独使用,那就跟自己设计一个证书信任链没什么区别了。
我们在实际使用中,导出的“公钥”实际上是一组公钥,其中就包括了被主私钥【认证 [C]】后的各个子公钥。信任关系很明确,不是吗?
单一公钥机制对于服务端的验证特别友好,无论用户有多少个子密钥,服务器只需要一份证书(公钥)就够了,而不需要额外开销,去查询这份证书是哪个上级签发的。
比如 GitHub、GitLab 的 GPG 功能,就只认用户上传的 GPG 公钥,而不从任何信任体系中获取上级证书。
子密钥的吊销
从图中可以看出来,整个“密钥串”只有一张吊销证书,一旦这张证书发布,主密钥即被吊销,连带着子密钥一并吊销。
那子密钥丢失了怎么办呢?
GPG 不允许用户生成子密钥的吊销证书,而是把变更都放在唯一的公钥中,简洁且不易出错。
你只需要编辑“大”密钥,将子密钥单独吊销,然后重新发布公钥即可,这样大家就都知道了。
GPG 的用户身份(UID)
uid 即 user id
一个 uid 由三个部分组成:
- 全名(一般是实名)
- 注释(用 ( ) 包括)
- 邮箱地址(用 < > 包括)
这三个部分都是可选的,只要有一项即可。
例如:
Han Meimei <email@example.com>
Han Meimei (Work) <hanmm@company.com>
Han Meimei (Product) <hanmm@company.com>
- 一个密钥可以有多个 uid,方便不同场合使用。
- uid 与哪个子密钥无关,uid 是作用于整个密钥的。
- uid 可以随时添加,但已有的 uid 不能修改,只能单独吊销。
- uid 单独吊销后,只需要重新发布公钥即可。
如果你希望只用一个密钥,但某些身份不公开,你需要小心配置你的公钥,不过最好还是另外用一个密钥。
应用
GPG 与 Git
Git 本身的默认设置非常简单,你提交(commit)到本地仓库的时候只需要提供一个用户名和邮箱地址,Git 会据此记录本次提交。
如果有人稍微懂点歪脑筋,用同你一样的用户名和邮箱提交,就有可能冒名顶替。
比如这个演示: https://github.com/jayphelps/git-blame-someone-else
当然,如何验证提交者身份,各大平台都有自己的措施,但其中有一种方法是通用的,就是 GPG 签名。
你用私钥签了名,提交并推送,服务器就会用公钥来验证,看看这个提交是不是“真的是你干的”,于是别人就不能冒名顶替了。
加密算法的选择
还记得我们之前提到的加密与签名概念吗?实际上,非对称加密的应用主要就是这两大方向,具体算法会针对两个方向优化出不同的变体。
比如椭圆曲线加密算法中的 Curve 25519 算法,用于签名时,变体为 ed25519;用于加密时,就是原装的 cv25519。
RSA 用于加密和签名时都叫 RSA,但是具体实现还是略有不同。
以实际的 GPG 密钥举例:
可以看出来,只有 [E] 类是 cv25519 算法,其它都是 ed25519 算法。
最近几年 ed25519 在 SSH 登录中很流行,因为这个算法在安全性(相对 RSA 2048)更好的同时,性能大幅领先,这对于 SSH 快速登录很有意义。
但从专业人士的观点来看:( https://security.stackexchange.com/questions/143083/ssh-key-strength-factor-besides-key-length-say-ed25519-vs-rsa-4096 )
RSA 4096 的强度还是要大于 ed25519 的。
笔者粗略总结为:
可以放心地说,在 2030 年之前,前两名的安全性都是充足的。具体的选择,往往取决于使用场景。
- RSA 对随身安全硬件的兼容性较好,比如 YubiKey 5 支持 RSA 4096,但不支持 Curve 25519。
- RSA 2048 的适用性很好,很多硬件为其提供了加速。
笔者建议,主密钥使用 RSA 4096,子密钥可以任意选择:
- 如果你使用 GPG 主要是用于签名,不在乎一时的性能,选择更安全的 RSA 4096 不会有错。
- 如果你需要兼容性,需要面向广大的受众,那么 RSA 2048 是保守稳妥的选择。
- 如果你不关心电脑以外的设备,不关心老旧的软件,那么可以用更快的 Ed25519。
附:概念对比
同义词
证书 = 公钥
指纹 = 摘要 = 哈希值 = 杂凑值
密钥能力 = 密钥用途
GPG 与 SSH
- SSH (Secure Shell) 用于交互通信过程中的安全,是双向的。
- GPG (GNU Privacy Guard) 既可用于加密,也可用于签名,这些都是单向的。
- 私钥在 SSH 中被称为 private key,而在 GPG 中被称为 secret key。
- 公钥则都叫 public key。
- 一般来讲,SSH 的公钥只有你的服务器知道,别人也不需要知道。
- GPG 的公钥就不一样了,巴不得全世界知道,告诉大家“认准这个证书,谨防假冒!”
- 对于 SSH,建议一站一钥,万一丢了也好处理。
- 对于 GPG,一把主钥+多个子钥即可。
- 如果你明白了 GPG 的多用途,完全可以把 GPG 的子密钥 [A] 用于 SSH 登录。
SSH 虽然用起来简单,但毕竟是安全通信,实际发生的登录过程并不简单:
- 对称加密算法的性能,比非对称加密算法高很多,因此通信的主要内容,还是要用对称加密。
- 对称加密需要双方使用相同的密码,如何在不告诉对方密码的前提下,互相使用一样的密码(会话密钥),就要用到密钥交换算法。
- 在折腾完这一通,建立起安全通信后,才开始互相的身份验证。首先是用户拿手里的服务器公钥,验验服务器是不是真货。
- 用户第一次连接服务器时,手里没有服务器的公钥,因此客户端会问用户要不要信任服务器发来的公钥。这是经典的中间人攻击切入点。
- 到现在用户的 SSH 私钥才派上用场,服务器拿它手里的用户公钥,验验用户是不是本尊,完成登录。
- 即使登录上了,双方也会时不时更换会话密钥,保证通信的安全性。
GPG 与 TLS
TLS 基于“集中式”的信任链,它依赖于操作系统出厂内置的【根证书】,用户默认信任根证书,因此才能信任根证书签发的二级、三级证书(公钥)。
要给网站启用 HTTPS 流量加密,我们需要先【申请】 TLS/SSL 证书,这本质上是请求签发机构【信任】你。用户信任你的上级单位 → 上级单位信任你 → 用户信任你。
GPG 并不搞集中式,虽然开源世界中有不少 GPG 公共服务器组成的【公钥服务器池】(Web of Trust),但 GPG 将信任与否的决定权交给用户。
当然,你也可以把自己的公钥上传到服务器池,方便他人悉知。
引用 依云 的一句话:
https://bbs.archlinuxcn.org/viewtopic.php?id=9906
世界上有两种密钥信任体系,PGP 的 web of trust 和 TLS 证书的 CA。前者是社交化的,相信你所相信的人。后者是相信权威。
参考
如果你还是有所疑惑,请阅读这一系列文章:
《用 PGP 保护代码完整性》
- https://linux.cn/article-9524-1.html
- https://linux.cn/article-9529-1.html
- https://linux.cn/article-9607-1.html
- https://linux.cn/article-10402-1.html
- https://linux.cn/article-10415-1.html
- https://linux.cn/article-10421-1.html
- https://linux.cn/article-10432-1.html