Skip to main content

【未完成】OpenPGP/GPG 实用不完全指北

介绍

什么是 GPG?

1991年,Phil Zimmermann,开发了加密程序 PGP(Pretty Good Privacy)。
但 PGP 是专有的,所以 1997 年互联网工程任务组 (IETF) 制定了 OpenPGP 这一个开放标准,使得任何人都可以自行实现。

于是 1997 年 GUN 开发了 PGP 替代品:GunPG(GNU Privacy Guard),简称为 GPG。
GnuPG 是 OpenPGP 协议的开源实现,是来自 GNU 项目的免费加密软件,可帮助人们确保其数据的机密性、完整性和保证。

Arguing that you don't care about the right to privacy because you have nothing to hide is no different from saying you don't care about free speech because you have nothing to say. – Edward Snowden


GPG 有什么用?

GPG 是一种非对称加密程序。

一般来说,GPG 通常用于两件事:签名和加密。

  • Certify 证明:其实就是签名。对文件签名叫做 Sign,对密钥签名叫做 Certify;
  • Sign 签名:对某个文件进行签名,以确定它是你发送的,且没有经过篡改;
  • Encrypt 加密:对某个文件进行加密,以便只有有私钥的人(你)能够解密;
  • Authentication 验证:私钥可以解密公钥加密的数据,公钥可以验证私钥签名的数据;


在哪下载 GPG?


什么是密钥/Key?

不幸的是,密钥/key 这个词是模棱两可的。
它既可以指允许加密、解密、签名和验证发生的数学结构,也可以指包含这些数学结构的相当大的数据块,以及与之相关的人的信息、额外的子密钥等等。
在非对称加密算法中,这也并不会帮助你区分公钥和私钥。

因此关于大块的数据,最好把它们称为证书,这样密钥这个词就可以明确地被认为只是指数学结构。
可惜,没有多少人遵守这一点。

注:有其他读音相似的写法,秘钥、密钥、密匙,一般写作密钥(mì yào)。

什么是证书?

证书是一种大型数据结构,包含一个或多个密钥,以及标识用户、指定的撤销者、已担保此证书的人等的可选信息。

什么是非对称加密与公钥交换?

在一般的非对称加密算法(如 RSA、ECC)中,有“公钥”和“私玥”两种密钥,其中私钥可以导出公钥,但公钥无法反推私钥。
公钥对所有人公开,而私钥由你自己持有。我们这里就不谈某种算法是否安全了。

如果用公钥加密数据的话,那么只有私钥可以解密;
如果使用私钥签名数据的话,那么使用公钥就可以验证签名是否真实。

即 Alice 向 Bob 发送消息,需要使用 Bob 的公钥加密数据,确保数据只有 Bob 能够解密。

虽然看起来很美好,但是如果 Alice 要向 Bob 发送私人消息 M,
那么 Alice 如何在一开始拿到 Bob 的公钥,同时确保 Bob 的公钥是正确的,而不是 Charlie 仿冒的。

于是在非对称密码学中,公钥交换是最薄弱的一环:我如何确定这个公钥是真的。
不存在一种在线的、可靠的、不事先协商的公钥交换机制。
所以公钥必须事先以一种广泛、可信任的方式发布。

HTTPS (SSL/TLS) 中的公钥交换 ——   PKI 中的 CA 机构

为了解决公钥交换不安全的难题,出现了 PKI(公钥基础设施),而 CA 机构(数字证书认证机构)则是其中的一部分 。
在 HTTPS 中数字证书由声誉良好的 CA 发行,通过交叉签名的方式互相和一层一层认证。

最底层的 CA 称为 Root CA,经过各种严格的认证后(AppleMircosoftChrome)Root CA 被认为是可以信任的,于是操作系统和浏览器内置并信任了它们自签名的 Root Certificate 根证书(包含公钥等信息)。

