使用GPG安全传递隐私信息
一、GPG 能解决什么问题
1. 保密性:只有指定接收方能够解密
发送方使用接收方的公钥加密文件,只有接收方保存的对应私钥才能解密。
即使加密文件经过协作平台、邮件服务器或其他中转服务,平台也无法直接读取文件内容。平台是否安全不再是保护文件内容的唯一前提。
2. 完整性和真实性:确认文件没有被篡改、确认发送者
GPG 还支持数字签名:
- 私钥用于生成签名;
- 公钥用于验证签名;
- 验证成功,说明文件内容自签名后没有被修改,并且签名对应于持有该私钥的人;但"这个公钥是否确实属于某个人",仍需要通过指纹等可信渠道核验。
仅加密文件只能提供保密性,不能充分证明发送者身份。需要同时确认发送者时,应使用"签名并加密"。
二、先理解 GPG 密钥结构
一套 GPG 密钥通常包含一个主密钥和一个或多个子密钥。公钥和私钥是两个不同的部分,但属于同一个密钥对。
text
主密钥对(Primary key pair)
├── pub 主公钥 可以公开
└── sec 主私钥 必须保护
子密钥对(Subkey pair)
├── sub 子公钥 可以公开
└── ssb 子私钥 必须保护
1. pub、sec、sub、ssb 分别是什么
| GPG 输出 | 含义 | 是否可以给别人 | 典型作用 |
|---|---|---|---|
pub |
主公钥 | 可以 | 标识身份、验证主密钥相关信息 |
sec |
主私钥(GPG 也称 secret key) | 不可以 | 管理密钥、认证子密钥,必要时也可以签名 |
sub |
子公钥 | 可以 | 日常加密或验证签名 |
ssb |
子私钥(secret subkey) | 不可以 | 日常解密、签名或身份认证 |
pub 与 sec 是同一个主密钥对的公钥和私钥;sub 与 ssb 是同一个子密钥对的公钥和私钥。因此它们显示相同的密钥标识或指纹是正常的:
text
pub ↔ sec 主公钥 ↔ 主私钥
sub ↔ ssb 子公钥 ↔ 子私钥
2. 主密钥和子密钥的区别
"主/子"描述的是密钥在密钥体系中的层级;"公钥/私钥"描述的是密钥的公开性。这是两个不同的维度:
text
公钥部分 私钥部分
主密钥(Primary key) pub sec
子密钥(Subkey) sub ssb
主密钥主要用于:
- 表示这套密钥对应的身份;
- 认证或签发子密钥;
- 撤销密钥或管理密钥有效期;
- 根据用途标记,也可以用于签名。
子密钥主要用于日常操作:
[E]:加密用途;[S]:签名用途;[A]:身份认证用途,例如 SSH;[C]:认证或签发其他密钥,通常由主密钥承担。
常见结构如下:
text
pub/sec [C] 主公钥/主私钥,用于管理身份
sub/ssb [S] 签名子公钥/签名子私钥
sub/ssb [E] 加密子公钥/加密子私钥
sub/ssb [A] 认证子公钥/认证子私钥(可选)
对于加密文件,典型关系是:
text
B 使用接收方的 sub [E] 加密 ──▶ 密文 ──▶ 接收方使用自己的 ssb [E] 解密
对于数字签名,典型关系是:
text
发送方的签名私钥 ──用于生成签名──▶ 发送方的签名公钥 ──用于验证签名
签名私钥通常是 ssb [S],也可能是具有 [S] 用途的主私钥 sec;对应的验证公钥通常是 sub [S] 或 pub [S]。
3. --list-keys 和 --list-secret-keys
bash
gpg --list-keys
查看本地保存的公钥,通常显示 pub 和 sub。
bash
gpg --list-secret-keys
查看本地拥有私钥材料的密钥,通常显示 sec 和 ssb。
例如:
text
pub rsa4096 2026-01-01 [SC]
PRIMARY-FINGERPRINT
uid Alice <alice@example.com>
sub rsa4096 2026-01-01 [E]
ENCRYPTION-SUBKEY-FINGERPRINT
text
sec rsa4096 2026-01-01 [SC]
PRIMARY-FINGERPRINT
uid Alice <alice@example.com>
ssb rsa4096 2026-01-01 [E]
ENCRYPTION-SUBKEY-FINGERPRINT
这里的 pub 和 sec 指向同一个主密钥,sub 和 ssb 指向同一个子密钥。[S]、[E]、[A] 和 [C] 才表示用途,不能仅根据 pub、sec、sub 或 ssb 判断它是签名密钥还是加密密钥。
三、Key ID 和指纹
查看密钥和完整指纹:
bash
gpg --list-keys --fingerprint
gpg --list-secret-keys --fingerprint
也可以显示较长格式的 Key ID:
bash
gpg --list-secret-keys --keyid-format=LONG
常见标识的区别如下:
text
SHORT Key ID:8 位十六进制字符
LONG Key ID :16 位十六进制字符
完整指纹 :常见 v4 密钥为 40 位十六进制字符
--keyid-format=LONG 只改变列表中 Key ID 的显示方式,不会改变密钥,也不会改变签名和解密行为。
短 Key ID 可能发生碰撞,因此确认密钥身份或编写重要脚本时,优先使用完整指纹。例如:
bash
gpg --fingerprint alice@example.com
导出时如何区分公钥和私钥
公钥和对应私钥显示相同的指纹是正常的。指纹只负责标识"要操作哪一套密钥",不负责决定导出公钥还是私钥:
text
指纹 = 密钥的身份证号码
--export = 取出公开部分
--export-secret-keys = 取出秘密部分
导出公钥:
bash
gpg --armor --export PRIMARY-FINGERPRINT > public-key.asc
导出私钥:
bash
gpg --armor --export-secret-keys PRIMARY-FINGERPRINT > private-key.asc
--armor 只负责把二进制 OpenPGP 数据转换为便于复制和传输的 ASCII 文本格式,不负责区分公钥和私钥。
公钥文件通常以以下内容开头:
text
-----BEGIN PGP PUBLIC KEY BLOCK-----
私钥文件通常以以下内容开头:
text
-----BEGIN PGP PRIVATE KEY BLOCK-----
私钥导出文件中通常也包含对应的公钥信息,但同时包含必须保护的私钥材料。因此,public-key.asc 可以发送给别人,private-key.asc 只能作为敏感备份保存。
四、完整操作流程
下面假设:
- A 是接收方,拥有密钥;
- B 是发送方,需要把敏感文件发给 A;
- 文件名为
secrets.txt。
1. A 生成密钥
如果 A 还没有密钥,可以使用完整向导:
bash
gpg --full-generate-key
--full-generate-key 会询问较完整的选项,例如算法、密钥长度、过期时间、姓名、邮箱和保护密码。
系统会引导你完成一系列选择。不同 GnuPG 版本显示的菜单编号可能不同,应以终端实际显示的选项为准:
- 算法类型 :如果需要较好的兼容性,可以选择 RSA;如果使用的软件都支持现代椭圆曲线算法,也可以选择 ECC。不要机械地依赖某个固定菜单编号,例如
(1)或(9),因为不同版本的选项顺序可能不同。 - 密钥长度 :如果选择 RSA,通常可以选择
4096位。相较于2048位,4096 位提供更大的安全余量,但密钥和计算开销也更大;在一般文件加密和签名场景中,4096 位是常见选择。 - 有效期 :可以根据密钥用途设置有效期,例如输入
2y表示两年。直接按回车通常表示永不过期;为了降低长期使用同一密钥的风险,通常建议设置有效期,并在需要时续期。 - 个人信息:输入用于标识这套密钥的姓名和电子邮箱。若该密钥用于公开身份,通常填写真实姓名和常用邮箱;这些信息会写入 UID 并随公钥公开,因此有隐私要求时应谨慎填写。
- 密码(Passphrase):这是保护私钥的重要密码。它相当于给存储在设备上的私钥增加一道额外保护;即使别人取得了私钥文件,没有 Passphrase 也通常不能直接使用私钥。请使用足够强且能够长期记住的密码,不要与其他账号共用。
也可以使用:
bash
gpg --generate-key
--gen-key 是 --generate-key 的缩写,使用当前默认参数生成密钥,交互选项较少:
text
--gen-key 使用当前默认参数
--full-generate-key 通过完整向导选择更多参数
具体生成的算法和子密钥会受 GnuPG 版本及向导选项影响。生成后,应设置足够强的 Passphrase,并妥善保存恢复信息和撤销证书。
2. 删除密钥(谨慎操作)
删除操作只影响当前机器上的本地密钥环,不会撤销或删除别人已经保存的公钥,也不会删除公钥服务器上的副本。
删除私钥
bash
gpg --delete-secret-keys PRIMARY-FINGERPRINT
删除公钥
bash
gpg --delete-keys PRIMARY-FINGERPRINT
如果同一套密钥同时存在公钥和私钥,通常应先删除私钥,再删除公钥:
bash
gpg --delete-secret-keys PRIMARY-FINGERPRINT
gpg --delete-keys PRIMARY-FINGERPRINT
也可以同时删除私钥和公钥:
bash
gpg --delete-secret-and-public-key PRIMARY-FINGERPRINT
删除前应确认使用的是完整指纹,而不是可能发生冲突的短 Key ID;需要保留密钥时,应先完成私钥备份。
3. A 查看自己的密钥和指纹
bash
gpg --list-secret-keys --keyid-format=LONG
确认主密钥指纹:
bash
gpg --fingerprint alice@example.com
向 B 提供完整指纹时,应通过电话、面对面沟通或其他可信渠道核对,不能只依赖邮件地址或短 Key ID。
4. A 导出并发送公钥
使用主密钥的完整指纹导出公钥:
bash
gpg --armor --export PRIMARY-FINGERPRINT > alice-public.asc
例如:
bash
gpg --armor --export \
E86BC0DF1976A5AEA2C9E9602E49C19A0A3934AE \
> alice-public.asc
--export 默认导出公钥;--armor 将二进制 OpenPGP 数据转换成便于邮件、聊天和文本文件传输的 ASCII 文本格式。生成的文件通常称为"ASCII 装甲公钥"或"公钥块"。
alice-public.asc 中通常包含:
- 主公钥
pub; - 一个或多个子公钥
sub; - UID(姓名和邮箱);
- 公钥相关的认证签名。
它不包含:
- 主私钥
sec; - 子私钥
ssb。
因此可以把 alice-public.asc 发给 B:
text
可以公开:alice-public.asc
不能公开:A 的私钥和私钥备份
如果不指定用户名或指纹,下面的命令可能会导出本机密钥环中的所有公钥,不建议在不确定时直接使用:
bash
gpg --armor --export
5. B 导入 A 的公钥
B 收到文件后执行:
bash
gpg --import alice-public.asc
然后查看导入结果:
bash
gpg --list-keys --keyid-format=LONG
B 应使用 A 提供的可信渠道核对完整指纹:
bash
gpg --fingerprint alice@example.com
只有指纹核对一致,B 才能确认导入的公钥确实属于 A。公钥服务器或聊天平台只能帮助传输公钥,不能替代指纹核验。
GPG 的信任值是本地对某个密钥持有者是否能够正确认证其他密钥的判断,不等同于指纹核验。直接给已核对指纹的公钥加密时,不应把设置 ultimate trust 当成身份验证的替代品;是否设置信任等级,应根据自己的密钥管理策略决定。
6. B 使用 A 的公钥加密文件
bash
gpg --armor --encrypt \
--recipient ALICE-FINGERPRINT \
secrets.txt
例如:
bash
gpg --armor --encrypt \
--recipient E86BC0DF1976A5AEA2C9E9602E49C19A0A3934AE \
secrets.txt
GPG 会自动选择 A 标记为 [E] 的加密子公钥 sub。输出文件通常为:
text
secrets.txt.asc
加密关系是:
text
B 使用 A 的 sub [E] 加密
B 把 secrets.txt.asc 发给 A
A 使用自己的 ssb [E] 解密
B 不需要 A 的私钥。B 只需要 A 的公钥即可完成加密。
加密不会自动删除原始文件。确认密文可以正常解密后,应根据文件敏感程度妥善处理原始明文;在 SSD、日志、备份和同步盘环境中,普通删除不一定能彻底擦除所有副本。
7. A 解密文件
A 收到 secrets.txt.asc 后执行:
bash
gpg --output secrets-decrypted.txt --decrypt secrets.txt.asc
GPG 会使用 A 本机保存的 ssb [E] 解密,必要时要求输入生成密钥时设置的 Passphrase。
五、同时签名和加密
如果 B 只执行加密:
bash
gpg --armor --encrypt --recipient ALICE-FINGERPRINT secrets.txt
主要保证的是"只有 A 能解密"。为了让 A 验证文件确实由 B 发出,B 还需要使用自己的签名私钥签名:
bash
gpg --armor --sign --encrypt \
--recipient ALICE-FINGERPRINT \
secrets.txt
此时流程是:
text
B 使用自己的签名私钥(通常是 ssb [S])生成签名
B 使用 A 的 sub [E] 加密
│
▼
密文发送给 A
│
▼
A 使用自己的 ssb [E] 解密
A 使用 B 的对应签名公钥验证签名
如果 A 需要验证 B 的签名,A 也必须提前取得并核对 B 的公钥指纹。
六、私钥备份和公钥导出
导出公钥
bash
gpg --armor --export PRIMARY-FINGERPRINT > public-key.asc
这是可以发送给别人的公钥文件。
导出完整私钥备份
bash
gpg --armor --export-secret-keys PRIMARY-FINGERPRINT > secret-key-backup.asc
该文件包含主私钥和私有子密钥,属于高敏感信息:
- 不要通过普通邮件、聊天工具或公共网盘发送;
- 不要提交到 Git 仓库;
- 使用离线、加密的备份介质保存;
- 同时保管好私钥 Passphrase;
- 不再需要时,应按所在存储介质的安全策略处理。
导出不带可用主私钥的子私钥材料
在需要把日常使用密钥放到另一台机器、而把主私钥离线保存的高级场景中,可以使用:
bash
gpg --armor --export-secret-subkeys PRIMARY-FINGERPRINT > secret-subkeys.asc
该命令用于把日常使用的私有子密钥材料导出到另一台机器,同时让导出的主私钥部分不可用;导出内容仍可能包含对应的公钥信息。这个操作涉及离线主密钥和密钥恢复策略,不熟悉时不要替代完整私钥备份使用。
七、常用命令速查
bash
# 查看公钥
gpg --list-keys
# 查看私钥
gpg --list-secret-keys
# 查看完整指纹
gpg --fingerprint
# 显示 16 位 Key ID
gpg --list-secret-keys --keyid-format=LONG
# 导入公钥
gpg --import public-key.asc
# 导出 ASCII 格式公钥
gpg --armor --export PRIMARY-FINGERPRINT > public-key.asc
# 加密文件
gpg --armor --encrypt --recipient RECIPIENT-FINGERPRINT secrets.txt
# 解密文件
gpg --output secrets.txt --decrypt secrets.txt.asc
# 签名并加密
gpg --armor --sign --encrypt --recipient RECIPIENT-FINGERPRINT secrets.txt