加密软件 GPG 入门教程

编程
22 min read

GPG 全称 GnuPG,是一个命令行工具,于 1999 年由德国开发者 Werner Koch 向公众发布。它是 OpenPGP 标准的一个开源实现。

GPG 工具并不容易上手,主密钥与子秘钥的关系、信任网的工作原理都很抽象,一定要上手实践。

安装 GPG

macOS 的用户最好通过 Homebrew 安装 GPG:

brew install gpg

大多数 Linux 的发行版内置 GPG,开箱即用。Debian 和 Ubuntu 的系统安装 GPG:

sudo apt install gpg

安装完毕,gpg --help 查看命令, gpg --version 查看版本,我的版本信息如下,需要注意的是,不同版本加密算法的选项稍有不同,不过总体操作流程是一致的。

版本信息
gpg --version
gpg (GnuPG) 2.2.27
libgcrypt 1.8.8
Copyright (C) 2021 Free Software Foundation, Inc.
License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Home: /home/alice/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

生成秘钥

生成秘钥的命令有 :

  • --full-generate-key (简写 --full-gen-key
  • --generate-key(简写 --gen-key
  • --quick-generate-key(不常用)

其中 --gen-key--quick-generate-key 在加密算法、秘钥长度、有效期等选项使用默认值,而 --full-gen-key 则是由用户手动配置,考虑到以学习为目的,我们走最长路径。

gpg --full-gen-key
  1. 选择加密算法及用途。选择默认值 (1) RSA and RSA (default),RSA 算法最常用,兼容性好。
  2. 选择秘钥长度,1024~4096 位,默认 3072。位数越长,抵抗暴力攻击的安全性越高,相对加解密的开销越大,推荐设置最大值。
  3. 配置合适的有效期限,有效期限后面可重新设置。3y3m3w3 分别对应三年、三个月、三个周和三天,0 为永不过期。
  4. 输入“姓名”、“邮箱”和“注释”,每一项都为非必填项,但“姓名”和“邮箱”至少保证其中一项有值。
  5. 确认信息,输入密码口令,再次确认密码,生成秘钥需要一会儿时间,期间可以胡乱移动鼠标或者敲打键盘,这会让随机数生成器获得足够的熵数。
秘钥生成全过程
gpg --full-gen-key
gpg (GnuPG) 2.2.27; 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: directory '/home/alice/.gnupg' created
gpg: keybox '/home/alice/.gnupg/pubring.kbx' created
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)
(14) Existing key from card
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 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) 3y
Key expires at Sun 13 Jul 2025 06:45:55 BST
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: alice
Email address: alice@example.com
Comment: Alice key for learning
You selected this USER-ID:
"alice (Alice key for learning) <alice@example.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /home/alice/.gnupg/trustdb.gpg: trustdb created
gpg: key D2E9A1DE3E6050E7 marked as ultimately trusted
gpg: directory '/home/alice/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/alice/.gnupg/openpgp-revocs.d/45571605D9D46583891EE269D2E9A1DE3E6050E7.rev'
public and secret key created and signed.
pub rsa4096 2022-07-14 [SC] [expires: 2025-07-13]
45571605D9D46583891EE269D2E9A1DE3E6050E7
uid alice (Alice key for learning) <alice@example.com>
sub rsa4096 2022-07-14 [E] [expires: 2025-07-13]

列出钥匙串上所有公钥 gpg --list-keys,短命令 gpg -k 等效。

gpg --list-keys
/home/alice/.gnupg/pubring.kbx
------------------------------
pub rsa4096 2022-07-14 [SC] [expires: 2025-07-13]
45571605D9D46583891EE269D2E9A1DE3E6050E7
uid [ultimate] alice (Alice key for learning) <alice@example.com>
sub rsa4096 2022-07-14 [E] [expires: 2025-07-13]

列出所有私钥 --list-secret-keys,短命令 gpg -K