然后 Root CA 使用 Root Certificate 为下一级 CA 颁发的 Intermediate Certificate 中间证书进行签名,当你从下一级 CA 或下下一级 CA 购买证书时,CA 会使用 DNS 记录等方式来验证你的域名所有权,然后 CA 会使用中间证书为你的证书进行签名,如此一来,就构成了一条信任链。为了保证广泛可信任,部分 CA 还会进行一些交叉签名。

Root Certificate -> Intermediate Certificate -> Intermediate Certificate -> Yours Certificate

当你访问 HTTPS 站点时,浏览器会一层一层检查证书是否合法,最后和系统内置的证书进行验证。

所以你大概也能明白,为什么安装 Root Certificate 是一种非常危险的操作了。
举一个例子,暴雪战网、奇游加速器、迅游加速器都会在你的设备上安装 Root Certificate,而你却毫不知情。
Windows 系统运行 certmgr.msc 便能看到你系统上的所有证书。

而且 CA 机构就是可信任的吗?
如果 CA 机构擅自为你颁发证书怎么办? 由此诞生了 CAA 记录。
如果 CA 签发假证书怎么办?Q05OSUMg6K+B5Lmm6KKr5ZCK6ZSA5ZKMIFdvc2lnbiDooqvlkIrplIA= 了解一下,至少你不应该信任任何一个 5Lit5Zu95aSn6ZmG 的 CA 机构。
为了让 CA 受到更多的公众监督 RFC 9162 定义了 Certificate Transparency (证书透明度)……

交叉签名可参考:https://letsencrypt.org/certificates/
PKI 可参考:https://blog.cloudflare.com/how-to-build-your-own-public-key-infrastructure/

OpenPGP 中的公钥交换

等一等,OpenPGP 可没有什么商业 CA 机构、内置证书和严格的认证,我们又该如何交换公钥?

好吧 OpenPGP 中确实没有那么强而有力的约束,因为这更像是一种 nerd 玩的东西,于是我们有 Web of Trust。

Web of Trust 信任网络

Web of Trust 的概念最早是由 PGP 创建者 Phil Zimmermann 于 1992 年在 PGP 版本 2.0 的手册中提出的:

As time goes on, you will accumulate keys from other people that you may want to designate as trusted introducers. Everyone else will each choose their own trusted introducers. And everyone will gradually accumulate and distribute with their key a collection of certifying signatures from other people, with the expectation that anyone receiving it will trust at least one or two of the signatures. This will cause the emergence of a decentralized fault-tolerant web of confidence for all public keys.

随着时间的推移,你会从其他人那里收集到一些密钥,你可能想要指定他们为受信任的介绍人。其他人也会各自选择自己的受信任的介绍人。而且每个人都会逐渐积累并分发自己的密钥和其他人的认证签名,希望收到签名集的人至少会信任其中的一两个签名。这将为所有公开密钥建立一个去中心化的容错信任网。

Web of Trust 的运作方式是,所有 OpenPGP 兼容实现都包括一个证书审查方案
OpenPGP 证书(包括一个或多个公钥以及所有者信息)可以由其他用户签名,通过签名,这些证书就相互信任了。
而这通常在密钥签名聚会上完成(是的,某种阴暗的 nerd 线下密钥交换会)。

于是每个用户都可以选择一些他们信任的其他用户作为可信介绍人,并用自己的私钥对他们的公钥进行签名,而可信介绍人还有自己的其他可信介绍人。这样层层递进就形成了一个包含多个签名者和被签名者的网络,用户可以根据网络中的签名数量和质量来判断一个公钥是否值得信任。

你也许并不认识这么多人,没有这么多的可信介绍人,或者说并不是所有公钥都是平等和可信赖的。有些公钥可能是伪造或篡改过的。
因此这个信任度是可以调整的,信任度量有四个等级:

  • 未知:表示用户没有对该公钥进行信任度量,或者不知道该公钥的所有者。
  • 不信任:表示用户不信任该公钥的所有者,或者认为该公钥是无效或危险的。
  • 部分信任:表示用户对该公钥的所有者有一定程度的了解和信任,但不足以完全信任。
  • 完全信任:表示用户对该公钥的所有者有充分的了解和信任,认为他们是可靠和诚实的。

