GPG 使用最佳实践


GPG(GNU Privacy Guard)是 OpenPGP 标准的开源实现,广泛用于加密文件、签名代码提交、保护邮件通信等场景。本文整理了日常使用 GPG 的最佳实践,帮助你在安全与便利之间找到平衡点。

一、密钥生成

选择合适的算法与长度

gpg --full-generate-key

推荐选择:

  • RSA 4096 —— 兼容性最好,适合大多数场景
  • Ed25519 —— 现代椭圆曲线算法,密钥短、速度快、安全强度高(推荐)

避免使用 DSA 1024 或 RSA 1024,这些算法已被认为不安全。

设置合理的过期时间

为主密钥设置 1~2 年的有效期,到期前通过续期(extend)而非重新生成的方式延续使用。有效期可以有效降低密钥泄漏后的持续伤害。

# 修改密钥有效期
gpg --edit-key <KEY_ID>
gpg> expire

填写正确的 UID

UID 格式为 Name <email@example.com>,建议:

  • 使用常用真实姓名(或昵称)
  • 邮箱与实际使用的一致
  • 如有多个邮箱,可以添加多个 UID

二、主密钥与子密钥分离

这是 GPG 安全使用中最重要的实践之一。

为什么要分离?

角色 用途 存放位置
主密钥(Primary Key) 证明身份、签署子密钥 离线冷存储
签名子密钥(Sign Subkey) 日常签署提交/文档 工作机或智能卡
加密子密钥(Encrypt Subkey) 接收加密邮件/文件 工作机或智能卡
认证子密钥(Auth Subkey) SSH 认证(可选) 智能卡

主密钥一旦泄漏,整个信任链就会崩溃。将主密钥保存在离线环境中,日常使用仅依赖子密钥,可以极大降低风险。

生成子密钥

gpg --edit-key <KEY_ID>
gpg> addkey
# 选择类型(签名/加密/认证)和有效期
gpg> save

导出并删除主密钥(仅保留子密钥在工作机)

# 备份完整密钥(含主密钥)到安全介质
gpg --export-secret-keys --armor <KEY_ID> > full-secret-key-backup.asc

# 仅导出子密钥到工作机使用
gpg --export-secret-subkeys --armor <KEY_ID> > subkeys.asc

# 删除工作机上的私钥
gpg --delete-secret-key <KEY_ID>

# 重新导入仅含子密钥的版本
gpg --import subkeys.asc

执行后用 gpg --list-secret-keys 检查,主密钥行显示 sec# 表示主密钥已从工作机移除(# 表示存根)。


三、密钥备份与吊销证书

备份密钥

至少保存到两个独立的离线介质(U 盘、纸张打印等),存放于不同地点。

# 导出公钥
gpg --export --armor <KEY_ID> > public-key.asc

# 导出私钥(主密钥 + 子密钥,妥善保管)
gpg --export-secret-keys --armor <KEY_ID> > secret-key.asc

提前生成吊销证书

在密钥生成后立刻生成吊销证书,防止密钥泄漏后无法吊销。

gpg --output revoke-<KEY_ID>.asc --gen-revoke <KEY_ID>

将吊销证书与私钥一起妥善保存(但存放位置要分开)。一旦密钥泄漏,立即使用吊销证书:

gpg --import revoke-<KEY_ID>.asc
gpg --keyserver hkps://keys.openpgp.org --send-keys <KEY_ID>

四、密钥发布与密钥服务器

上传公钥

# 上传到主流密钥服务器
gpg --keyserver hkps://keys.openpgp.org --send-keys <KEY_ID>

# 也可上传到 Ubuntu 密钥服务器
gpg --keyserver hkps://keyserver.ubuntu.com --send-keys <KEY_ID>

推荐使用 keys.openpgp.org,它支持邮件验证,防止垃圾密钥污染。

导出指纹供他人验证

gpg --fingerprint <KEY_ID>

在个人主页、GitHub Profile 或名片上公布指纹,便于他人验证你的公钥真实性。


五、与 Git 集成签名提交

配置 Git 使用 GPG 签名

# 开启 commit 签名
git config --global commit.gpgsign true

