问题描述
在使用 GPG 签名 Git 提交时,如果配置了子密钥(Subkey),但 Git 仍然使用主密钥或其他子密钥进行签名。如何解决这个问题?
解决方案
查看密钥结构
首先查看你的 GPG 密钥结构:
gpg --list-secret-keys --keyid-format LONG
输出示例:
sec rsa4096/0x1234567890ABCDEF 2024-01-01 [SCA]
ABCD1234ABCD1234ABCD1234ABCD1234ABCD1234
uid [ultimate] Your Name <your.email@example.com>
ssb rsa4096/0xFEDCBA0987654321 2024-01-01 [SEA]
其中 ssb 行显示的就是子密钥,记录其 Key ID(如 0xFEDCBA0987654321)。
强制指定子密钥
关键步骤:在密钥 ID 后添加叹号 ! 来强制 Git 使用指定的子密钥。
# 配置 Git 使用指定的子密钥(注意叹号)
git config --global user.signingkey 0xFEDCBA0987654321!
# 如果 shell 解释叹号,需要使用引号或转义
git config --global user.signingkey '0xFEDCBA0987654321!'
# 或者
git config --global user.signingkey 0xFEDCBA0987654321\!
为什么需要叹号?
根据 GitHub 官方文档和 GPG 的工作原理:
GPG 的自动选择机制:当不指定叹号时,GPG 会自动选择一个”合适”的密钥进行签名。通常情况下,GPG 会优先使用最新创建的子密钥,如果没有合适的子密钥,则使用主密钥。
叹号的作用:叹号
!告诉 GPG “精确使用这个密钥,不要自动选择”。这可以确保 Git 始终使用你明确指定的子密钥。实际场景:假设你有以下密钥结构:
sec 4096R/0x1234567890ABCDEF [SCA] (主密钥) ssb 4096R/0xAAAAAAAAAAAAAAAA [SEA] (子密钥 1,旧) ssb 4096R/0xFEDCBA0987654321 [SEA] (子密钥 2,新)如果你想使用旧的子密钥 1(
0xAAAAAAAAAAAAAAAA),但不加叹号,GPG 很可能会使用新的子密钥 2。只有添加叹号才能强制使用指定的子密钥。
验证配置
# 查看 Git 配置
git config --global --get user.signingkey
# 测试提交并查看使用的密钥
git commit -S -m "Test commit"
git log --show-signature -1
在日志中,你应该看到类似这样的输出:
commit abc123...
gpg: Signature made Wed Mar 18 10:00:00 2026 CST
gpg: using RSA key FEDCBA0987654321
显示的密钥 ID 应该与你配置的子密钥一致。
常见问题
Git 使用了错误的密钥
症状:尽管配置了子密钥,Git 仍然使用主密钥或其他密钥签名。
解决方案:
- 使用叹号强制指定:
git config --global user.signingkey 0xFEDCBA0987654321!
- 清除缓存配置:
git config --global --unset user.signingkey
git config --global user.signingkey 0xFEDCBA0987654321!
- 检查 GPG Agent 缓存:
gpgconf --kill gpg-agent
GPG 提示”secret key not found”
症状:提交时 GPG 找不到指定的密钥。
解决方案:
- 确认子密钥存在且可用:
gpg --list-secret-keys --keyid-format LONG
- 如果子密钥在智能卡上,确保卡片已插入且被识别:
gpg --card-status
总结
配置 Git 使用指定的 GPG 子密钥的关键是:在密钥 ID 后添加叹号 !。
git config --global user.signingkey 0xSUBKEY_ID!
这样可以强制 Git 使用你指定的子密钥,而不是让 GPG 自动选择。