跳转到内容

GPG 签名 Git Commit 完全指南 | 提升代码安全性与身份验证

GPG Signing

在协作开发中,确保代码提交的真实性和完整性至关重要。GPG(GNU Privacy Guard)签名为 Git commit 提供了强大的身份验证机制,防止他人伪造你的提交记录。通过 GPG 签名,GitHub、GitLab 等平台会显示绿色的 "Verified" 标识,证明该提交确实来自你。本文将详细介绍从零开始的完整配置流程。

为什么需要 GPG 签名?

核心优势

优势说明
🔒 身份验证证明提交确实来自你,防止身份伪造
完整性保护确保提交内容未被篡改
🟢 可信标识GitHub/GitLab 显示 "Verified" 徽章
🛡️ 安全防护防止中间人攻击和提交注入
📋 审计追踪提供不可抵赖的提交记录

适用场景

  • 开源项目贡献:证明你的贡献真实性
  • 企业团队协作:确保内部代码来源可信
  • 个人项目管理:提升代码库的专业度
  • 合规要求:满足某些行业的安全审计需求

GPG 安装

bash
brew install gpg
# or
brew cask install keybase

不同平台安装方法

macOS

bash
# 方法 1:Homebrew(推荐)
brew install gnupg

# 方法 2:MacGPG2(图形界面)
# 下载:https://gpgtools.org/

# 验证安装
gpg --version

Linux

bash
# Ubuntu/Debian
sudo apt-get install gnupg2

# CentOS/RHEL
sudo yum install gnupg2

# Arch Linux
sudo pacman -S gnupg

# 验证安装
gpg2 --version

Windows

bash
# 方法 1:Gpg4win(推荐)
# 下载:https://gpg4win.org/

# 方法 2:Chocolatey
choco install gpg4win

# 方法 3:Scoop
scoop install gpg

# 验证安装
gpg --version

常用命令

bash
$ gpg --gen-key
$ gpg --list-keys
$ gpg --list-secret-keys
$ gpg2 --list-keys
$ gpg2 --list-secret-keys

$ gpg2 --keyserver hkp://pool.sks-keyservers.net --send-keys C6EED57A
$ gpg2 --keyserver hkp://pool.sks-keyservers.net --recv-keys C6EED57A

命令详解

命令功能说明
gpg --gen-key生成新密钥对交互式向导
gpg --list-keys列出公钥查看所有公钥
gpg --list-secret-keys列出私钥查看可用于签名的密钥
gpg2 --list-keys列出公钥(v2)GnuPG 2.x 版本
--send-keys上传公钥到服务器分享公钥
--recv-keys从服务器下载公钥获取他人公钥

生成 GPG key

bash
gpg --gen-key

具体步骤如下

bash
$ gpg --gen-key
gpg (GnuPG) 2.2.15; Copyright (C) 2019 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 --full-generate-key" 以获得一个功能完整的密钥产生对话框。

GnuPG 需要构建用户标识以辨认您的密钥。

真实姓名: Theo
电子邮件地址: fanxiaobin422@gmail.com
您选定了此用户标识:
    "Theo <fanxiaobin422@gmail.com>"

更改姓名(N)、注释(C)、电子邮件地址(E)或确定(O)/退出(Q)? o
我们需要生成大量的随机字节。在质数生成期间做些其他操作(敲打键盘
、移动鼠标、读写硬盘之类的)将会是一个不错的主意;这会让随机数
发生器有更好的机会获得足够的熵。
gpg: 密钥 8284896418BCC645 被标记为绝对信任
gpg: 目录'/Users/xiexianbin/.gnupg/openpgp-revocs.d'已创建
gpg: 吊销证书已被存储为'/Users/xiexianbin/.gnupg/openpgp-revocs.d/11518af49eaa27d86ea01b5c901487ea218aeb1a.rev'
公钥和私钥已经生成并被签名。

pub   rsa2048 2019-05-16 [SC] [有效至:2021-05-15]
      11518af49eaa27d86ea01b5c901487ea218aeb1a
uid                      xiexianbin <me@xiexianbin.cn>
sub   rsa2048 2019-05-16 [E] [有效至:2021-05-15]

详细配置说明

步骤 1:选择密钥类型

bash
# 使用完整生成模式(推荐)
gpg --full-generate-key