gpg --list-secret-keys
/home/alice/.gnupg/pubring.kbx
------------------------------
sec rsa4096 2022-07-14 [SC] [expires: 2025-07-13]
45571605D9D46583891EE269D2E9A1DE3E6050E7
uid [ultimate] alice (Alice key for learning) <alice@example.com>
ssb rsa4096 2022-07-14 [E] [expires: 2025-07-13]

按照上面的步骤,我们得到一主密钥对(Primary keypair)和一子秘钥对(Subordinate keypair),而秘钥对是由公钥和私钥组成的。

  1. pub -- public primary key
  2. sec -- secret primary key
  3. sub -- public sub-key
  4. ssb -- secret sub-key

rsa4096 代表秘钥算法和秘钥长度,接着是创建日期、秘钥功能、和有效期限。其中秘钥功能的含义如下:

简称全称功能解释
[C]Certify认证,认证其他秘钥
[S]Sign签名:给邮件、文件签名
[E]Encrypt加密,加密信息、文件
[A]Authenticate身份认证,登录鉴权

45571605D9D46583891EE269D2E9A1DE3E6050E7 为公钥的指纹(fingerprint,又叫哈希值),后 16 位将会作为主密钥的 ID。以 uid 开头的一行是用户信息,包含有效性、用户名、备注、邮箱。最后一行是具有加密功能的子秘钥。

sitelinks-searchbox

主密钥、子秘钥的操作

编辑秘钥命令:--edit-key。所有和秘钥相关的设置,都通过该命令完成,比如:

  • 添加、吊销、删除子秘钥;
  • 修改主密钥、子秘钥过期时间;
  • 显示秘钥指纹(fingerprint),认证导入的公钥;
  • 设置算法偏好、变更密码;
  • 新增用户标识(uid 信息);
  • 启用/禁用秘钥、变更信任度等等。

输入 gpg --edit-key alice@example.com,进入秘钥交互状态。交互状态下输入 help 查看所有命令。

gpg --edit-key alice@example.com
gpg (GnuPG) 2.2.27; 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.
Secret key is available.
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate # 信任度 有效性
ssb rsa4096/103FCD7E12E2082B # 算法 秘钥长度/子秘钥 ID
created: 2022-07-14 expires: 2025-07-13 usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com> # 用户 uid 信息
gpg> help

添加子秘钥

现在,我们得到的秘钥为:具有 [SC] 功能的主密钥和一具有 [E] 功能的子秘钥。但现实的应用场景是复杂的,假设我们想使用其他子秘钥加密某些特定文件,那就得添加新的子秘钥了。

添加子秘钥使用 addkey 命令,与创建初始秘钥的过程相似,选择秘钥用途、秘钥长度、有效期限、最后输入密码即可,最后记得 save 保存。

添加子秘钥全过程
gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(14) Existing key from card
Your selection? 6
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 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) 0
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
ssb rsa4096/C9A9A1E7A477FEE6
created: 2022-07-14 expires: never usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> save

修改秘钥有效期限

交互模式下输入 expire,修改主密钥的有效期。如修改某个子秘钥的有效期,先 key [n] 命令选中对应秘钥(选中和取消选中秘钥的命令都是 key [n]),再用 expire 命令,输入有效期限即可。

修改秘钥有效期
gpg --edit-key alice@example.com
gpg (GnuPG) 2.2.27; 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.
Secret key is available.
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
ssb rsa4096/C9A9A1E7A477FEE6
created: 2022-07-14 expires: never usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> key 2
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
ssb* rsa4096/C9A9A1E7A477FEE6
created: 2022-07-14 expires: never usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> expire
Changing expiration time for a subkey.
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) 3y
Key expires at Sun 13 Jul 2025 06:56:32 BST
Is this correct? (y/N) y
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
ssb* rsa4096/C9A9A1E7A477FEE6
created: 2022-07-14 expires: 2025-07-13 usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> save

