# 签署工作

Git 虽然是密码级安全的,但它不是万无一失的。 如果你从因特网上的其他人那里拿取工作,并且 想要验证提交是不是真正地来自于可信来源,Git 提供了几种通过 GPG 来签署和验证工作的方式。

# GPG 介绍

首先,在开始签名之前你需要先配置 GPG 并安装个人密钥。

$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub   2048R/0A46826A 2014-06-04
uid                  Scott Chacon (Git signing key) <schacon@gmail.com>
sub   2048R/874529A9 2014-06-04
1
2
3
4
5
6

如果你还没有安装一个密钥,可以使用 gpg –gen-key 生成一个。

$ gpg --gen-key
gpg (GnuPG) 2.2.23-unknown; Copyright (C) 2020 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.

Note: Use "gpg --full-generate-key" for a full featured key generation dialog.

GnuPG needs to construct a user ID to identify your key.

Real name: zqzqzq		# 输入一个至少 5 位数的密匙 ID
Email address: 99299684@qq.com   # 你的邮箱
You selected this USER-ID:	# 下面是生成的 user-id
    "zqzqzq <99299684@qq.com>"

Change (N)ame, (E)mail, or (O)kay/(Q)uit? o  # 输入 o 开始生成
# 特别注意这里,会弹出一个对话框,叫你输入你密匙的 密码,我这里输入的是 12345678,会输入两次
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
# 上面是生成过程的说明,下面是生成好的密匙信息与存储路径
# 注意这里的 key 5EEB7501BA64A581,他可以用来代替 user id,签署的时候也是用的他
gpg: key 5EEB7501BA64A581 marked as ultimately trusted
gpg: directory '/c/Users/zq/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/c/Users/zq/.gnupg/openpgp-revocs.d/46AD35DEB41E02E793366B255EEB7501BA64A581.rev'
public and secret key created and signed.

pub   rsa3072 2020-11-05 [SC] [expires: 2022-11-05]
      46AD35DEB41E02E793366B255EEB7501BA64A581
uid                      zqzqzq <99299684@qq.com>
sub   rsa3072 2020-11-05 [E] [expires: 2022-11-05]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

一旦你有一个可以签署的私钥,可以通过设置 Git 的 user.signingkey 选项来签署。

# 再次查看
# 笔者这是新版的 git (v2.x)生成的,可能和上面书上讲解的信息不太一致
$ gpg --list-keys
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2022-11-05
/c/Users/zq/.gnupg/pubring.kbx
------------------------------
# 注意这里第二列的信息,不会显示所属的 user-id 的 hash 字符串
pub   rsa3072 2020-11-05 [SC] [expires: 2022-11-05]
      46AD35DEB41E02E793366B255EEB7501BA64A581
uid           [ultimate] zqzqzq <99299684@qq.com>
sub   rsa3072 2020-11-05 [E] [expires: 2022-11-05]

# 设置密匙
# 注意这里填写的是用户id 的 hash 字符串
git config --global user.signingkey 5EEB7501BA64A581
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

现在 Git 默认使用你的密钥来签署标签与提交。

有关 GPG 入门详细信息,请参考 《阮一峰的 GPG 入门教程》 (opens new window)

# 签署标签

如果已经设置好一个 GPG 私钥,可以使用它来签署新的标签。 所有需要做的只是使用 -s 代替 -a 即可:

$ git tag -s v1.5 -m 'my signed 1.5 tag'

You need a passphrase to unlock the secret key for
user: "Ben Straub <ben@straub.cc>"
2048-bit RSA key, ID 800430EB, created 2014-05-04

# 注意: 这一步在新版 git bash 中,执行标签签署命令,同样会弹出一个对话框,让你输入你的密匙密码
1
2
3
4
5
6
7

如果在那个标签上运行 git show,会看到你的 GPG 签名附属在后面:

$ git show v1.5
tag v1.5
Tagger: Ben Straub <ben@straub.cc>
Date:   Sat May 3 20:29:41 2014 -0700

my signed 1.5 tag
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAABAgAGBQJTZbQlAAoJEF0+sviABDDrZbQH/09PfE51KPVPlanr6q1v4/Ut
LQxfojUWiLQdg2ESJItkcuweYg+kc3HCyFejeDIBw9dpXt00rY26p05qrpnG+85b
hM1/PswpPLuBSr+oCIDj5GMC2r2iEKsfv2fJbNW8iWAXVLoWZRF8B0MfqX/YTMbm
ecorc4iXzQu7tupRihslbNkfvfciMnSDeSvzCpWAHl7h8Wj6hhqePmLm9lAYqnKp
8S5B/1SSQuEAjRZgI4IexpZoeKGVDptPHxLLS38fozsyi0QyDyzEgJxcJQVMXxVi
RUysgqjcpT8+iQM1PblGfHR4XAhuOqN5Fx06PSaFZhqvWFezJ28/CLyX5q+oIVk=
=EFTF
-----END PGP SIGNATURE-----

commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 验证标签

要验证一个签署的标签,可以运行 git tag -v [tag-name]。 这个命令使用 GPG 来验证签名。 为了验证能正常工作,签署者的公钥需要在你的钥匙链中

$ git tag -v v1.4.2.1
object 883653babd8ee7ea23e6a5c392bb739348b1eb61
type commit
tag v1.4.2.1
tagger Junio C Hamano <junkio@cox.net> 1158138501 -0700

GIT 1.4.2.1

Minor fixes since 1.4.2, including git-mv and git-http with alternates.
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Good signature from "Junio C Hamano <junkio@cox.net>"
gpg:                 aka "[jpeg image of size 1513]"
Primary key fingerprint: 3565 2A26 2040 E066 C9A7  4A7D C0C6 D9A4 F311 9B9A
1
2
3
4
5
6
7
8
9
10
11
12
13

果没有签署者的公钥,那么你将会得到类似下面的东西:

gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Can't check signature: public key not found
error: could not verify the tag 'v1.4.2.1'
1
2
3

# 签署提交

在最新版本的 Git 中(v1.7.9 及以上),也可以签署个人提交。 如果相对于标签而言你对直接签署到提交更感兴趣的话,所有要做的只是增加一个 -Sgit commit 命令。

$ git commit -a -S -m 'signed commit'

You need a passphrase to unlock the secret key for
user: "Scott Chacon (Git signing key) <schacon@gmail.com>"
2048-bit RSA key, ID 0A46826A, created 2014-06-04

[master 5c3386c] signed commit
 4 files changed, 4 insertions(+), 24 deletions(-)
 rewrite Rakefile (100%)
 create mode 100644 lib/git.rb
1
2
3
4
5
6
7
8
9
10

git log 也有一个 –show-signature 选项来查看及验证这些签名。

$ git log --show-signature -1
commit 5c3386cf54bba0a33a32da706aa52bc0155503c2
gpg: Signature made Wed Jun  4 19:49:17 2014 PDT using RSA key ID 0A46826A
gpg: Good signature from "Scott Chacon (Git signing key) <schacon@gmail.com>"
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Jun 4 19:49:17 2014 -0700

    signed commit
1
2
3
4
5
6
7
8

另外,也可以配置 git log 来验证任何找到的签名并将它们以 %G? 格式列在输出中。

$ git log --pretty="format:%h %G? %aN  %s"

5c3386c G Scott Chacon  signed commit
ca82a6d N Scott Chacon  changed the version number
085bb3b N Scott Chacon  removed unnecessary test code
a11bef0 N Scott Chacon  first commit
1
2
3
4
5
6

这里我们可以看到只有最后一次提交是签署并有效的,而之前的提交都不是。

在 Git 1.8.3 及以后的版本中,git mergegit pull 可以使用 –verify-signatures 选项来检查并拒绝没有携带可信 GPG 签名的提交。

如果使用这个选项来合并一个包含未签名或有效的提交的分支时,合并不会生效。

$ git merge --verify-signatures non-verify
fatal: Commit ab06180 does not have a GPG signature.
1
2

如果合并包含的只有有效的签名的提交,合并命令会提示所有的签名它已经检查过了然后会继续向前。

$ git merge --verify-signatures signed-branch
Commit 13ad65e has a good GPG signature by Scott Chacon (Git signing key) <schacon@gmail.com>
Updating 5c3386c..13ad65e
Fast-forward
 README | 2 ++
 1 file changed, 2 insertions(+)
1
2
3
4
5
6

也可以给 git merge 命令附加 -S 选项来签署自己生成的合并提交。 下面的例子演示了验证将要合并的分支的每一个提交都是签名的并且签署最后生成的合并提交。

$ git merge --verify-signatures -S  signed-branch
Commit 13ad65e has a good GPG signature by Scott Chacon (Git signing key) <schacon@gmail.com>

You need a passphrase to unlock the secret key for
user: "Scott Chacon (Git signing key) <schacon@gmail.com>"
2048-bit RSA key, ID 0A46826A, created 2014-06-04

Merge made by the 'recursive' strategy.
 README | 2 ++
 1 file changed, 2 insertions(+)
1
2
3
4
5
6
7
8
9
10

# 每个人必须签署

签署标签与提交很棒,但是如果决定在正常的工作流程中使用它,你必须确保团队中的每一个人都理解如何这样做。 如果没有,你将会花费大量时间帮助其他人找出并用签名的版本重写提交。 在采用签署成为标准工作流程的一部分前,确保你完全理解 GPG 及签署带来的好处。