# 选项说明:
# (1) RSA and RSA (default) - 推荐
# (2) DSA and Elgamal
# (3) DSA (sign only)
# (4) RSA (sign only)
# (14) Existing key from card

步骤 2:选择密钥长度

bash
# 推荐配置:
RSA 密钥长度应在 1024 位与 4096 位之间。
您想要用多大的密钥尺寸?(2048) 4096

# 建议:
# - 个人使用:2048 位(足够安全)
# - 企业/高安全:4096 位(更安全但稍慢)

步骤 3:设置有效期

bash
# 选项:
# 0 = 密钥永不过期
# <n>  = 密钥在 n 天后过期
# <n>w = 密钥在 n 周后过期
# <n>m = 密钥在 n 月后过期
# <n>y = 密钥在 n 年后过期

# 推荐:
# - 个人项目:0(永不过期)
# - 企业环境:1y 或 2y(定期轮换)

步骤 4:填写用户信息

bash
真实姓名: Your Name
电子邮件地址: your.email@example.com
注释: GitHub Key(可选,用于区分多个密钥)

# 重要提示:
# ⚠️ 邮箱必须与 Git 配置的邮箱一致!
# ⚠️ 确认信息无误后再继续

步骤 5:设置密码短语

bash
# 输入保护私钥的密码
# 建议使用强密码(12+ 字符,包含大小写、数字、符号)
# 可以使用密码管理器保存

# 提示:
# - 密码不会显示在屏幕上
# - 需要输入两次确认
# - 忘记密码将无法恢复私钥!

步骤 6:生成熵

bash
# 系统需要收集随机数据来生成密钥
# 在此期间可以:
# - 敲击键盘
# - 移动鼠标
# - 打开/关闭程序
# - 读写文件

# 等待直到显示:
# "公钥和私钥已经生成并被签名"

查看 GPG key

bash
gpg --list-keys
gpg --list-secret-keys

示例

bash
$ gpg --list-keys
/Users/xiexianbin/.gnupg/pubring.kbx
------------------------------------
pub   rsa2048 2019-05-16 [SC] [有效至:2021-05-15]
      11518af49eaa27d86ea01b5c901487ea218aeb1a
uid           [ 绝对 ] xiexianbin <me@xiexianbin.cn>
sub   rsa2048 2019-05-16 [E] [有效至:2021-05-15]

输出解读

pub   rsa2048 2019-05-16 [SC] [有效至:2021-05-15]
      11518af49eaa27d86ea01b5c901487ea218aeb1a
uid           [ 绝对 ] xiexianbin <me@xiexianbin.cn>
sub   rsa2048 2019-05-16 [E] [有效至:2021-05-15]

# 字段说明:
# pub  - 公钥(Public Key)
# sub  - 私钥(Subkey,用于加密)
# rsa2048 - 算法和密钥长度
# 2019-05-16 - 创建日期
# [SC] - 用途:S=签名(Sign), C=认证(Certify)
# [E] - 用途:加密(Encrypt)
# 11518af4... - 密钥指纹(Key ID)
# [ 绝对 ] - 信任级别

密钥

  • pub:公钥
  • sub:私钥
  • 11518af49eaa27d86ea01b5c901487ea218aeb1a 是 pub GPG key ID

导出公钥

bash
gpg --armor --export <pub GPG key ID>

示例:

bash
gpg --armor --export 11518af49eaa27d86ea01b5c901487ea218aeb1a

GPG key 格式

bash
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFz...(Base64 编码的密钥数据)
...
-----END PGP PUBLIC KEY BLOCK-----

导出到文件:

bash
# 导出到文件
gpg --armor --export your@email.com > gpg_public_key.asc

# 或直接复制终端输出
gpg --armor --export your@email.com

删除GPG key

bash
gpg --delete-keys <pub GPG key ID>
gpg --delete-secret-keys <pub GPG key ID>

完整删除流程:

bash
# 1. 先删除私钥(必须)
gpg --delete-secret-keys 11518af49eaa27d86ea01b5c901487ea218aeb1a

# 2. 再删除公钥
gpg --delete-keys 11518af49eaa27d86ea01b5c901487ea218aeb1a

# 3. 验证删除
gpg --list-keys
gpg --list-secret-keys

