GnuPGP 是一个基于公钥加密体系的加密软件,功能强大,有很快的速度,而且源码是免费的。
这里简单介绍下 PGP 相关内容。
简介
Pretty Good Privacy, PGP 加密由一系列散列、数据压缩、对称密钥加密,以及公钥加密的算法组合而成,遵守 OpenPGP 数据加密标准 RFC 4880 ,每个步骤支持几种算法,可以选择一个使用。
注意 PGP 是一个商用软件,对应的开源软件为 GnuPGP ,两者都遵守 OpenPGP 标准。
通常,使用如上的加密和解密过程大致为。
- 用户产生需要加密的信息。
- OpenPGP 发送方产生一串随机数,作为对称加密密钥,这一随机数只对该信息或该会话有效。
- 使用接受者的公钥加密上述的随机数 (密钥),放置到需要发送消息的开头。
- 通过上述产生的随机数加密需要加密的信息,通常会先对信息进行压缩。
- 接收方则会先通过私钥解密随机数,然后解密信息。
当然,也可以先产生一个数字签名,并将数字签名添加到信息中,然后同样会通过随机串加密。在 OpenPGP 中也可以对信息进行压缩,通常压缩是在获得数字签名后且在压缩之前。
The Public-Key Cryptography Standards(PKCS) 是由美国 RSA 数据安全公司及其合作伙伴制定的一组公钥密码学标准,包括证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议。
信任模型
PGP 的好处在于它的信任模型 Web Trust Model,一种非常贴近生活中人与人的信任模型。在信息世界里面,人与人之间的信任纽带有两种方式被建立。
集中式
如 CA 结构,大家都信任 CA ,于是 Peter 是不是 Peter ,是根据 CA 是否认识这个 Peter ,我们现在通常所说的的数字证书认证,就是这种集中认证的模式。
分布式
假设没有CA,只信任自己和朋友,每个人都是在一个朋友圈子中,朋友圈子以外的人,则需要一个中间人作为介绍人,这就是现实的生活。
我有一个朋友叫 Andy,且我有另一个朋友叫 Bob,如果 Bob 想认识 Andy,于是他想通过我来认识 Andy,方法很简单,鉴于我认识他们两者,通过我为 Bob 的公钥签名,于是 Andy 可以直接向 Bob 证明他是我的朋友。
PGP 采用的后者是这种方式,每个人都以自己为中心,然后是朋友圈,然后是朋友的朋友。
GnuPGP
公钥算法的关键点在于如何传播公钥,如果有恶意用户传递了一把虚假公钥,此时,如果别人不知就里,用这把公钥加密信息,持有该虚假钥匙的侵入者就可以解密而读到信息。而如果侵入者再将解密的讯息加以修改,并以真正的公开钥匙加密,然后传送出去,这种进攻无法被发现。
PGP 的解决方法是对公钥进行签名,也即生成指纹 (fingerprint),只有公钥会有指纹。每个公钥均绑定唯一的用户身份 (包括用户名、注释、邮箱),当然一个人的公钥可以由别人来签名,签名 (指纹) 是用来测试该公钥是否是由其声明的用户发出的 。
至于有多信任这些签名,完全取决于 GPG 用户,当你信任给这把钥匙签名的人时,你认为这把钥匙是可信的,并确信这把钥匙确实属于拥有相应用户身份的人。只有当你信任签名者的公开钥匙时,你才能信任这个签名。要想绝对确信一把钥匙是正确和真实的,你就得在给予绝对信任之前,通过可靠渠道比较钥匙的 “指纹”。
使用
在 ~/.gnupg
目录下,保存了一些必须的文件,其中密钥以加密形式存储在磁盘上,包括了两个文件,一个存储公钥 (pubring.gpg
)、一个存储私钥 (secring.gpg
)。
GPG 会通过 username、comment、email 生成一个 UID(hash value),在使用时可以用其中的一种方式。
1. 生成密钥对 (公钥+私钥)
生成时需要选择 A) 加密算法、密钥长度 (长度越长解密时间越长,不过也会更安全)、密钥的有效时间;B) 区分该密钥对的用户名、e-mail、注释 (这些信息是可以修改的);C) 一个保护该私钥的验证密码。
$ gpg --gen-key
Please select what kind of key you want: # 选择加密以及指纹算法
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: foobar
Email address: foobar@ooops.com
Comment: just for test
You selected this USER-ID:
"foobar (just for test) <foobar@ooops.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key. # 输入密码
gpg: key FCC7A473 marked as ultimately trusted # 用户的hash值,可以用来替代userid
public and secret key created and signed.
gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
pub 2048R/FCC7A473 2016-07-06
Key fingerprint = ACB2 B707 B2D8 A548 55A8 5E24 A5CC BA5C FCC7 A473 # 可以用来电话验证^_^
uid foobar (just for test) <foobar@ooops.com> # 三元素组成userid
sub 2048R/3B44DA00 2016-07-06
生成的公钥 (pubring.gpg
)、私钥 (secring.gpg
) 保存在 ~/.gnupg/
目录下。
2. 撤销公钥
当密钥对生成之后,应该立即做一个公钥回收证书,当忘记了私钥的口令或者私钥丢失或被盗窃,可以发布这个证书来声明以前的公钥不再有效。
$ gpg --output revoke.asc --gen-revoke "user-name/e-mail/user-id" # 生成回收证书,以备不时之需
$ gpg --import revoke.asc # 导入回收证书
$ gpg --keyserver keys.gnugp.net --send "8D253E8A" # 发送回收证书到服务器,声明原GPG Key作废
当然如果要这么做,一定要先有私钥,否则任何人都能取消你的公钥。
3. 传播公钥
导出公钥之后,就可以通过一些可靠的方式传播公钥。可以将公钥通过 e-mail 发送给好友,或者上传到指定的服务器(如果不指定使用默认的 keys.gnupg.net ,其它的还有 keyserver.ubuntu.com 。
$ gpg --keyserver keys.gnugp.net --send-keys "8D253E8A" # 上传到服务器
$ gpg --fingerprint # 打印公钥指纹
$ gpg --keyserver keys.gnugp.net --search-keys "user-name/e-mail/user-all" # 在服务器上查找
由于公钥服务器没有检查机制,任何人都可以用你的名义上传公钥,所以没有办法保证服务器上的公钥的可靠性。通常,可以在网站上公布一个公钥指纹,让其他人核对下载到的公钥是否为真。
4. 查看密钥
$ gpg --list-keys # 输出现有公钥,包括UID、公私钥的HASH值,等同于gpg -k
$ gpg --list-sigs # 同时显示签名,实际也就是上述的HASH值
$ gpg --fingerprint # 输出公钥的指纹
$ gpg --list-secret-keys # 列出私钥,PS. 列出私钥的指纹和签名根本就没用,等同于gpg -K
通过 gpg -k
列出所有公钥,其中第一行表示保存的文件名 (pubring.gpg
);第二行为公钥的特征 (加密方式:如 2048R
表示 2048-bit RSA
,user-id
的 hash
值,生成时间);第三行表示用户 ID ;第四行表示私钥特征。
5. 导出公钥
将指定用户的公钥以 ASCII 的格式导出。
$ gpg --armor --export "user-name/e-mail/user-id" # 导出ASCII公钥到终端
$ gpg --armor --output public-key.gpg --export "user-name/e-mail/user-id" # 导出ASCII公钥到文件,或者-o
$ gpg --armor --export-secret-keys # 导出私钥
$ gpg --refresh-keys # 从服务器定期更新公钥
其中 --armor
表示将内容转换成可见的 ASCII 码输出,否则是二进制不方便阅读。
6. 导入公钥
除了生成自己的密钥,还需要别人的公钥,用于生成加密信息或者验证数字签名等。为此,就需要将他人的公钥或者你的其他密钥输入系统,这时可以使用 import 参数。
$ gpg --import public-key.gpg # 导入ASCII公钥到文件
$ gpg --keyserver hkp://subkeys.pgp.net --search-keys "user-name/e-mail/user-all" # 从服务器查看然后选择
$ gpg --keyserver subkeys.gpg.net --recv-keys 8D253E8A # 直接导入
由于任何人都可以导入公钥,我们无法保证服务器上的公钥是否可靠,下载后还需要用其他机制验证,如数字签字。
7. 删除密钥
我们可以通过如下命令删除密钥。
$ gpg --delete-keys "user-name/e-mail/user-id" # 从公钥钥匙环里删除密钥
$ gpg --delete-secret-keys "user-name/e-mail/user-id" # 从私钥钥匙环里删除密钥
$ gpg --delete-secret-and-public-key "user-name/e-mail/user-id" # 同时删除公钥私钥
8. 修改密钥
用此命令你可以修改钥匙的失效日期,加进一个指纹,对钥匙签名等等。 尽管显得太清楚而不用提,这里还是要说,要做以上事情你得用你的通行句。 敲入通行句后,你会见到命令行。
$ gpg --edit-key "user-name/e-mail/user-id"
9. 加密/解密
在安装和按照需要设置好所有事以后,我们就可以开始加密和解密了。
$ gpg --recipient "8D253E8A" --output demo.en.txt --encrypt demo.txt # 用指定用户公钥加密
$ gpg --output demo.de.txt --decrypt demo.en.txt # 解密,可以不指定用户名
$ gpg demo.en.txt # 解密,最简单方式
--recipient
参数指定接收者的公钥,--output
指定加密后的文件名,--encrypt
指定源文件。
10. 签名
有时只需要对文件签名,表示这个文件确实是我本人发出的。
$ gpg --sign demo.txt # 生成二进制签名demo.txt.gpg,同时包含文本内容
$ gpg --clearsign demo.txt # 生成ASCII的签名文件demo.txt.asc,同时包含文本内容
$ gpg --detach-sign demo.txt # 只生成二进制签名demo.txt.sig
$ gpg --armor --detach-sign demo.txt # 只生成ASCII签名demo.txt.asc
$ gpg --verify demo.txt.asc demo.txt # 验证解密的文件与签名是否相符
$ gpg --armor --detach-sign -u UID demo.txt # 可以指定用户
11. 签名+加密
此时签名和加密同时保存在同一个文件中。
$ gpg --local-user "user-name/e-mail/user-id" --recipient "8D253E8A" --armor --sign --encrypt demo.txt
$ gpg demo.txt.asc # 解密
公钥的管理
正如前言所提到的,这个系统有一个最大的薄弱点,那就是公钥的真实性问题。可以通过直接向公钥的提供者当面或者电话验证指纹,从而确定公钥的真实性问题。
当确定了公钥的真实性之后可以对公钥签名,表示你绝对确信这把钥匙是真实的,有了这个保证,用户就可以开始放心用这把钥匙加密了。
要对一把钥匙签名,用 gpg --edit-key UID
,然后用 sign 命令。
GPG 会根据现有的签名和对 “主人的信任度” 来决定钥匙的真实性,包括了四个值。
1 = 我不知道
2 = 我不信任(这把钥匙)
3 = 我勉强信任
4 = 我完全信任
对于用户不信任的签名,就会将废弃这个签名不用。注意这些信任信息不是存在储存钥匙的文件里,而是存在另一个文件里。
参考
关于如何使用 subkey Creating a new GPG key with subkeys 一个不错的介绍,可以参考 本地文档;另一篇参考的是最佳实践,可以参考 OpenPGP Best Practices ,或者 本地文档 。
一个官方的中文文档 GnuPG 袖珍 HOWTO ,或者英文版 The GNU Privacy Handbook 。