吊销子秘钥

使用秘钥的过程中,可能会遇到秘钥废弃、过期或者泄露的情形,这时需要立即吊销秘钥。秘钥一旦吊销,信息发送者便无法用该秘钥的公钥来加密信息,而信息的接受者依然可以使用该秘钥解密在此之前收到的信息。

进入交互模式,吊销整个秘钥,直接使用 revkey 命令。吊销子秘钥,先 key [n] 选中对应子秘钥,再运行 revkey

吊销子秘钥
gpg --edit-key alice@example.com
gpg (GnuPG) 2.2.27; 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.
Secret key is available.
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2025-07-13
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
ssb rsa4096/C9A9A1E7A477FEE6
created: 2022-07-14 expires: 2025-07-13 usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> key 2
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
ssb* rsa4096/C9A9A1E7A477FEE6
created: 2022-07-14 expires: 2025-07-13 usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> revkey
Do you really want to revoke this subkey? (y/N) y
Please select the reason for the revocation:
0 = No reason specified
1 = Key has been compromised
2 = Key is superseded
3 = Key is no longer used
Q = Cancel
Your decision? 0
Enter an optional description; end it with an empty line:
>
Reason for revocation: No reason specified
(No description given)
Is this okay? (y/N) y
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
The following key was revoked on 2022-07-14 by RSA key D2E9A1DE3E6050E7 alice (Alice key for learning) <alice@example.com>
ssb rsa4096/C9A9A1E7A477FEE6
created: 2022-07-14 revoked: 2022-07-14 usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> save

删除子秘钥

删除秘钥属于危险操作,删除意味着之前使用该秘钥加密的所有信息将无法解密,删除前请确认秘钥已彻底废弃。

删除子秘钥
gpg --edit-key alice@example.com
gpg (GnuPG) 2.2.27; 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.
Secret key is available.
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
The following key was revoked on 2022-07-14 by RSA key D2E9A1DE3E6050E7 alice (Alice key for learning) <alice@example.com>
ssb rsa4096/C9A9A1E7A477FEE6
created: 2022-07-14 revoked: 2022-07-14 usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> key 2
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
The following key was revoked on 2022-07-14 by RSA key D2E9A1DE3E6050E7 alice (Alice key for learning) <alice@example.com>
ssb* rsa4096/C9A9A1E7A477FEE6
created: 2022-07-14 revoked: 2022-07-14 usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> delkey
Do you really want to delete this key? (y/N) y
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> save

如何进行加密通信

假设 Alice 约 Bobby 吃火锅,为了防止信息被窃听,二人使用加密通信,那么具体需要哪些步骤呢?

sequenceDiagram actor Alice actor Bobby Note over Alice,Bobby: 加密通信 Alice->>Bobby: Bobby,下午三点一起吃火锅,怎么样? Bobby-->>Alice: OK!
  1. 双方交换秘钥;
  2. Alice 用 Bobby 的公钥将明文(Bobby,下午三点一起吃火锅,怎么样?)加密,发送给 Bobby;
  3. Bobby 用自己的私钥解密,将密文还原为明文;
  4. Bobby 使用 Alice 的公钥将明文(OK!)加密,发送给 Alice;
  5. Alice 用自己的私钥解密,将密文还原为明文。

在正式开始交换秘钥前,先完成准备工作:为了模拟两个人通信,我们可以新建一个系统用户,useradd -m bobby,同时开两个终端,其中一个登录 Alice,另一个登录 Bobby,接下来就可以左右手互搏了。

# 为 Bobby 生成秘钥
gpg --full-gen-key
# 查看秘钥列表
gpg -k
/home/bobby/.gnupg/pubring.kbx
------------------------------
pub rsa4096 2022-07-14 [SC] [expires: 2025-07-13]
E630E139264BB08B62EC62939D35E01249C1450D
uid [ultimate] bobby (Bobby key) <bobby@example.com>
sub rsa4096 2022-07-14 [E] [expires: 2025-07-13]