除了对其他公钥进行信任度量之外,每个用户还可以对自己的密钥环中的其他公钥进行有效性度量,这是一个客观的过程,取决于该公钥有多少其他用户用自己的私钥进行了签名。有效性度量也有四个等级:

  • 未知:表示该公钥没有被任何其他用户签名,或者没有被用户信任的其他用户签名。
  • 无效:表示该公钥已经过期或被撤销了,或者被证实是伪造或篡改过的。
  • 边缘有效:表示该公钥被一些部分可信或完全可信的其他用户签名了,但没有达到用户设定的阈值。
  • 完全有效:表示该公钥被足够多的部分可信或完全可信的其他用户签名了,达到了用户设定的阈值。

当用户想要使用一个公钥时,他们可以根据其有效性度量来判断是否可以信赖它。一般来说,只有完全有效的公钥才会被认为是可靠和安全的。


Key Server 密钥服务器

等一下,你可能会说,我没有参加过什么线下密钥交换会,也不认识其他 nerd,我该怎么办?

别担心,我们还有密钥服务器。

用户可以将自己的公钥上传到密钥服务器上,或者从密钥服务器上下载其他用户的公钥。密钥服务器会为每个公钥分配一个唯一的标识符,通常是一个长字符串,称为密钥指纹(key fingerprint)。

在你生成你的密钥对后,立即将你的公钥和邮箱名称等信息上传到密钥服务器,这样就出现了一种基于先来后到的信任系统。
你输入一个邮箱查询就能返回该邮箱对应用户的公钥。

但是:

  • 密钥服务器不能保证公钥服务器上存储的公钥都是有效和安全的,有可能存在过期、撤销、伪造、篡改等问题。
  • 密钥服务器不能强制要求用户对自己或他人的公钥进行签名,有可能导致信任网络的稀疏和不完善。
  • 密钥服务器不能防止恶意用户上传大量无效或虚假的公钥,有可能造成信息污染和混淆。
  • 要是我在 A 密钥服务器存储了我的公钥,但是 B 服务器内有我的假冒公钥怎么办?

当然一些密钥服务器也会采取一些手段来确定你是你,比如 keys.openpgp.org 要求验证电子邮件后才能上传公钥。

一些可用的密钥服务器:

或者你也可以采取一些其他办法,比如在你的家门口贴上你的 GPG 密钥,在你的网站上贴上你的 GPG 密钥并用 Wayback Machine 制作一个快照以防止篡改……

不过其实 Github 也可以上传 GPG :Adding a GPG key to your GitHub account
然后你就可以在 https://github.com/{user}.gpg 访问 gpg 公钥。

可以看出,OpenPGP 的信任模型是“信任你信任的人所信任的人”,而 HTTPS 的信任模型则是的“信任权威机构信任的人”。

说了这么多,让我们开始使用 GPG 吧!

创建密钥对

运行以下命令以生成您的密钥对。

gpg --full-gen-key --expert

  • --expert  默认情况下,GPG 仅显示 RSA 和 DSA,使用此选项来使用更加现代的 ECC 算法。
1.选择密钥类型

然后 GPG 会问你想要什么样的密钥,你应该选择 1(RSA and RSA)或者 9 (ECC and ECC)。

若要保证最佳兼容性,请选择 RSA 4096 位。
若要安全与性能兼得请选择更新的 ECC Ed25519(EdDSA)。

ECC 更新更小更快更安全,但更新往往意味着更不兼容。
使用 ECC 时请避免使用 NIST P-256/384/512 系列曲线,因为 NIST 无法说明他们怎么获取的这组曲线,更推荐使用 25519 系列。