# 指定使用的密钥(如有多个密钥)
git config --global user.signingkey <KEY_ID>

强制指定子密钥(重要)

如果希望 Git 使用特定子密钥而非主密钥,需要在 Key ID 后面加上叹号 !

git config --global user.signingkey <SUBKEY_ID>!

不加叹号时,GPG 会自动选择”合适”的密钥,可能并非你预期的那一个。
详细说明可参考:Git Signoff 指定 Subkey 的配置方法

跳过 GPG 签名的临时办法

在 CI/CD 等自动化环境中,如果没有 GPG Agent 可用,可临时跳过签名:

git commit --no-gpg-sign -m "提交消息"

六、智能卡 / 硬件密钥

将 GPG 私钥存储在硬件设备(如 YubiKey、CanoKey)中,可以防止私钥被直接导出,是目前最安全的使用方式。

将子密钥移入智能卡

gpg --edit-key <KEY_ID>
gpg> key 1    # 选中第一个子密钥
gpg> keytocard
# 按照提示选择存储槽(签名/加密/认证)
gpg> save

移入后子密钥本体从本地删除,只保留存根(stubs),使用时需要插入智能卡。

低成本替代方案

如果不想购买商业智能卡,可以使用 JavaCard + SmartPGP 方案自制 OpenPGP 卡,详见:基于SmartPGP低成本DIY OpenPGP卡


七、GPG Agent 配置

GPG Agent 负责缓存密码短语(passphrase),合理配置可以在安全与便利之间取得平衡。

配置缓存时间

编辑 ~/.gnupg/gpg-agent.conf

# 密码短语缓存时间(秒)
default-cache-ttl 3600
max-cache-ttl 86400

重启 Agent

gpgconf --kill gpg-agent
gpgconf --launch gpg-agent

使用 SSH 认证

将 GPG 认证子密钥用于 SSH 登录:

# 在 gpg-agent.conf 中启用 SSH 支持
echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf

# 将 GPG_AUTH_KEY_GRIP 添加到 sshcontrol
gpg --with-keygrip --list-secret-keys

# 将对应的 keygrip 写入 sshcontrol
echo "<KEYGRIP>" >> ~/.gnupg/sshcontrol

在 shell 配置文件中设置 SSH_AUTH_SOCK

export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)

八、日常安全注意事项

  1. 设置强密码短语:私钥必须加密保存,密码短语建议 20 字符以上,混合大小写、数字、符号。

  2. 定期检查密钥状态

    gpg --list-keys
    gpg --list-secret-keys
  3. 定期从密钥服务器刷新:获取他人密钥的吊销/更新状态:

    gpg --refresh-keys
  4. 不要在不可信环境中使用私钥:公用电脑、共享服务器上尽量避免导入私钥。

  5. 验证他人公钥的指纹:不要盲目信任从网络下载的公钥,通过带外方式(当面、电话、个人主页)核对指纹。

  6. 子密钥即将过期时提前续期:避免密钥突然过期导致验证失败。


九、常用命令速查

# 生成密钥
gpg --full-generate-key

# 列出密钥
gpg --list-keys                          # 公钥
gpg --list-secret-keys --keyid-format LONG  # 私钥(含长格式 Key ID)

# 导入/导出
gpg --import key.asc
gpg --export --armor <KEY_ID> > pub.asc
gpg --export-secret-keys --armor <KEY_ID> > sec.asc

# 加密与解密
gpg --encrypt --recipient <KEY_ID> file.txt
gpg --decrypt file.txt.gpg

# 签名与验证
gpg --sign file.txt           # 生成二进制签名文件
gpg --clearsign file.txt      # 生成可读的签名文本
gpg --detach-sign file.txt    # 生成分离签名
gpg --verify file.txt.sig file.txt

# 编辑密钥(添加子密钥、修改有效期等)
gpg --edit-key <KEY_ID>

# 吊销并上传
gpg --import revoke.asc
gpg --keyserver hkps://keys.openpgp.org --send-keys <KEY_ID>

# 重启 GPG Agent
gpgconf --kill gpg-agent

十、密钥过期处理