交换秘钥

交换秘钥的第一步先要导出秘钥,--export 命令后的参数可以是 uid,也可以是用户邮箱地址,或者其中的一部分

Bobby 导出秘钥

# 二进制文件
gpg --output bobby.gpg --export bobby
# 纯文本文件
# gpg --output bobby.asc --armor --export bobby

Alice 导出秘钥

#二进制文件
gpg --output alice.gpg --export alice
# 纯文本文件
# gpg --output alice.asc --armor --export alice

与私钥不同,公钥是公开的,不用担心被窃取。 bobby.gpg 为 Bobby 的公钥,Bobby 可通过当面交换 U 盘或者发送文件等方式,将该文件送到 Alice 手中。也可以通过 --armor 选项生成一串经过 ASCII 编码的纯文本,通过邮件或者即时通讯工具发送纯文本给对方。

以 Alice 为例,拿到对方公钥后,首先导入秘钥,再对秘钥的指纹(fingerprint)进行验证,通过电话向 Bobby 确认指纹是否一致,如果一致,则证明文件在传递的过程中没有被篡改。最后使用 sign 命令签署。

Alice 导入 Bobby 的公钥

gpg --import bobby.asc
# 查看钥匙串
gpg --list-keys
/home/alice/.gnupg/pubring.kbx
------------------------------
pub rsa4096 2022-07-14 [SC] [expires: 2025-07-13]
45571605D9D46583891EE269D2E9A1DE3E6050E7
uid [ultimate] alice (Alice key for learning) <alice@example.com>
sub rsa4096 2022-07-14 [E] [expires: 2025-07-13]
pub rsa4096 2022-07-14 [SC] [expires: 2025-07-13]
E630E139264BB08B62EC62939D35E01249C1450D
uid [ unknown] bobby (Bobby key) <bobby@example.com>
sub rsa4096 2022-07-14 [E] [expires: 2025-07-13]
Alice 签署认证 Bobby 的公钥
gpg --edit-key bobby
gpg (GnuPG) 2.2.27; 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.
pub rsa4096/9D35E01249C1450D
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: unknown validity: unknown
sub rsa4096/DA3FED6077F7F30D
created: 2022-07-14 expires: 2025-07-13 usage: E
[ unknown] (1). bobby (Bobby key) <bobby@example.com>
gpg> fpr
pub rsa4096/9D35E01249C1450D 2022-07-14 bobby (Bobby key) <bobby@example.com>
Primary key fingerprint: E630 E139 264B B08B 62EC 6293 9D35 E012 49C1 450D
gpg> sign
pub rsa4096/9D35E01249C1450D
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: unknown validity: unknown
Primary key fingerprint: E630 E139 264B B08B 62EC 6293 9D35 E012 49C1 450D
bobby (Bobby key) <bobby@example.com>
This key is due to expire on 2025-07-13.
Are you sure that you want to sign this key with your
key "alice (Alice key for learning) <alice@example.com>" (D2E9A1DE3E6050E7)
Really sign? (y/N) y
gpg> save

Bobby 导入、签署认证 Alice 公钥的流程也是一样的。

加密信息与解密信息

双方秘钥认证完毕,就可以发送加密信息了,以 Alice 发送信息为例:

  1. Alice 使用 Bobby 的公钥对文件加密;
  2. Alice 将密文传递给 Bobby;
  3. Bobby 接收到加密文件后,用自己的私钥解密。
flowchart LR subgraph Alice direction LR i1["Bobby,下午三点一起吃火锅,怎么样?"] -->|加密| i2[MKHjjvbP...e+TXQs+CfR] end subgraph Bobby direction LR j1[解密信息] end i2 --> j1
# 明文文件
echo "Bobby,下午三点一起吃火锅,怎么样?" > msg
# 生成二进制加密文件
gpg --output msg.gpg --encrypt --recipient bobby msg
# 简化命令
# gpg -o msg.gpg -er bobby msg
# 或者生成纯文本加密文件
# gpg --output msg.asc --armor --encrypt --recipient bobby msg
# 简化命令
# gpg -o msg.asc -ear bobby msg