# gpg --full-gen-key --expert
gpg (GnuPG) 2.3.3; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gpg: 目录‘/root/.gnupg’已创建
gpg: 钥匙箱‘/root/.gnupg/pubring.kbx’已创建
请选择您要使用的密钥类型:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC and ECC
  (10) ECC(仅用于签名)
  (11) ECC (set your own capabilities)
  (13) Existing key
  (14) Existing key from card
您的选择是? 
2.设置到期时间

接下来,GPG 会询问您到期时间。
如果您是一位经常面临钥匙被盗风险的知名人士,那么也许您想设置一个到期日期。
否则,我认为没有理由这样做,只要你妥善保护你的私钥。

3.提供用户信息

提供您的姓名、电子邮件和注释。
这很重要,以后无法更改。

4.设置私钥密码

如果您的私钥被盗,这会为您提供最后一层保障。
输入一个好的长密码并记住它。如果您忘记了此密码,您将无法解锁您的私钥。

5.生成结束
gpg: 密钥 FF299B9F105605D3 被标记为绝对信任
gpg: 吊销证书已被存储为‘/root/.gnupg/openpgp-revocs.d/7188CBCB53256DDFC8EB2765FF299B9F105605D3.rev’
公钥和私钥已经生成并被签名。

pub   ed25519 2022-10-29 [SC]
      7188CBCB53256DDFC8EB2765FF299B9F105605D3
uid                      a10935336 (10935336) <109@pha.pub>
sub   cv25519 2022-10-29 [E]

可以看到,GPG 为公钥创建了一个唯一标识符(FF29 9B9F 1056 05D3)。这是你的 Master Key ID,和邮箱一样作为你的User ID。
此唯一标识符采用十六进制格式。当有人想要下载您的公钥时,他们可以使用您的电子邮件地址或此十六进制值作为唯一标识符。
唯一标识符是从你的公钥指纹中截取的(7188 CBCB 5325 6DDF C8EB 2765 FF29 9B9F 1056 05D3)。

 如上所示,GPG 还在 ~/.gnupg/openpgp-revocs.d/ 创建了一个吊销证书,位于,如果您的私钥不幸泄露,您可以用使用此证书来声明一个公钥不再被使用。
你可以用文本方式打开它看看里面有什么。

吊销证书文件内容节选
这是一份针对此 OpenPGP 密钥的吊销证书:

吊销证书就像是一种“紧急按钮”,用来公开地
声明一个密钥将不再被使用。 一旦一个这样的
吊销证书被发布之后,就不可能撤回。

使用它来吊销这个密钥是一种折衷的方法,或者在私钥丢失的
时候使用。然而只要私钥仍然可被访问,更好的做法是生成一
个新的吊销证书并给定一个吊销理由。更多的细节请参阅
 GnuPG 手册中关于 gpg 命令“--generate-revocation”的描述。

为了避免意外使用了此文件,一个冒号已经被插入到了下面的
5 个短横线之前。在导入和发布此吊销证书之前,请使用一个
文本编辑器来移除此冒号。

:-----BEGIN PGP PUBLIC KEY BLOCK-----
你的私钥

您的私钥存储在 ~/.gnupg/private-keys-v1.d/ 目录下。有两个带有 .key 扩展名的文件。
为什么是两个?

Master Key 主密钥和 Sub Key 子密钥

输入 gpg --list-secret-keys

/root/.gnupg/pubring.kbx
------------------------
sec   ed25519 2022-10-29 [SC]
      0D950AEA342135FAAE189B646DC64EC7EB749FEF
uid           [ 绝对 ] Project Halcyon <admin@pha.pub>
ssb   cv25519 2022-10-29 [E]

sec   ed25519 2022-10-29 [SC]
      7188CBCB53256DDFC8EB2765FF299B9F105605D3
uid           [ 绝对 ] a10935336 (10935336) <109@pha.pub>
ssb   cv25519 2022-10-29 [E]