⚠️ 警告:删除密钥后无法恢复,请谨慎操作!

Github 配置 GPG

配置 GPG 公钥到仓库

Github Setting -> SSH and GPG keys -> New GPG Key 导入即可

详细步骤

步骤 1:获取公钥

bash
# 方法 1:导出到剪贴板(macOS)
gpg --armor --export your@email.com | pbcopy

# 方法 2:导出到文件
gpg --armor --export your@email.com > ~/gpg_key.asc
# 然后用文本编辑器打开复制

# 方法 3:直接查看
gpg --armor --export your@email.com
# 选中并复制全部内容(包括 BEGIN 和 END 行)

步骤 2:添加到 GitHub

  1. 登录 GitHub
  2. 点击右上角头像 →【Settings】
  3. 左侧菜单选择【SSH and GPG keys】
  4. 点击【New GPG key】
  5. 粘贴公钥内容
  6. 点击【Add GPG key】
  7. 输入 GitHub 密码确认

步骤 3:验证添加

  • 公钥会显示在列表中
  • 显示创建日期和密钥 ID
  • 状态为活跃

本地代码仓库启用GPG Sign

通过 gpg --list-keys 查看 pub GPG key ID,后设置 git签名 时用的 key

全局设置

bash
# 配置已经生成的GPG Key ID
git config --global user.signingkey <pub GPG key ID>
# 配置启用GPG签名
git config --global commit.gpgsign true

示例:

bash
git config --global user.signingkey 11518af49eaa27d86ea01b5c901487ea218aeb1a
git config --global commit.gpgsign true

指定仓库设置,需要进入代码目录:

bash
# 配置已经生成的GPG Key ID
git config --local user.signingkey <pub GPG key ID>
# 配置启用GPG签名
git config --local commit.gpgsign true

验证配置:

bash
# 查看全局配置
git config --global --list | grep gpg

# 查看当前仓库配置
git config --local --list | grep gpg

# 预期输出:
# user.signingkey=11518af49eaa27d86ea01b5c901487ea218aeb1a
# commit.gpgsign=true

配置说明:

配置项作用推荐
user.signingkey指定用于签名的密钥 ID必填
commit.gpgsign自动为所有 commit 签名推荐 true
tag.gpgSign自动为 tag 签名可选
gpg.program指定 GPG 程序路径默认即可

重启 gpg-agent

bash
gpgconf –kill gpg-agent

为什么需要重启?

  • 清除缓存的密钥信息
  • 重新加载配置
  • 解决签名失败问题

其他相关命令:

bash
# 查看 gpg-agent 状态
gpgconf --list-dirs

# 重启特定组件
gpgconf --kill gpg-agent
gpgconf --launch gpg-agent

# 查看日志
tail -f ~/.gnupg/S.gpg-agent

上述示例

bash
~ xiexianbin$ git config --global user.signingkey 11518af49eaa27d86ea01b5c901487ea218aeb1a
~ xiexianbin$ git config --global commit.gpgsign true
~ xiexianbin$ gpgconf –kill gpg-agent
gpg:OpenPGP:/usr/local/Cellar/gnupg/2.2.15/bin/gpg
gpg-agent:私钥:/usr/local/Cellar/gnupg/2.2.15/bin/gpg-agent
scdaemon:智能卡:/usr/local/Cellar/gnupg/2.2.15/libexec/scdaemon
gpgsm:S/MIME:/usr/local/Cellar/gnupg/2.2.15/bin/gpgsm
dirmngr:网络:/usr/local/Cellar/gnupg/2.2.15/bin/dirmngr
pinentry:密码条目:/usr/local/opt/pinentry/bin/pinentry
~ xiexianbin$

git 使用

提交

bash
git commit -am "feature: something"
git push origin develop

然后我们可以在 git 中看到 Verified 的标识。

如果不设置 git config --global commit.gpgsign true,提交的时候加上一个 -S 参数就可以为提交签名:

bash
git commit -S -m `your commit message`

签名提交流程

自动签名(推荐):

bash
# 已配置 commit.gpgsign=true
git add .
git commit -m "feat: add new feature"
# 自动弹出密码输入框
# 提交成功后显示 Verified 标识

手动签名:

bash
# 未配置自动签名时
git commit -S -m "feat: add new feature"