Bobby 解密信息的过程

flowchart LR subgraph Bobby direction LR i1[MKHjjvbP...e+TXQs+CfR] -->|解密| i2["Bobby,下午三点一起吃火锅,怎么样?"] end subgraph Alice j1[加密信息] end j1 --> i1
gpg --decrypt msg.gpg
# 简化命令
# gpg -d msg.gpg
# 通过 --output 输出到文件
# gpg --output msg.txt --decrypt msg.gpg

文件签名和验证

与文件加密不同,文件签名时,发送者需要用自己的私钥对文件签名,接受者使用发送者的公钥对文件进行验证。

Alice 对文件 doc 签名,sign 命令会对文档签名并压缩,生成二进制文件签名

# 生成需要签名的文件
echo "Hello world, Alice signature" > doc
gpg --output doc.sig --sign doc

Bobby 验证 Alice 的签名,检查签名并提取文档:

gpg --output doc-from-alice --decrypt doc.sig

有时候我们需要在电子邮件上签名,或者在网络帖子上签名。此时签名时压缩文档是不可取的。使用选项 --clearsign 会将文档包装为 ASCII 纯文本签名

创建纯文本签名

gpg --output doc.asc --clearsign doc

Bobby 验证签名

gpg --decrypt doc.asc

上面的两种签名方式用处有限,因为接收者必须从已签名的版本中恢复原始文件,这里推荐第三种签名方式——使用--detach-sign命令,创建分离的签名——更常用一些。

创建分离的签名

gpg --output doc.sig --detach-sign doc

Bobby 验证签名

gpg --verify doc.sig doc

对称加密

如果只是简单地将文件加密保存,对称加密是一个很好的选择。使用下面命令,输入口令后会生成加密过的文件,注意:这里不要使用和主密钥一样的口令。

gpg --output doc.gpg --symmetric doc

如何发布 PGP 公钥?

每个人加密的需求不同,所以用什么样的方式发布公钥,请根据自身情况来定。

通过 KeyServer 发布、吊销公钥

KeyServer 是专门用于收集和分发公钥的服务器,它的出现在一定程度上简化了人们分发秘钥的流程。用户将自己的公钥上传到公钥服务器,其他人在服务器上搜索 uid 或者 keyid ,就可以快速导入公钥。但是请注意,公钥一旦上传到服务器,是无法删除的,只能吊销,而且,生成秘钥时的用户名和邮箱地址都是公开的,任何人都能看见,所以建议使用伪邮箱地址来练习。更多关于 KeyServer 的内容推荐阅读 《2021 年,用更现代的方法使用 PGP》

ubuntu keyserver 为例,将本地公钥推送到远程服务器:

gpg --keyserver hkps://keyserver.ubuntu.com --send-key [PRIMARYKEYID]

搜索服务器中的公钥,搜索得出结果后,输入索引数字,导入公钥:

gpg --keyserver hkps://keyserver.ubuntu.com --search-keys [PRIMARYKEYID/UID]

直接导入 ID 为指定值的公钥:

gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys [PRIMARYKEYID]

生成吊销证书

gpg --output revoke.asc --gen-revoke alice

吊销本地秘钥

gpg --import revoke.asc

吊销信息同步服务器,吊销后 https://keyserver.ubuntu.com/ 能看到红色 revok 标记

gpg --keyserver hkps://keyserver.ubuntu.com --send-key [YOURPRIMARYKEYID]

信任网(web of trust)

使用 GPG 时,确认自己得到的公钥是否真的属于正确的人(即公钥合法性)非常重要,因为公钥可能会通过 中间人攻击 被替换。