注意到 [SC] 了吗?
这意味着这个私钥对可以进行 Sign 和 Certify 。同理 [E] 代表只能进行 Encrypt。

拥有 [C] 的私钥为 Maser Key,可以和 Sub Key 互相签名生成独立的子私钥。且 [C] 只有 Maser Key 拥有。
你可以导出单独的 [S]、[A]、[E] 密钥用于不同的用途。
而 [E] 必须属于独立的子私钥,因为算法不一样。

为了避免使用上过于麻烦,GPG 设计了子密钥机制。
每个子密钥都与主密钥相关联,并且由主私钥签名。
主公钥用于验证子公钥的真实性,而子公钥用于加密和解密信息。因此,在使用 GPG 子密钥时,需要同时验证主公钥和子公钥。

忘了 [C] [S] [E] [A] 的含义吗?

  • Certify 证明:其实就是签名。对文件签名叫做 Sign,对密钥签名叫做 Certify,如给其他密钥签名;
  • Sign 签名:对某个文件进行签名,以确定它是你发送的,且没有经过篡改,如给邮件签名;
  • Encrypt 加密:对某个文件进行加密,以便只有有私钥的人(你)能够解密,如给文件加密;
  • Authentication 验证:私钥可以解密公钥加密的数据,公钥可以验证私钥签名的数据,如 SSH 登录;

为什么 [E] 必须是独立的?

非对称加密的应用主要有两大方向:加密和签名。具体算法会针对两个方向优化出不同的变体。例如,椭圆曲线加密算法中的 Curve 25519 算法,用于签名时,变体为 ed25519;用于加密时,就是原装的 cv55191。

RSA 算法用于加密和签名时都叫 RSA,但是具体实现还是略有不同。
以实际的 GPG 密钥举例:可以看到上面的代码块中只有 [E] 是 cv25519 算法,其它都是 ed25519 算法

你的公钥

默认情况下,所有的本地公钥都保存在 ~/.gnupg/pubring.kbx 中,这是一个钥匙环,你可以从中导出公钥。




还没写完








真的有必要吗?

案例A

假设一个最简单的情况,你收到了一封银行的邮件,如何确定这封邮件确实是银行发的,而不是诈骗邮件呢?
你也许会说,看发件人。

但最近我们的 admin 邮箱收到了这样一封诈骗邮件,而发件人的确是 contact AT smtb.jp,也并未显示代发。

image.png

是不是很容易认为,这就是来自官方的邮件,但很可惜这不是。

WA)AG6MT8MSDQIVBPDR7E`Y.png

首先 admin 邮箱并没有日本银行账户,其次邮件内的超链接指向一个奇怪的 URL。
因此可以断定这是一封诈骗邮件,但如果骗子隐藏的更深一点呢?
它还可以使用将超链接文本改成官方网站,而实际链接指向另一个网站,就像这样 https://wiki.pha.pub 是不是看起来更真了?

很可惜,腾讯企业邮并没有导出邮件原始内容的功能(QQ邮箱就有,很不理解),也就没法看到它究竟是入侵了这个银行,还是用了什么手段伪造的此邮件。

要解决这一问题,可以使用 GPG 的 Sign 功能,对发送的邮件进行签名。
收到的人就可以使用发件人发布于官方网站或密钥服务器的公钥进行验证。

但显然,这一点也不用户友好 :(
注定了这些东西只会留在 nerd 之间。(?)

案例B

你知道吗?你的 Git 提交可以被冒充。

比如这个 LMAO 的项目:
https://github.com/jayphelps/git-blame-someone-else

使用后弹出的 You're officially an asshole 更是大幅提高了 LMAO 程度。

冒充 Linus Torvalds 的提交:
https://github.com/jayphelps/git-blame-someone-else/commit/e5cfe4bb2190a2ae406d5f0b8f49c32ac0f01cd7


主要参考