GPG 使用指南
加密与签名
在传输信息时,会面临两个典型的问题:
- 如何保证发出的消息,只能被预期的接收人获取?
- 如何保证收到的消息,确实由预期的发送人发出?
这两个问题不难理解。例如发送的邮件可能会被监听,诈骗分子可以冒充你老板。不一定是在网络上,在任何非面对面交流的情况下,都可能存在这两个问题。例如打仗时,情报可能被敌方监听窃取,收到的命令可能是被敌人冒充的。
对于第一个问题,可以通过加密来解决。就像战争电影中,双方会加密自己的情报和破解对方的情报。
那么如何进行加密呢?最容易想到的方法就是双方约定一套规则(加密算法),发送方将原始信息(明文)按照这套规则转换(加密)成别人看不懂的信息(密文)。其他人不知道这套规则,就无法获取原始信息。而接收人按照约定好的规则,则可以还原(解密)出原始信息。最简单的规则可以是将字母后移三位,例如将 hi
转换成 kl
。这种规则很容易被人猜到,而且人类的脑洞是有限的,或者说优秀密码学家设计出的加密算法是有限的,很可能就英雄所见略同了。而且如果加密算法被想到,就只能再换一种加密算法,估计密码学专家的头发也不太够用。所以一般常用的加密算法都是公开的,再提供一个额外的保密的信息(密钥),解密的时候必须使用相同的密钥才能成功。例如提供一本书,发送方将信息转换为书中的页码,接收方找到每页第一个字即可还原信息。这就是常说的对称加密的基本思路,对称的意思是加解密双方需要具备相同的密钥,解密的人同时也可以加密信息。常见的对称加密算法有:AES、Twofish 等。
对称加密解决了消息本身不能被其他人获取的问题,但带来了一个新问题:密钥又该如何传输呢?为了让后续的消息能被安全的加密,通信双方需要以安全的方式(例如面对面)交换一个密钥,然后就可以使用这个密钥进行安全的通信了。这就给通信带了困难,很多时候可能不方便这样面对面的交流。更致命的是,如果需要与多个人通信,与每个人都要重复一遍这个过程,而且交换的密钥都不能相同。这样交换密钥和管理密钥的成本都很高,甚至是不可行的。
有对称加密,自然就有非对称加密,可以用来解决密钥交换的问题。非对称加密的密钥不是一个,而是一对。其中一个可以发布给所有人,用来加密信息,这个叫公钥。另一个只能自己持有,用来解密信息,这个叫私钥。他们就像一对钥匙和锁头,和生活中的锁头不一样,这里钥匙(私钥)只有一把,而锁头(公钥)到处都是。这些锁头可以发布给所有人,发送方把信息用锁头锁上再发送,最终只有钥匙的持有者可以解开这条消息。常用的非对称加密算法有:RSA、椭圆曲线算法等。
非对称加密看起来没什么问题了,那是不是就不需要对称加密了?因为非对称加密算法的计算一般都很复杂,性能远远不如对称加密,所以这两个结合才是最佳的选择:用非对称加密算法去加密对称加密算法的密钥,然后再用这个密钥使用对称加密算法去加密原始信息。发送信息的过程变成了:
- 接收方有一对公钥和私钥,并将公钥广而告之所有人,例如挂在自己的个人网站上
- 发送方使用对称加密算法加密原始信息
- 发送方使用接收方的公钥加密上一步使用的密钥
- 发送方将这两个信息一起发送给接收方
接收方收到消息后:
- 使用自己的私钥解密出对称加密用到的密钥
- 使用解密出来的密钥解密原始信息
这样就彻底保证了发出的信息只能被预期的接收人获取。至于公钥如何广而告之的问题,我们在后面的 GPG 使用中讨论。
非对称加密也可以反过来,使用私钥加密,同样只有对应的公钥才能解密。这样做一般不是为了保密,因为公钥已经广而告之,所有人都能解开。但是有另一个意义:可以用来做数字签名。发送方使用自己的私钥将信息加密(签名),接收方收到信息后,使用公钥解密(验证)得到原始信息。这样其他人没有发送方的私钥,如果冒充或篡改原始信息,接收方使用公钥验证时将会失败。实际使用时一般不会对消息全文进行签名,因为效率太低,只需要对消息的摘要进行签名,如果消息被篡改,摘要也会变化,验证同样会失败。发送信息的过程如下:
- 发送方有一对公钥和私钥,并将公钥广而告之所有人
- 发送方计算原始信息的摘要
- 发送方使用自己的私钥加密摘要
- 发送方将原始信息和加密后的摘要一起发送给接收方
接收方收到信息后:
- 计算原始信息的摘要
- 使用发送方的公钥解密发送方加密后的摘要
- 验证两个摘要是否相同
至此我们已经介绍了加密和签名的基本概念,有了这两个概念配合使用,就能解决本文开通提到的两个问题。下面将会介绍如何通过 GPG 工具来实现这些。
什么是 GPG
GPG 可以分成两部分来看:
第一部分:GPG 是一个加密、解密、签名、验证工具。前面介绍了加解密的基本概念、相关的算法。但这些概念和算法并不能直接使用,而 GPG 就是一个使用这些算法,对信息进行加密、解密、签名、验证的工具。具体介绍见如何使用部分。
第二部分:GPG 还是一个密钥管理工具,可以用于管理自己的私钥,其他人的公钥,以及提供了一套公钥信任体系。前面加解密概念中提到,公钥要广而告之,GPG 可以帮助我们更加安全高效的交换公钥。具体介绍见管理密钥部分。
另外在使用 GPG 时,可能还会看到 PGP、OpenPGP 等名词,他们是什么关系?PGP (Pretty Good Privacy) 是最早的于 1991 年发布的此类工具。PGP 很好用,但他是商业软件。于是 GNU 计划在 1999 年发布了开源版本的 GNU Privacy Guard (GnuPG 或 GPG)。而 OpenPGP 是于 1997 年制定的一套标准,GPG、PGP 等工具都实现了这套标准。
如何使用
已经做了很多铺垫,接下来先看一下 GPG 是如何使用的。
下面的每一条命令都涉及很多可选参数可以组合使用,这也是命令行工具的灵活之处。不过初次接触可能会觉得很不友好,没有关系,只要了解即可,需要时可以通过 man gpg
快速查询。
这里涉及到的一些概念,将在基本概念中介绍。
生成密钥
命令:
1 | gpg --gen-key |
将会交互式的要求输入真实姓名、邮箱的信息。输入完成后将会生成一对密钥。
查看已有密钥
命令:
1 | gpg (-k | -K) [--with-fingerprint] [--with-subkey-fingerprints] [--with-sig-list] [--with-sig-check] [<key-id>] |
参数说明:
-k
/--list-public-keys
:查看公钥。-K
/--list-secret-keys
:查看私钥。--with-xxx
:根据字面意思,将会额外打印一些信息,具体信息可以通过man gpg
查看。--fingerprint
:打印密钥的指纹,等价于gpg -k --with-fingerprint
。--list-sigs
:打印密钥的签名,等价于gpg -k --with-sig-list
。<key-id>
:仅打印指定的密钥。可选,否则打印所有密钥。如何选择一个密钥请参考:选择密钥
打印出来信息的含义,将在基本概念中进一步说明。
示例:
1 | gpg -k |
加密
命令:
1 | gpg -r <key-id> [-o <output-file>] [-a] -e [<input-file>] |
参数说明:
-r
/--recipient
:指定接受者的公钥 ID,消息将会使用这个公钥进行加密,也就是只有拥有这个私钥的人才能解密信息。可以指定多个,则多个接受者都能解密信息。-o
/--output
:指定加密后的信息输出到哪个文件。可选,如果不指定将会输出到标准输出。-a
/--armor
:将加密后的信息转为可打印的 ASCII 字符。可选,如果不指定将会输出二进制信息。-e
/--encrypt
:加密。相应的还有解密、签名、验证等参数,将在后面介绍。<input-file>
:要加密的文件。可选,如果不指定将会从标准输入读取。
注意:只有 -r
指定的私钥才能解密信息,如果没有指定自己,则自己也无法解密。也可以仅指定自己,用于加密隐私文件。可以通过配置,加密时默认将自己包含在内,后面将会介绍。
示例:
1 | gpg -r someone -o myfile.gpg -e myfile |
解密
命令:
1 | gpg [-o <output-file>] -d [<input-file>] |
参数说明:
-o
/--output
:指定解密后的信息输出到哪个文件。可选,如果不指定将输出到标准输出。-d
/--decrypt
:解密。<input-file>
:待解密的文件。可选,如果不指定将尝试从标准输入读入。
解密时没有指定私钥,因为一般加密信息中会包含使用的公钥 ID,GPG 将会自动在本地寻找对应的私钥,如果找不到将会解密失败。
示例:
1 | gpg -o myfile -d myfile.gpg |
签名
命令:
1 | gpg [-u <key-id>] [-o <output-file>] [-a] (-s | -b | --clearsign) [<input-file>] |
参数说明:
-u
/--local-user
:指定用来签名的密钥 ID,签名后,只有对应的公钥可以验证成功。如果不指定将使用默认密钥,如果没有默认密钥将使用第一个可用密钥。默认密钥参考:配置文件。-s
/--sign
:签名。--clearsign
:签名并保持原始信息。-s
签名后,信息将会打包成 GPG 的格式。虽然没有加密,但仍需 GPG 命令才能解析查看。--clearsign
签名,会保持原始信息,额外附加一段签名信息,这样任何人都可以直接看到原始信息,需要验证的再使用 GPG 验证。-b
/--detach-sign
:分离签名。如果是文本信息不想修改原始信息,可以使用--clearsign
保持原始信息。而对于二进制文件,则可以使用-b
创建一个独立的签名文件。
这三种签名方式,实际动手操作一下就知道他们的差别了。
示例:
1 | echo 'some message' | gpg -u myself --clearsign |
验证
命令:
1 | gpg (--verify | -d) [<file>...] |
参数说明:
--verify
:仅验证签名是否正确,不输出原始信息。-d
/--detach-sign
:验证签名是否正确,并输出原始信息。<file>...
:对于-s
/--clearsign
的签名,输入为签名文件。对于-b
的签名,第一个参数为签名文件,第二个参数为被签名的数据,如果不指定第二个参数,将会自动尝试去掉后缀作为原始文件名。
验证签名同样不用指定公钥。GPG 将会在本地的公钥中寻找,如果无法找到,将会验证失败。
示例:
1 | echo 'some message' | gpg -u myself -s | gpg -d |
组合
为了更清楚的说明每个功能的用法,上面将每个参数分开介绍的,这些参数可以组合使用。例如实际使用时,可能同时需要签名和加密:
1 | echo 'some message' | gpg -u myself -r someone -ase |
基本概念
查看密钥列表时,会有类似如下输出,在介绍如何管理密钥前,先了解一下这些输出信息的含义。这个输出在每个版本有可能会不同。
1 | $ gpg -k --with-fingerprint --with-subkey-fingerprint --with-sig-list |
1 | $ gpg -K --with-fingerprint --with-subkey-fingerprint --with-sig-list |
密钥类型
每个密钥开头都会有这样几个字母:pub
/sub
/sec
/ssb
,表示这个密钥的类型。
在非对称加密算法中,密钥都是成对出现的。pub
和 sec
就分别表示这一对密钥的公钥和私钥,而 sub
和 ssb
则分别表示一对子密钥的公钥和私钥。
为什么会存在子密钥?理论上只需一对主密钥,就可以完成所有加密和签名的需要了。而 GPG 支持一对主密钥和若干对子密钥,主要是为了密钥本身的安全考虑。例如需要在多台设备上进行签名和加密操作,如果在每个机器上都使用主密钥,一旦丢失,则所有设备上的信息都将可以被解密。而且除了撤销整个主密钥之外,没有任何其他办法来止损。而如果将主密钥保存在安全的地方,仅在必要的时候使用,其余机器上都使用子密钥。那么如果一台机器上的子密钥丢失,只有这台机器上的信息可能被泄露。而且无需撤销整个主密钥,只需要使用主密钥撤销这个子密钥,然后重新创建一个子密钥即可。
另外每个子密钥也可以选择不同的加密算法,可以指定不同子密钥来选择不同的加密算法来进行加解密。
密钥用途
每个密钥旁边都会有方括号包围的一个或多个字母,可能取值有 E
/S
/C
/A
,表示这个密钥的用途。每个值的含义如下:
- Encryption:加密。
- Signing:签名。
- Certification:认证其他子密钥或 uid。
- Authentication:身份认证,例如用于 SSH 登录。
其中前三个较为常用。如果使用 gpg --gen-key
生成的密钥,默认就会生成一对主密钥 [SC]
和一对子密钥 [E]
。这样将会使用主密钥进行签名,使用子密钥进行解密。
我们也可以自行添加其他子密钥或者修改已有子密钥用途,具体操作方式将在编辑密钥中介绍。
加密算法
pub
等标识后面紧跟着的是密钥的加密算法,例如 rsa3072
表示密钥长度为 3072 位的 RSA 算法。当前版本 (2.2.19) 使用 gpg --gen-key
生成的默认密钥使用的就是 3072 位的 RSA 算法。支持的其他算法可以输入 gpg --version
查看。注意这里指的是非对称加密的算法,对称加密使用的算法可以在每次加密时通过参数指定不同算法。
如果想要生成其他算法的主密钥,可以使用 gpg --full-gen-key
命令。这里也体现了使用子密钥的灵活性,例如我们可以使用 4096 位的主密钥,更加安全。然后使用 3072 位的子密钥用于加密和签名,更加高效。如果未来 3072 位密钥不够安全时,只需要重新生成 4096 位的子密钥,而不用重新生成主密钥。
指纹
每个密钥或子密钥下面都有一行由 10 组 4 个字符组成的信息。这是使用 SHA-1 算法对整个密钥计算出来的摘要,共 160 位,20 字节,一般表示为 40 个十六进制数,就是现在看到的。这个指纹可以用于唯一的标识一对密钥。
User ID
每个主密钥可以有一个或多个 uid,用于标识用户身份。格式为 Real Name (Comment) <Email>
。
有效性
每个 uid 旁边的方括号内,标识了这个 uid 的有效性。
如果新导入了一个公钥,默认将会显示 [unknown]
。这是因为任何人都可以声称自己是任何人,所以在导入公钥时,需要想办法验证他的有效性。
第一种验证方式是直接当面交换公钥,绝对安全。但很多时候这不可能。
另一种方式是验证指纹,可以使用 gpg --fingerprint
命令将指纹打印出来,然后通过打电话或者其他可靠的方式确认指纹是否正确。因为指纹是通过安全散列算法对整个密钥信息计算的摘要,很难构造出(碰撞)两个摘要相同的密钥,所以一旦确认指纹没错,就可以认为整个密钥是有效的。
确认之后,需要将密钥标识为有效的,标识的方式就是使用自己的密钥对公钥进行签名,这样 GPG 就认为这是个有效的密钥了。
注意签名和有效性都是针对 uid 的。例如一个人声称了两个身份,第一个 uid 说他叫特朗普,第二个 uid 说他是前美国总统。你想尽一切办法最终只验证了他确实叫特朗普,但不知道是不是那个美国总统。那就可以仅对第一个 uid 签名。
如何对 uid 签名将会在编辑密钥具体介绍。
信任网络
事实上,几乎很难直接验证每个密钥的有效性,这时可以利用 GPG 的信任网络来间接验证密钥的有效性。
简单来说就是如果我确认了某个密钥的主人确实是美国总统,而我信任他不会乱认人(但愿吧)。那么如果这个密钥对某个叫做国务卿的密钥进行了签名,我就也认为这个国务卿确实是有效的,而不用亲自去认证。
信任分为几个不同的等级:
ultimate
:终极信任。一般只应该对自己的密钥进行终极信任,可以理解为所有信任链的根节点。full
:完全信任。对于这个人签名的其他密钥,我也认可。marginal
:信了,但没完全信。这个人签名过的密钥,我不一定认可,但如果三个这样的人都签了,那我就信了。never
:不信任。这个人对其他密钥的签名,我一律视而不见。
需要注意信任和有效性不一定有关系。信任表示的是对这个密钥签名其他密钥的信任程度,而有效性是对这个密钥本身的验证。例如我知道某个密钥确实是特朗普的,他是有效的。但我觉得这个人满嘴跑火车,他签名的其他密钥我一概不认。
有了信任网络,一个密钥有效的条件就变成了:被一个 full
签名或三个 marginal
签名,同时这个签名传递的路径距离我(ultimate
)的长度不超过 5。这些值都可以修改,具体可以参考 man gpg
。更具体的说明可以参考 The GNU Privacy Handbook。
如何修改信任值将在编辑密钥具体介绍。
自签名和交叉签名
除了通过验证其他人对 uid 的签名外,所有 uid 和子密钥都还需要有自签名。否则攻击者就可以绕过公钥的主人插入一个自己的子密钥,来冒充别人。而如果要求所有的 uid 和子密钥都经过主密钥自签名,则冒名顶替的 uid 和子密钥因为没有主密钥的私钥,将无法蒙混过关。
对于子密钥,除了自签名外,还需要和主密钥进行交叉签名。否则攻击者将其他人的子密钥插入到自己密钥中,就可以声称某个签名是自己发出的。而如果要求子密钥和主密钥进行交叉签名,则攻击者因为无法获取子密钥的私钥对自己的主密钥进行签名,将无法蒙混过关。
撤销
GPG 在更新密钥的时候,对 uid 的签名、uid、子密钥等都会采用合并的方式处理。因此如果一个签名、uid 或者子密钥不再使用了,将其删除是没有意义的,因为导出给其他人或者密钥服务器,合并后原来的签名、uid 和子密钥还是存在的。所以如果不再使用,我们需要撤销它。
其实无论撤销什么,本质都是在撤销签名。撤销对 uid 的签名,将会生成一条撤销签名的信息,标记原来的签名无效。而如果撤销的是 uid 或子密钥,将会撤销对 uid 或子密钥的自签名,使原来的 uid 或子密钥失效。
撤销的密钥只是不能用于新的加密和签名。但依然可以解密和验证已有的信息,只是会提示已经被撤销。
当然也可以撤销整个主密钥,这些都将在编辑密钥中介绍。
有效期
如果有必要,可以分别对主密钥和每对子密钥设置有效期。有效期可以随时修改,过期前重新设置有效期即可,无需重新生成新密钥。
过期的密钥只是不能用于新的加密和签名。但依然可以解密和验证已有的信息,只是会提示过期。
设置有效期的安全性,并不在于定期更换密钥,实际上 GPG 官方文档也没有推荐更换密钥,而是建议在到期前续期。也不在于担心密钥泄露,因为如果密钥泄露,获取私钥的人也可以随时修改有效期。有效期的意义是在于让持有公钥的人,记得及时更新公钥信息。例如如果密钥已经泄露了,但没有设置有效期。其他人如果没有想到要主动更新公钥,可能永远也发现不了密钥已经泄露了。那么获取了私钥的人冒充你进行签名,依然会被其他人认可。
如何设置有效期将在编辑密钥介绍。
选择密钥
前面介绍的命令中,已经出现了很多次 <key-id>
的占位符。其实这里用 <key-id>
并不恰当,只是没有更好的名称来描述。实际使用中,可以有多种方式来选择一个密钥。常用的有:
- 指纹:就是那 40 个十六进制数。这是 GPG 推荐的,最不容易重复的方式,缺点是太长。
- Key ID:有两种格式
- 短 ID:指纹的后 8 位,这种已经不推荐了,因为有被碰撞的风险
- 长 ID:指纹的后 16 位
- 精确匹配的邮箱:
<xxx@xxx>
- 部分匹配的邮箱:
@xxx
- 部分匹配 uid:
someone
,不区分大小写
所有需要选择某个 key 的地方,都可以使用上面这些形式。注意除了指纹和 Key ID 外,其他都可能匹配到多个,将会选择第一个使用。
除了这些基本的匹配外,对于 Key ID 和指纹,还有个特殊的用法:!
。
前面介绍过 GPG 支持一对主密钥和若干对子密钥。而如果主密钥支持 [SC]
,某个子密钥也支持 [S]
。那么签名时将会使用哪个呢?答案是使用子密钥。在选择密钥时,GPG 会自动选择一个支持对应能力的子密钥,如果存在多个,则选择较新的一个。那么如果就是要使用某个特定的主密钥或子密钥呢?这时就可以在 key ID 或指纹后面加上 !
,强制使用这个密钥,而不要自动推算。
这种用法还可以用在导出密钥时,仅导出某个子密钥或主密钥。
管理密钥
导出密钥
命令:
1 | gpg [-a] [-o <output-file>] [--export-options <option>[,<option>...]] (--export | --export-secret-keys | --export-secret-subkeys) [<key-id>] |
参数说明:
--export
:导出公钥。--export-secret-keys
:导出私钥。--export-secret-subkeys
:导出子密钥。--export-options
:导出选项,逗号分割,常用的有:backup
:导出用于备份。会将信任值、本地签名等一起导出。export-clean
/export-minimize
:参考:清理签名
<key-id>
:如果不指定将会导出所有公钥或私钥,如果指定仅会导出指定的密钥,使用!
可以仅导出主密钥或子密钥。
导入密钥
命令:
1 | gpg --import [--import-options <option>[,<option>...]] [<key-file>] |
参数说明:
--import
:导入密钥。--import-options
:导出选项,逗号分割,常用的有:restore
:恢复,与导出选项的backup
对应。import-show
:回显导入的密钥信息。
示例:
1 | gpg --import someone.gpg |
删除密钥
命令:
1 | gpg (--delete-keys | --delete-secret-keys | --delete-secret-and-public-keys) <key-id>... |
参数说明:
--delete-keys
:删除公钥。--delete-secret-keys
:删除私钥。--delete-secret-and-public-keys
:同时删除私钥和公钥。
示例:
1 | gpg --delete-keys someone |
密钥服务器
通过手动的导入、导出密钥,当密钥比较多时会比较麻烦。更关键的是不方便密钥的更新。例如已经通过手动导出的方式将公钥发布给了很多人,一旦公钥发生变动(例如获得了一个新签名、撤销了一个子密钥等),就需要重新导出,然后找到每个持有公钥的人,要求他更新一下。而 GPG 可以通过密钥服务器帮助我们完成这些,使用起来和导入导出差不多,只不过导出到密钥服务器、从密钥服务器导入而已。
命令:
1 | gpg [--keyserver <key-server>] [--keyserver-options <option>[,<option>...]] (--send-keys | --recv-keys | --refresh-keys | --search-keys) [<key-id>] |
参数说明:
--send-keys
:将密钥发送到密钥服务器。--recv-keys
:从密钥服务器接收密钥。--refresh-keys
:从密钥服务器更新本地密钥。--search-keys
:从密钥服务器搜索密钥。--keyserver
:指定一个密钥服务器,大部分密钥服务器之间都会同步密钥。--keyserver-options
:导入或导出密钥时的一些选项,支持所有--import-options
和--export-options
,另外还支持像http-proxy
来指定代理等。
示例:
1 | gpg --search-keys EB4C1BFD4F042F6DDDCCEC917721F63BD38B4796 |
编辑密钥
使用 gpg --edit-key <key-id>
命令,可以交互式的编辑一个密钥。输入 help
可以查看帮助,输入 save
可以保存并退出编辑,输入 quit
可以不保存退出编辑。
下面是一些常用操作的介绍。
对 uid 签名
使用 sign
或 lsign
可以对 uid 进行签名。两者的区别是,sign
的签名可以被导出和上传到密钥服务器,而 lsign
的签名仅可以在本地查看和生效。一般我们验证了一个人的身份,如果没有得到公钥主人的允许,应该优先使用 lsign
进行签名。
如果需要撤销一个签名,可以使用 revsig
命令。如果签名还没有发布(上传到密钥服务器或者导出给其他人),也可以使用 delsig
直接删除。
这里的签名将会使用默认的私钥,如果没有指定将会使用第一个可用的私钥。或者也可以在 --edit-key
时通过 -u
参数指定一个私钥。
如果不指定某个 uid,将会对所有 uid 签名。也可以先通过 uid [n]
命令选择某个或某些 uid,再进行签名。uid 0
将取消选择所有 uid
。
信任密钥
使用 trust
命令可以设置对密钥的信任值。关于信任值的解释和每个信任值的含义参考:信任网络。
信任值是一个很主观的事情,因此信任值仅会保存到本地的数据库中,不会被导出或上传到数据库中。除非使用 --export-options backup
选项导出。
uid
使用 adduid
命令可以创建一个新的 uid。并可以使用 uid [n]
命令选择一个 uid 后,使用 primary
命令指定一个主 uid。
如果某个 uid 不再使用,可以先使用 uid [n]
选中,再使用 revuid
命令撤销。如果 uid 还没有发布,也可以使用 deluid
直接删除。
子密钥
使用 addkey
命令可以创建一对子密钥。创建过程中可以交互式的选择使用的算法、用途等。
如果某个子密钥不再使用,可以先使用 key [n]
选中,再使用 revkey
命令撤销。如果子密钥还没有发布也没有使用过,可以使用 delkey
直接删除。但要注意,如果删除,使用此子密钥加密过的信息将永远无法解密!
撤销密钥
密钥服务器为了防止被篡改,被设计为只增不删的形式。也就是说如果密钥一旦上传到服务器,将永远不能删除。那么如果主密钥丢失或者泄露了,我们只能撤销它。
不选择任何子密钥,执行 revkey
命令,将撤销整个密钥。撤销之后,重新将密钥发送到密钥服务器,密钥服务器上的密钥也会被撤销。其他人从密钥服务器更新之后,将会知道密钥已经被撤销,以后就不会再使用此密钥进行加密,也将不会信任此密钥的签名。
上面的操作必须在我们拥有私钥的情况下进行,而如果私钥丢失了,我们就永远无法更新密钥的状态了。所以为了避免出现这种情况,一般建议生成一个撤销证书,和私钥分开保存。撤销证书本质上就是一个撤销密钥的签名。如果私钥丢失,需要将撤销证书导入,并发送到密钥服务器,密钥服务器上的公钥也将被撤销。
- 生成撤销证书:
gpg [-a] [-o <output-file>] --gen-revoke <key-id>
- 通过导入撤销证书撤销密钥:
gpg --import [<input-file>]
- 发布撤销密钥:
- 如果之前使用了密钥服务器,则发布到密钥服务器即可:
gpg --send-keys <key-id>
- 如果是手动导出的方式,则再次导出并发送给其他人导入即可:
gpg --export-keys <key-id>
- 如果之前使用了密钥服务器,则发布到密钥服务器即可:
注意: 密钥一旦撤销将无法恢复。
其实在拥有私钥的情况下,无需撤销证书即可撤销整个密钥。撤销证书单独保存的意义在于,他可以保存在比私钥稍低一个安全等级的地方。因为一旦私钥泄露,以往的信息都将泄露。而如果安全证书泄露,只会使密钥失效,不会泄露任何信息。最多是需要重新生成一对密钥发布给其他人,比较麻烦而已。例如撤销证书可以放在信任的人那里备份,而私钥只能放在不会被任何人获取的地方备份。
过期时间
使用 expire
命令可以设置密钥的过期时间。默认会设置主密钥的,使用 key [n]
选中子密钥后,将设置选中密钥的。
密码
使用 passwd
命令可以修改密钥的密码。
清理签名
如果 uid 上收集的很多签名无效了,例如撤销了的签名,不会删除原有的签名,而是在生成一条撤销的信息。可以使用 clean
命令,清除所有无效的签名。
如果更进一步,不需要所有其他人的签名,则可以使用 minimize
清除自签名外的所有签名。
配置文件
前面的介绍中可以看到,很多时候我们都需要通过 -u
参数指定使用哪个密钥进行签名。还有加密时如果不指定自己,以后将会无法解密自己发出的信息。我们希望将这些参数默认配置下来,不用每次都在命令行中输入。
GPG 命令中的所有长参数 (--
开头的参数) 都可以配置在 ~/.gnupg/gpg.conf
中,这样每次执行 GPG 命令时,都会默认带上这些参数。
例如指定默认的用于签名的密钥,指定每次加密都默认也加密给自己等等。
示例:
1 | default-key B69BC89653B7D844C67F137C3684CA4A7AF90D04 |
使用场景
GPG 主要用于信息传输过程中的加密。对于大多数人来说,既没有什么恐怖袭击计划需要讨论,也没有几百亿的生意要谈,所以可能用到的机会不多。不过 GPG 也可以用于给自己的文件进行加密,例如将敏感文件加密后再通过网盘备份等。
GPG 的签名功能在很多地方都会用到。例如大部分 Linux 发行版的包管理系统,都会使用 GPG 签名来保证包在分发的过程没有被篡改。所以各种 Linux 发行版一般都在世界各地存在大量的镜像源,我们可以放心的享受这些镜像源的便利,不是靠对镜像源的信任来保证的,而是靠 GPG 签名来保证的。此外还可以使用 GPG 对 Git 的提交和标签进行签名等。