为了证明公钥确实属于某个人,常规的做法是引入认证机构,由认证机构担保公钥的合法性,类似 https 证书的机制。Bobby 向认证机构注册自己的公钥,认证机构确认 Bobby 身份与公钥的一致性后,由认证机构对公钥签名,为 Bobby 的公钥合法性背书。当 Alice 要给 Bobby 发送加密信息时,Alice 从认证机构下载 Bobby 的公钥即可。

然而 GPG 并没有采用这种机制,而是采用了一种叫信任网的方法,在这种方法中,GPG 用户互相为对方的公钥进行签名认证。信任网的要点是不依赖认证机构,从而建立每个人之间的信任关系,就是自己去决定要信任哪些公钥。

使用命令 --edit-key ,能看到两个属性,一个是 trust,另一个为 validity(有效性)。trust 代表信任度,信任度是一种主观选择,一共有五个信任级别。

1 = I don't know or won't say (我不知道或不作答)
2 = I do NOT trust(我不相信)
3 = I trust marginally(我勉强相信)
4 = I trust fully(我完全相信)
5 = I trust ultimately(我绝对相信)
修改信任度
gpg --edit-key bobby@example.com
gpg (GnuPG) 2.2.27; 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.
pub rsa4096/9D35E01249C1450D
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: unknown validity: full
sub rsa4096/DA3FED6077F7F30D
created: 2022-07-14 expires: 2025-07-13 usage: E
[ full ] (1). bobby (Bobby key) <bobby@example.com>
gpg> trust
pub rsa4096/9D35E01249C1450D
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: unknown validity: full
sub rsa4096/DA3FED6077F7F30D
created: 2022-07-14 expires: 2025-07-13 usage: E
[ full ] (1). bobby (Bobby key) <bobby@example.com>
Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)
1 = I don't know or won't say
2 = I do NOT trust
3 = I trust marginally
4 = I trust fully
5 = I trust ultimately
m = back to the main menu
Your decision? 2
pub rsa4096/9D35E01249C1450D
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: never validity: full
sub rsa4096/DA3FED6077F7F30D
created: 2022-07-14 expires: 2025-07-13 usage: E
[ full ] (1). bobby (Bobby key) <bobby@example.com>
Please note that the shown key validity is not necessarily correct
unless you restart the program.
gpg> save
Key not changed so no update needed.
flowchart LR Alice --> Bobby --> Chloe Bobby --> Dharma

箭头代表签署关系,Alice 签署了 Bobby 的公钥,Bobby 的公钥对 Alice 是有效的,但 Alice 可以选择不信任 Bobby 正确验证并签署的公钥。这种情况下,仅凭 Bobby 的签名,Alice 是不会认为 Chloe 的公钥是有效的。

最初,公钥在你签名后才被视为有效,而现在可以使用更灵活的算法,如果公钥同时满足了两个条件则视为有效:

  1. 它有足够多的有效签名,比如
    • 本人为公钥添加签名,
    • 或者被一个完全信任的人签名,
    • 或者被三个有限信任的人签名。
  2. 秘钥到本人的路径最大为 5 步或者更短。

有限信任人签署的个数,和最大路径长度是可以设置的。下面假设需要两个有限信任密钥或一个完全受信任密钥来验证另一个密钥,且最大路径长度为 3。在计算示例中的有效密钥时,Bobby 和 Dharma 的密钥始终被认为是完全有效的,因为它们是由 Alice 直接签名的。假设 Dharma 是完全信任的,那么由他签署的 Chloe 和 Francis 的秘钥将被视为有效。再假设 Bobby 和 Dharma 都是有限信任的,根据两个有限信任的人签署后认为有效,那么 Chloe 的秘钥将被视为有效,而 Francis 的秘钥视为勉强有效。

flowchart LR Alice --> Bobby --> Chloe Alice --> Dharma --> Chloe --> Elena Dharma --> Francis