# 指定密钥
git commit -S<key-id> -m "feat: add new feature"

批量提交签名:

bash
# 修改多个文件后
git add file1.js file2.js file3.js
git commit -S -m "refactor: update multiple files"

提交 tag 时签名

bash
git tag -s ...

详细用法:

bash
# 创建带签名的 tag
git tag -s v1.0.0 -m "Release version 1.0.0"

# 签署已有的 tag
git tag -s v1.0.0 HEAD -f

# 验证 tag 签名
git tag -v v1.0.0

# 推送签名 tag
git push origin v1.0.0

查看日志

bash
git log --show-signature -1

更多查看方式:

bash
# 查看最近一次提交的签名
git log --show-signature -1

# 查看所有提交的签名状态
git log --pretty=format:"%h %G? %aN %s"

# 仅显示已验证的提交
git log --pretty=format:"%h %G? %aN %s" | grep "^.* G .*"

# 图形化查看
git log --graph --oneline --show-signature

签名状态标识:

G -  good signature(良好签名)
B -  bad signature(错误签名)
U -  good signature, unknown validity(未知有效性)
X -  good signature, expired key(密钥已过期)
R -  good signature, revoked key(密钥已撤销)
E -  cannot check (no public key)(无公钥)
N -  no signature(无签名)

高级配置

1. 配置 GPG Agent

编辑配置文件:

bash
# macOS/Linux
nano ~/.gnupg/gpg-agent.conf

# 添加以下内容
default-cache-ttl 600
max-cache-ttl 7200
allow-preset-passphrase

配置说明:

default-cache-ttl 600     # 密码缓存时间(秒)
max-cache-ttl 7200        # 最大缓存时间(秒)
allow-preset-passphrase   # 允许预设密码(脚本自动化)

重启生效:

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

2. 配置 Pinentry

macOS 用户:

bash
# 使用 macOS 原生密码输入框
echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf

# 安装 pinentry-mac
brew install pinentry-mac

Linux 用户:

bash
# 使用 GTK 界面
echo "pinentry-program /usr/bin/pinentry-gtk-2" >> ~/.gnupg/gpg-agent.conf

# 或使用命令行
echo "pinentry-program /usr/bin/pinentry-curses" >> ~/.gnupg/gpg-agent.conf

Windows 用户:

bash
# Gpg4win 自带 pinentry,通常无需配置

3. 多密钥管理

列出所有密钥:

bash
gpg --list-keys --keyid-format long

切换默认密钥:

bash
# 查看当前配置
git config --global user.signingkey

# 修改为其他密钥
git config --global user.signingkey NEW_KEY_ID

为不同项目使用不同密钥:

bash
# 全局使用密钥 A
git config --global user.signingkey KEY_A

# 特定项目使用密钥 B
cd /path/to/project
git config --local user.signingkey KEY_B

常见问题

bash
$ GIT_TRACE=1 git commit -m "xxx"
22:03:55.417251 git.c:455               trace: built-in: git commit -m 'feature: jwt support'
22:03:55.424110 run-command.c:667       trace: run_command: gpg --status-fd=2 -bsau B5A1B728A2FD170FE0E6C4E2D6B71988603A67D2
error: gpg failed to sign the data
fatal: failed to write commit object

error: gpg 无法为数据签名
fatal: 写提交对象失败

解决办法

bash
echo export GPG_TTY=$(tty) >> ~/.bash_profile
export GPG_TTY=$(tty)

重新执行,发现会弹出一个密码输入界面。

如果没有解决,执行如下命令

bash
$ gpg --status-fd=2 -bsau B5A1B728A2FD170FE0E6C4E2D6B71988603A67D2

# 如果卡住,执行
killall gpg-agent

常见问题完整解决方案

问题 1:gpg failed to sign the data

症状:

error: gpg failed to sign the data
fatal: failed to write commit object

解决方案:

bash
# 方案 1:设置 GPG_TTY(最常见)
echo 'export GPG_TTY=$(tty)' >> ~/.bashrc  # Linux
echo 'export GPG_TTY=$(tty)' >> ~/.zshrc   # macOS
source ~/.zshrc

# 方案 2:重启 gpg-agent
gpgconf --kill gpg-agent
gpgconf --launch gpg-agent

# 方案 3:检查密钥是否存在
gpg --list-secret-keys

