前言
在日常 GPG 的使用场景中,很多密钥持有者都是直接使用主密钥进行签名或加密的,这是强烈不推荐的。日常频繁使用主密钥,会大幅提升私钥泄露的风险;一旦主密钥失窃,整套身份验证的信任链将彻底崩塌。
攻击者获取主密钥后可以随意签发、吊销密钥,或是冒用身份分发签名、加密文件,验证者无法辨别内容真伪。而密钥持有者只能选择吊销主密钥并发布吊销声明,创建新的密钥并重新创建信任链。
若采用主密钥离线保管、子密钥日常使用的管理方案,被盗风险仅限于子密钥范畴内,不会影响到主密钥。
子密钥泄露后密钥持有者可进行吊销、重新创建子密钥并分发新公钥,验证者及时更新公钥即可拦截吊销后发布的伪造内容。并且轮换子密钥不会影响历史签名的验证,这样可以有效保全原有的信任体系。
NOTE若密钥泄露后、正式吊销前已产生签名、加密内容,即便更换新子密钥,也无法核验历史内容真伪。可借助
faketime工具回溯时间执行吊销,可以缓解该问题
一、GPG 密钥的功能
在开始 GPG 密钥创建之前,先了解一下 GPG 各类密钥的定位与功能
| 密钥类型 | 能力标记 | 能力含义(行为) | 实际用途 |
|---|---|---|---|
| 主密钥(默认) | CS | Certify:用于签发、吊销和管理子密钥 Sign:生成数字签名(不推荐日常使用) | 身份锚点、子密钥管理 |
| 签名子密钥 | S | Sign:生成可验证数字签名 | Git commit、文件签名、发布签名 |
| 加密子密钥 | E | Encrypt:密钥协商与数据加密 | 文件加密、邮件加密 |
| 认证子密钥(可选) | A | Authenticate:基于挑战响应的身份认证 | SSH 登录、系统认证 |
主密钥核心职能为管理子密钥,因此安全实践中建议将主密钥精简为仅保留签发、管理子密钥(Certify) 权限。
由具备签名、加密能力的子密钥承接日常操作,这是保障 GPG 身份体系长期稳定、提升整体安全性的核心设计思路与基础。
二、生成具有 Certify(认证)能力的主密钥
TIP本文以创建
Ed25519和Curve25519算法的密钥为例
2.1 选择密钥类型和自定义主密钥能力
使用以下命令开始自定义生成
gpg --full-generate-key --expert根据提示选择(11) ECC (set your own capabilities)进行能力的自定义
默认的能力可能因为系统和版本的不同而不同,我们只需要保留Certify(认证)
以 Ubuntu 为例默认能力是Sign(签名),输入S即可将能力更改为Certify,确认后输入Q完成自定义
ubuntu@server:~# gpg --full-generate-key --expertgpg (GnuPG) 2.4.4; Copyright (C) 2024 g10 Code GmbHThis is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.
Please select what kind of key you want: (1) RSA and RSA (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 (sign and encrypt) *default* (10) ECC (sign only) (11) ECC (set your own capabilities) (13) Existing key (14) Existing key from cardYour selection? 11
Possible actions for this ECC key: Sign Certify AuthenticateCurrent allowed actions: Sign Certify
(S) Toggle the sign capability (A) Toggle the authenticate capability (Q) Finished
Your selection? S
Possible actions for this ECC key: Sign Certify AuthenticateCurrent allowed actions: Certify
(S) Toggle the sign capability (A) Toggle the authenticate capability (Q) Finished
Your selection? q2.2 选择加密算法
输入1或直接回车选择默认的加密方法Curve 25519
Please select which elliptic curve you want: (1) Curve 25519 *default* (2) Curve 448 (3) NIST P-256 (4) NIST P-384 (5) NIST P-521 (6) Brainpool P-256 (7) Brainpool P-384 (8) Brainpool P-512 (9) secp256k1Your selection? 12.3 输入有效期
根据需要输入主密钥的有效期,建议填写5y或10y,默认0为永久有效
输入后输入y进行确认
TIP主密钥不建议永久有效
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 yearsKey is valid for? (0) 10yKey expires at Sun Sep 8 08:33:35 2030 UTCIs this correct? (y/N) y2.4 输入密钥持有者的信息
按照提示依次输入姓名、邮箱以及备注
备注为可选字段,用于补充身份说明,可留空不填
填写后输入O进行确认,如果需要修改信息可在此时输入N E C修改姓名、邮箱以及备注
GnuPG needs to construct a user ID to identify your key.
Real name: self4mEmail address: self4m@example.comComment:You selected this USER-ID: "self4m <self4m@example.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O2.5 设置密码完成 GPG 主密钥生成
CAUTION建议设置高强度密码,强化密钥防护
2.6 查阅主密钥信息
使用如下命令查看密钥的公钥信息
gpg --list-keys
# 示例如下ubuntu@server:~# gpg --list-keyspub ed25519 2025-09-09 [C] [expires: 2030-09-08] C5D403FBDEDF67791962A393F9904AD5C93B705Auid self4m <self4m@example.com>使用如下命令查看密钥的私钥信息
gpg --list-secret-keys
# 示例如下ubuntu@server:~# gpg --list-secret-keyssec ed25519 2025-09-09 [C] [expires: 2030-09-08] C5D403FBDEDF67791962A393F9904AD5C93B705Auid [ultimate] self4m <self4m@example.com>额外查看密钥的 Long Key ID 信息
gpg --list-keys --keyid-format=LONG
# 示例如下ubuntu@server:~# gpg --list-keys --keyid-format=LONGpub ed25519/F9904AD5C93B705A 2025-09-09 [C] [expires: 2030-09-08] C5D403FBDEDF67791962A393F9904AD5C93B705Auid [ultimate] self4m <self4m@example.com>gpg --list-secret-keys --keyid-format=LONG
# 示例如下ubuntu@server:~# gpg --list-secret-keys --keyid-format=LONGsec ed25519/F9904AD5C93B705A 2025-09-09 [C] [expires: 2030-09-08] C5D403FBDEDF67791962A393F9904AD5C93B705Auid [ultimate] self4m <self4m@example.com>字段说明:
-
sec表示当前列出的为私钥(Secret Key) -
pub表示当前列出的为公钥(Public Key) -
ed25519表示该密钥使用 Ed25519 算法
密钥实际使用算法由选择的能力决定 -
2025-09-09表示密钥创建日期 -
[C]表示该主密钥仅具备 Certify(认证) 能力 -
[expires: 2030-09-08]表示密钥过期时间
密钥过期后不会自动失效,但会被 GPG 及 Git 托管平台标记为过期状态,可由主密钥手动延长 -
C5D403FBDEDF67791962A393F9904AD5C93B705A
密钥指纹 - Fingerprint,是密钥的全局唯一标识 -
F9904AD5C93B705A
长密钥 ID - Long Key ID,是密钥指纹的后16位 -
[ultimate] self4m <self4m@example.com>
[ultimate]为本地信任级别,仅表示该密钥在当前环境中的信任级别
self4m为密钥持有者名称self4m@example.com为密钥持有者的邮箱,GitHub、GitLab 等平台验证 git 签名时,会校验此处邮箱是否与提交使用的邮箱一致
三、添加子密钥
3.1 进入密钥编辑模式
gpg --edit-key F9904AD5C93B705A # 使用密钥指纹或者是 Long Key ID 均可
# 示例如下ubuntu@server:~# gpg --edit-key F9904AD5C93B705Agpg (GnuPG) 2.4.4; Copyright (C) 2024 g10 Code GmbHThis 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 ed25519/F9904AD5C93B705A created: 2025-09-09 expires: 2030-09-08 usage: C trust: ultimate validity: ultimate[ultimate] (1). self4m <self4m@example.com>gpg>3.2 创建子密钥
输入addkey开始创建子密钥,具体的创建流程与上文一致
gpg> addkey3.2.1 创建签名子密钥
- 密钥类型选择
(10) ECC (sign only) - 加密算法选择
Curve 25519 - 有效期建议
1y或2y
输入y确认创建并输入主密钥密码完成创建
gpg> addkeyPlease select what kind of key you want: (3) DSA (sign only) (4) RSA (sign only) (5) Elgamal (encrypt only) (6) RSA (encrypt only) (10) ECC (sign only) (12) ECC (encrypt only) (14) Existing key from cardYour selection? 10Please select which elliptic curve you want: (1) Curve 25519 *default* (4) NIST P-384 (6) Brainpool P-256Your selection? 1Please 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 yearsKey is valid for? (0) 1yKey expires at Tue Sep 8 08:34:55 2026 UTCIs this correct? (y/N) yReally create? (y/N) yWe need to generate a lot of random bytes. It is a good idea to performsome other action (type on the keyboard, move the mouse, utilize thedisks) during the prime generation; this gives the random numbergenerator a better chance to gain enough entropy.
sec ed25519/F9904AD5C93B705A created: 2025-09-09 expires: 2030-09-08 usage: C trust: ultimate validity: ultimatessb ed25519/AC3897D449CE8390 created: 2025-09-09 expires: 2026-09-08 usage: S[ultimate] (1). self4m <self4m@example.com>
gpg>3.2.2 创建加密子密钥
- 密钥类型选择
(12) ECC (encrypt only) - 加密算法选择
Curve 25519 - 有效期建议
1y或2y
输入y确认创建并输入主密钥密码完成创建
gpg> addkeyPlease select what kind of key you want: (3) DSA (sign only) (4) RSA (sign only) (5) Elgamal (encrypt only) (6) RSA (encrypt only) (10) ECC (sign only) (12) ECC (encrypt only) (14) Existing key from cardYour selection? 12Please select which elliptic curve you want: (1) Curve 25519 *default* (4) NIST P-384 (6) Brainpool P-256Your selection? 1Please 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 yearsKey is valid for? (0) 1yKey expires at Tue Sep 8 08:35:22 2026 UTCIs this correct? (y/N) yReally create? (y/N) yWe need to generate a lot of random bytes. It is a good idea to performsome other action (type on the keyboard, move the mouse, utilize thedisks) during the prime generation; this gives the random numbergenerator a better chance to gain enough entropy.
sec ed25519/F9904AD5C93B705A created: 2025-09-09 expires: 2030-09-08 usage: C trust: ultimate validity: ultimatessb ed25519/AC3897D449CE8390 created: 2025-09-09 expires: 2026-09-08 usage: Sssb cv25519/92F1AB15A1AD51AF created: 2025-09-09 expires: 2026-09-08 usage: E[ultimate] (1). self4m <self4m@example.com>
gpg>3.3 保存更改并退出
gpg> save四、主密钥的隔离与备份
CAUTION在执行主密钥隔离前,确保你已经创建了用于日常使用的子密钥(签名/加密等),并完成必要导出与备份
4.1 导出公钥
gpg --armor --export F9904AD5C93B705A > public.ascpublic.asc 主要用于对外发布,供验证者校验签名以及向密钥持有者发送加密文件使用
4.2 导出子密钥私钥
gpg --armor --export-secret-subkeys F9904AD5C93B705A > subkeys.asc此命令会导出所有子密钥私钥,将子密钥私钥导入其他设备时主密钥私钥会被替换为占位符(stub)
因此导入此子密钥后只能在设备上使用签名、加密能力,无法执行addkey revkey adduid 等主密钥管理操作
这也正是 主密钥离线保管、子密钥日常使用 的核心安全设计——将高风险的主密钥权限严格隔离,仅在可信环境中持有,从而最大限度降低私钥泄露带来的整体风险
4.3 备份完整私钥信息
gpg --armor --export-secret-keys F9904AD5C93B705A > master-full.asc此命令会导出该密钥的完整私钥信息,包含主密钥私钥与所有子密钥私钥
建议将 master-full.asc 进行多处离线备份,确保密钥丢失后可完整恢复。
完成备份后,建议在隔离环境(如临时虚拟机、离线设备)中执行以下命令,验证备份文件的有效性:
gpg --import master-full.ascecho "test" | gpg --encrypt --recipient self4m@example.com > test.gpggpg --decrypt test.gpg若 gpg --decrypt test.gpg 能成功输出明文test,说明备份的文件完整且可用
TIP
--recipient用于指定加密接收者,可以使用接收者的邮箱地址、公钥指纹(Fingerprint)或 Long Key ID
在生产环境中,强烈建议优先使用完整 Fingerprint 以避免 Key ID 碰撞风险
4.4 日常设备仅导入子密钥私钥
# 若当前设备已存在完整私钥,先删除整套密钥材料,避免状态异常gpg --delete-secret-keys F9904AD5C93B705A
# 导入仅包含子密钥私钥的备份(主密钥私钥会自动变为占位符 stub)gpg --import subkeys.ascCAUTION若仅单独导出并备份主私钥(不含子密钥私钥),加密子密钥私钥一旦丢失将无法恢复。
这类问题常出现在密钥迁移、重装系统、清理密钥环或误删密钥等场景,例如仅备份主密钥(非上文提到的备份方法,本文中不描述该方式命令,避免发生操作失误)、仅备份公钥、备份主密钥后生成子密钥但未重新备份、仅导出子密钥用于日常使用后却未保留完整离线备份,或在子密钥私钥已从本地密钥环中删除后才执行导出操作。上述操作均会导致备份中不包含加密子密钥私钥,进而无法解密历史数据。
在 OpenPGP、GnuPG 的密钥体系中,子密钥私钥是独立存在的密钥材料,并非由主密钥派生生成。主密钥仅用于身份认证及子密钥的签发与吊销,不具备恢复或替代子密钥私钥的能力
因此,是否能够恢复历史加密数据,不取决于是否单独保留了主密钥,而完全取决于备份中是否包含用于加密的子密钥私钥;一旦加密子密钥私钥在备份或迁移过程中丢失或未被备份,即使主密钥私钥仍然完整存在,所有使用该子密钥加密的数据也将永久无法解密。
补充说明:使用
gpg --export-secret-keys 密钥ID导出的是完整私钥包,会同时包含主密钥与所有子密钥私钥,是标准的全量备份方案。在执行主密钥隔离、删除主密钥私钥或轮换子密钥之前,必须确认加密子密钥私钥已被完整备份,并至少保留一份包含主密钥及全部子密钥私钥的离线备份;建议在新设备或新环境中验证该备份能成功解密历史数据后,再进行密钥删除或吊销操作
五、加密与解密(使用加密子密钥)
在主密钥隔离的安全方案中,加密与解密操作将分别由子密钥完成,主密钥不参与具体的数据加密与解密过程
NOTE关于 GPG 的加密密钥选择规则说明 发送方在执行加密操作时,如果接收方的公钥中仅存在一个可用于加密的子密钥,GPG 会自动使用该子密钥进行加密 发送方在执行加密操作时,如果接收方的公钥中存在多个可用于加密的子密钥,GPG 会根据密钥有效性、未吊销状态及算法匹配情况自动选择合适的加密子密钥(通常优先选择最新创建的子密钥)
5.1 加密文件
使用接收方邮箱receiver@example.com对应的公钥加密文件
gpg --encrypt --recipient receiver@example.com file.txt也可以使用接收方的公钥 ID 进行加密:
gpg --encrypt --recipient XXXXXXXXXXXXXXXX file.txt执行后默认生成后缀为 .gpg 的加密文件
5.2 解密文件
解密他人使用你的公钥加密的文件:
gpg --decrypt file.txt.gpg > file.txt六、数字签名(使用签名子密钥)
在主密钥隔离的安全方案中,签名和验签操作分别由签名子密钥完成,主密钥不参与具体的签名与验签操作
NOTE关于 GPG 的密钥选择规则说明(签名)
- 发送方在执行签名操作时,如果本地密钥环中仅存在一个可用于签名的子密钥,GPG 会自动使用该子密钥进行签名
- 发送方在执行签名操作时,如果本地密钥环中存在多个可用于签名的子密钥,GPG 会根据密钥有效性、未吊销状态及用途标记等条件自动选择合适的签名子密钥(通常优先选择仍在有效期内的子密钥)
- 如需手动指定本地用于签名的密钥,可通过两种方式配置
- 永久默认:在
~/.gnupg/gpg.conf中添加default-key AAAAAAAAAAAAAAAA
推荐使用密钥的完整指纹,该操作仅影响签名,不影响加密
若采用主密钥隔离方法,推荐显式指定具有签名能力的 [S] 子密钥 ID- 单次生效:命令行使用 -u / —local-user 参数临时指定密钥,该方式优先级高于配置文件
6.1 签名文件
# 生成二进制签名文件(包含源文件+签名,不可直接阅读)# 输出:file.txt.gpg(验证时需直接解密/验证该文件)gpg --sign file.txt
# 生成独立分离式签名文件(源文件保持不变,签名单独存放)# 输出:file.txt.sig(验证时需源文件 + 签名文件同时存在)gpg --detach-sign file.txt
# 生成带明文内容的 ASCII 签名文件(可直接阅读原文,自带签名)# 输出:file.txt.asc(适合邮件、论坛、文本传输场景)gpg --clearsign file.txt6.2 验证签名
# 验证二进制签名文件(仅校验签名有效性,不提取文件内容)gpg --verify file.txt.gpg
# 提取并还原源文件(自动验证签名)gpg --decrypt file.txt.gpg > file.txt
# 验证分离式签名(需同时提供签名文件 + 原始文件)gpg --verify file.txt.sig file.txt
# 验证 clearsign 明文签名文件gpg --verify file.txt.ascNOTE对于
gpg --sign生成的签名封装文件,gpg --verify仅用于校验签名,不会输出原始内容
如需恢复原文件,应使用gpg --decrypt进行解包
NOTE在实际使用中,签名与加密可以组合使用,例如在向他人发送文件时同时保证机密性与来源可信性:
Terminal window gpg --sign --encrypt --recipient receiver@example.com file.txt使用
gpg --decrypt进行解包时,gpg 会自动尝试验证内嵌的签名,并在输出中提示签名是否有效
七、子密钥吊销与轮换
NOTE
- 执行该操作前,需在当前设备导入完整主密钥备份文件
- 子密钥吊销操作不可逆;若尚未将更新后的公钥同步至公钥服务器,可尝试删除本地整套密钥进行重置
- 吊销子密钥不会影响主密钥本身,原有身份信任关系保持有效,可重新生成子密钥完成密钥轮换
- 操作完成后,请重新导出并发布公钥,让其他用户及时同步子密钥吊销状态
推荐使用公钥服务器https://keys.openpgp.org发布更新后的公钥,这也是目前主流的公钥分发渠道Terminal window # 上传公钥(GnuPG 2.2+ 默认目标为 keys.openpgp.org)gpg --send-keys 密钥ID- 吊销仅对外标识该子密钥不再用于新的加密与签名,不会变更原有公私钥配对关系
他人使用旧公钥加密的历史文件,仍需依靠对应的旧私钥解密
切勿删除已吊销的加密子密钥私钥,否则历史加密数据将永久无法解密
7.1 子密钥吊销
- 使用
gpg --edit-key F9904AD5C93B705A进入密钥编辑模式 - 确认需要吊销的子密钥编号(编号按生成顺序从 1 开始)
- 输入
key 编号选中目标子密钥(选中后密钥前会标记 * 号) - 输入
revkey启动吊销流程 - 输入
y确认吊销操作 - 根据提示选择吊销原因,并可选择性填写额外描述信息
- 输入主密钥密码完成吊销后,输入
save保存并退出编辑模式
ubuntu@server:~# gpg --edit-key F9904AD5C93B705Agpg (GnuPG) 2.4.4; Copyright (C) 2024 g10 Code GmbHThis 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 ed25519/F9904AD5C93B705A created: 2025-09-09 expires: 2030-09-08 usage: C trust: ultimate validity: ultimatessb ed25519/AC3897D449CE8390 created: 2025-09-09 expires: 2026-09-08 usage: Sssb cv25519/92F1AB15A1AD51AF created: 2025-09-09 expires: 2026-09-08 usage: E
gpg> key 1
sec ed25519/F9904AD5C93B705A created: 2025-09-09 expires: 2030-09-08 usage: C trust: ultimate validity: ultimatessb* ed25519/AC3897D449CE8390 created: 2025-09-09 expires: 2026-09-08 usage: Sssb cv25519/92F1AB15A1AD51AF created: 2025-09-09 expires: 2026-09-08 usage: E
gpg> revkeyDo you really want to revoke this subkey? (y/N) yPlease 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 = CancelYour decision? 1Enter an optional description; end it with an empty line:>Reason for revocation: Key has been compromised(No description given)Is this okay? (y/N) y
sec ed25519/F9904AD5C93B705A created: 2025-09-09 expires: 2030-09-08 usage: C trust: ultimate validity: ultimatessb cv25519/92F1AB15A1AD51AF created: 2025-09-09 expires: 2026-09-08 usage: EThe following key was revoked on 2025-09-09 by ? key F9904AD5C93B705A self4m <self4m@example.com>ssb ed25519/AC3897D449CE8390 created: 2025-09-09 expires: 2026-09-08 usage: S[ ultimate] (1). self4m <self4m@example.com>
gpg> save7.2 子密钥轮换
轮换方式与 #三添加职能子密钥 章节内容一致
八、延长主密钥与子密钥有效期
子密钥即将过期时,可在持有完整主密钥私钥的离线环境中延长有效期,无需重新创建
- 使用
gpg --edit-key F9904AD5C93B705A进入密钥编辑模式 - 延长主密钥:直接输入
expire - 延长子密钥:先确认子密钥编号,输入
key 编号选中目标子密钥(选中后密钥前会标记 * 号),然后输入expire - 按提示输入新有效期(如
10y),输入y确认,输入密钥密码完成授权 - 输入
save保存并退出
NOTE有效期修改完成后,需重新导出公钥并同步至公钥服务器。
九、修改密钥密码
TIP
- 该操作仅修改当前设备上本地存储的私钥保护密码,不会影响任何其他设备或公钥
- 若设备上没有主密钥私钥,则会直接修改本地子密钥私钥的保护密码
- 在仅保留子密钥私钥的环境中,同样可以正常执行
- 修改密码后导入主密钥的完整备份将会覆盖设置的子密钥的密码
- 使用
gpg --edit-key F9904AD5C93B705A进入密钥编辑模式 - 输入
passwd,按提示输入旧密码并设置新密码 - 输入
save保存并退出
十、生成主密钥吊销证书(必须)
NOTE
- 主密钥吊销证书必须在主密钥私钥仍然存在且受控时提前生成,并离线安全保存
- 一旦主密钥丢失或私钥泄露,应立即发布吊销证书,对外声明密钥永久失效
- 吊销证书本身不包含私钥,但拥有等效吊销权限,必须离线保管、切勿提前上传
- 吊销发布后无法撤回,会让整个密钥(主密钥 + 所有子密钥)全部失效
10.1 生成吊销证书
gpg --output revoke.asc --gen-revoke F9904AD5C93B705A10.2 发布吊销证书
当密钥丢失、泄露或不再使用时,按以下步骤发布吊销:
- 将吊销证书导入本地密钥
gpg --import revoke.asc导入后,本地公钥会立即标记为已吊销,本地所有使用都会提示密钥已失效
但只要还未将吊销后的公钥上传到公钥服务器,就可以撤销吊销
# 删除本地已标记吊销的密钥gpg --delete-secret-and-public-keys F9904AD5C93B705A
# 重新导入原始备份公钥即可恢复正常gpg --import public.asc- 上传更新后的公钥到公钥服务器
gpg --send-keys F9904AD5C93B705A- 主密钥吊销证书一旦上传公钥服务器,整组密钥(主密钥 + 所有子密钥)永久作废,无任何恢复手段
吊销状态会全网同步并永久生效,任何人获取该公钥都会看到密钥已被吊销,无法再用于加密、签名或验签
非商业用途可以使用,但必须注明出处;
若有改编需采用相同许可协议发布。