信任网的工作机制,有点像生活中的“熟人圈子”。好比 A 信任 B,B 信任 C,所以 A 也信任 C。信任并不能凭空产生,而是基于现有信任之上。

其他秘钥发布方式

  1. 当面交换
  2. 使用邮箱或者其他即时通讯工具
  3. 发布于个人网站或社交网络
  4. 代码仓库

其他

新增 UID

根据 GPG 官方手册的描述,GPG 秘钥可以设置多个 uid。现实生活中,一个人可以有多种身份,每种身份可能对应不同的邮箱和用户名。

新增 UID
gpg --edit-key alice@example.com
gpg (GnuPG) 2.2.27; 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.
Secret key is available.
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 1 trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: depth: 1 valid: 1 signed: 0 trust: 0-, 0q, 1n, 0m, 0f, 0u
gpg: next trustdb check due at 2025-07-13
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
[ultimate] (1). alice (Alice key for learning) <alice@example.com>
gpg> adduid
Real name: alice.github
Email address: alice.github@example.com
Comment:
You selected this USER-ID:
"alice.github <alice.github@example.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
sec rsa4096/D2E9A1DE3E6050E7
created: 2022-07-14 expires: 2025-07-13 usage: SC
trust: ultimate validity: ultimate
ssb rsa4096/103FCD7E12E2082B
created: 2022-07-14 expires: 2025-07-13 usage: E
[ultimate] (1) alice (Alice key for learning) <alice@example.com>
[ unknown] (2). alice.github <alice.github@example.com>
gpg> save

其他秘钥功能

查看子秘钥指纹:

gpg --list-keys --with-subkey-fingerprint

使用特定子秘钥加密:

# -r 后面参数为 0x[subkey id]!,一定要有!
gpg --recipient 0x103FCD7E12E2082B! --encrypt doc
# 或者
gpg -er 0x103FCD7E12E2082B! doc

使用特定子秘钥签名:假设用于签名的秘钥 id 为8FA3C48E0E9AF514

gpg --output doc.sig --local-user 0x8FA3C48E0E9AF514! --clearsign doc
# 短命令:
gpg -o doc.sig -u 0x8FA3C48E0E9AF514! --clearsign doc

当接受方为多人,可同时用多个秘钥加密:

gpg -r alice -r bobby -e doc

秘钥导出

使用 --export 命令导出全部公钥,其中包括 pub、所有 sub,全部 uid 信息。

导出钥匙串中全部公钥:

gpg --export > public-keys.gpg

导出私钥,其中包括 pub、sec、sub、ssb 和全部 uid 信息:

gpg --export-secret-keys > private-keys.gpg

导出子私钥,包括 pub、sub、ssb 和全部 uid 信息,无 sec:

gpg --export-secret-subkeys > private-subkeys.gpg

上面的三个导出命令都可以加参数,参数为 UID/PRIMARYKEYID 时,按其筛选。

导出uid为alice的全部公钥:

gpg --export alice > alice-pub-keys.gpg

参数为特定子秘钥的 id7D4DA3D8C798D51F,表示按子秘钥 id 筛选:

gpg --armor --export 0x7D4DA3D8C798D51F! > aaa.asc
gpg --armor --export-secret-keys 0x7D4DA3D8C798D51F! > bbb.asc
gpg --export-secret-subkeys 0x7D4DA3D8C798D51F! > ccc.asc

最后导出信任数据:

gpg --export-ownertrust > ownertrust.asc

以上就是 GPG 的基础知识和用法,关于秘钥的备份和导出策略在下一篇中 GPG 最佳实践 中叙述。

参考资料

本文作者:杨奇的博客
版权声明:本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可,非商业转载及引用请注明出处(作者、原文链接),商业转载请联系作者获得授权。
杨奇的博客 微信公众号
微信公众号
杨奇的博客
评论