# 方案 4:验证 Git 配置
git config --global user.signingkey
git config --global commit.gpgsign

# 方案 5:测试手动签名
echo "test" | gpg --clearsign

问题 2:No secret key

症状:

error: No secret key

解决方案:

bash
# 1. 检查私钥是否存在
gpg --list-secret-keys

# 2. 如果没有,导入私钥备份
gpg --import private_key_backup.asc

# 3. 或者重新生成密钥
gpg --full-generate-key

# 4. 更新 Git 配置
git config --global user.signingkey NEW_KEY_ID

问题 3:Inappropriate ioctl for device

症状:

error: Inappropriate ioctl for device

解决方案:

bash
# macOS
brew install pinentry-mac
echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent

# Linux
sudo apt-get install pinentry-curses
echo "pinentry-program /usr/bin/pinentry-curses" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent

问题 4:密钥已过期

症状:

key has expired

解决方案:

bash
# 1. 延长密钥有效期
gpg --edit-key YOUR_KEY_ID
> expire
> save

# 2. 上传更新后的公钥
gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID

# 3. 更新 GitHub 上的公钥
# 重新导出并替换 GitHub 上的公钥
gpg --armor --export YOUR_EMAIL | pbcopy

问题 5:Unverified 标识

症状:

  • Commit 显示 "Unverified" 而非 "Verified"

原因和解决:

bash
# 原因 1:邮箱不匹配
# 检查 Git 邮箱
git config user.email
# 检查 GPG 密钥邮箱
gpg --list-keys
# 确保两者一致

# 原因 2:公钥未上传到 GitHub
# 重新导出并添加到 GitHub
gpg --armor --export your@email.com

# 原因 3:使用了错误的密钥
# 检查当前使用的密钥
git config user.signingkey
# 确保与 GitHub 上的公钥对应

问题 6:密码每次都要输入

解决方案:

bash
# 配置密码缓存
cat >> ~/.gnupg/gpg-agent.conf << EOF
default-cache-ttl 3600
max-cache-ttl 86400
EOF

# 重启 agent
gpgconf --kill gpg-agent

# macOS 用户使用 Keychain
brew install pinentry-mac
echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf

最佳实践

1. 密钥备份

bash
# 导出私钥(重要!)
gpg --export-secret-keys --armor your@email.com > backup_private_key.asc

# 导出公钥
gpg --export --armor your@email.com > backup_public_key.asc

# 导出吊销证书
gpg --output revoke_cert.asc --gen-revoke your@email.com

# 安全存储
# - 保存到加密的 USB 驱动器
# - 打印纸质备份
# - 存储在密码管理器中

2. 密钥轮换

bash
# 建议轮换周期:
# - 个人使用:2-3 年
# - 企业环境:1 年
# - 高安全需求:6 个月

# 轮换步骤:
# 1. 生成新密钥
gpg --full-generate-key

# 2. 更新 Git 配置
git config --global user.signingkey NEW_KEY_ID

# 3. 上传新公钥到 GitHub
# 4. 保留旧密钥用于验证历史提交
# 5. 适时吊销旧密钥
gpg --edit-key OLD_KEY_ID
> revkey
> save

3. 安全建议

bash
# ✅ 应该做的:
# - 使用强密码保护私钥
# - 定期备份密钥
# - 使用 4096 位密钥(高安全场景)
# - 保持 GPG 软件更新
# - 验证他人公钥指纹

# ❌ 不应该做的:
# - 不要分享私钥
# - 不要在公共场合输入密码
# - 不要忽略密钥过期警告
# - 不要使用过时的 GPG 版本
# - 不要在不信任的机器上使用私钥

总结

GPG 签名为 Git 提交提供了强大的安全保障:

  1. 身份验证:证明提交的真实性
  2. 完整性保护:防止内容篡改
  3. 可信标识:GitHub "Verified" 徽章
  4. 专业形象:提升代码库可信度
  5. 审计追踪:不可抵赖的记录

关键步骤回顾:

bash
1. 安装 GPG
2. 生成密钥对
3. 导出公钥并添加到 GitHub
4. 配置 Git 使用 GPG 签名
5. 测试提交并验证

下一步学习:

开始使用 GPG 签名,让你的代码提交更加安全可靠!🔐✨