密钥过期后并不等于密钥作废,处理方式取决于你想继续使用还是彻底更换。

过期后的影响

操作 过期后的行为
用过期密钥加密 GPG 会警告并拒绝(需强制指定才能加密)
用过期密钥签名 GPG 会报错拒绝
验证过期密钥签名的历史签名 仍然有效,历史签名不受影响
解密用过期加密密钥加密的数据 仍然可以解密,过期只限制新操作

关键认知:过期的含义是”不再建议使用”,而非”历史数据无效”。历史签名和加密文件依然可以正常使用。

方案一:续期(推荐)

续期是最常见的处理方式,即延长现有密钥的有效期,无需更换密钥。

前提:需要取出离线保存的主密钥。

# 1. 导入主密钥(从离线备份)
gpg --import full-secret-key-backup.asc

# 2. 编辑密钥,修改有效期
gpg --edit-key <KEY_ID>

# 续期主密钥
gpg> expire
Key is valid for? (0)   # 输入新的有效期,如 1y 表示1年

# 续期子密钥(需逐个选中)
gpg> key 1              # 选中第一个子密钥
gpg> expire
gpg> key 1              # 取消选中
gpg> key 2              # 选中第二个子密钥
gpg> expire
gpg> save

# 3. 将续期后的公钥上传到密钥服务器
gpg --keyserver hkps://keys.openpgp.org --send-keys <KEY_ID>

# 4. 重新导出公钥分发给合作方(如果不使用密钥服务器)
gpg --export --armor <KEY_ID> > public-key-renewed.asc

# 5. 完成后,再次从工作机删除主密钥(保持离线原则)
gpg --delete-secret-key <KEY_ID>
gpg --import subkeys.asc   # 重新导入仅含子密钥的版本

续期后,他人只需刷新你的公钥即可看到新有效期:

# 他人刷新密钥
gpg --refresh-keys
# 或指定服务器
gpg --keyserver hkps://keys.openpgp.org --recv-keys <KEY_ID>

方案二:添加新子密钥替换旧子密钥

如果仅子密钥过期,可以保留旧子密钥(用于解密历史数据),再添加新子密钥投入使用:

gpg --edit-key <KEY_ID>
gpg> addkey       # 添加新的签名子密钥或加密子密钥
gpg> save

旧的加密子密钥过期后仍保留在密钥环中,GPG 依然可以用它解密之前接收的文件,只是不再接受用它加密新数据。

方案三:生成全新密钥(不推荐轻易使用)

仅在以下情况考虑重新生成:

  • 主密钥私钥已丢失,无法续期
  • 密钥算法过时,需要更换算法(如从 RSA 迁移到 Ed25519)
  • 密钥已泄漏,需要完全作废

生成新密钥后需要:

  1. 用旧密钥的吊销证书吊销旧密钥,并上传到密钥服务器
  2. 将新公钥分发给所有合作方并重新建立信任

子密钥在智能卡上的续期

如果子密钥已移入智能卡,续期流程略有不同:

# 1. 导入主密钥(智能卡只存储子密钥,主密钥仍需从备份导入)
gpg --import full-secret-key-backup.asc

# 2. 正常执行 expire 续期
gpg --edit-key <KEY_ID>
gpg> key 1
gpg> expire
gpg> save

# 3. 智能卡上的子密钥有效期已通过主密钥续期,无需重新写卡
# (智能卡存储的是私钥本体,有效期元数据在公钥中)

续期前的检查清单

  • 确认你还持有主密钥备份
  • 确认吊销证书已安全保存
  • 续期后重新导出并分发最新公钥
  • 续期完成后重新将主密钥从工作机删除
  • 更新 GitHub/GitLab 等平台上的 GPG 公钥(如有必要)

总结

GPG 的安全性高度依赖于正确的使用方式。核心原则是:

  • 主密钥离线保存,日常只使用子密钥
  • 提前备份私钥和吊销证书,并存放在不同介质
  • 有效期不要设置太长,定期续期比一次性设置永久更安全
  • 使用智能卡进一步保护私钥不被导出

遵循这些实践,GPG 能够成为你数字身份和数据安全的可靠基础。


文章作者: 2356
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 2356 !