在 Github 上使用 GPG 的全过程
起因
其实在很早之前 Github 就已经充分支持 GPG 密钥了,而在我之前使用 Github 的两年时间内,竟对此一无所知,实在有些“没见过世面”。直至近日,在一次偶然查看仓库的 commit 历史中,发现某些 commit 有一个不同寻常的绿色标记(Verified),不仅美观(?)而且看上去舒心,如图所示:
点击这个标记,得知这一次 commit 是经过签名验证的(signed with a verified signature),因此,我便开始研究如何利用 GPG 对自己的每次 commit 进行签名验证。
什么是 GPG
GnuPG is a complete and free implementation of the OpenPGP standard as defined by RFC4880(also known as PGP). GnuPG allows you to encrypt and sign your data and communications; it features a versatile key management system, along with access modules for all kinds of public key directories. GnuPG, also known as GPG, is a command line tool with features for easy integration with other applications.
以上是从 GPG 网站 上摘取的部分简介,总的来说,GPG 的功能十分丰富,然而我这次主要是用它来对 Git 中的 commit 进行签名验证,所以需要做的事情也不算太复杂:
- 生成自己的 GPG 密钥
- 关联 GPG 公钥与 Github 账户
- 设置利用 GPG 私钥对 commit 进行签名
- 可选步骤:信任 Github 的 GPG 密钥
过程
安装 GPG
由于我的目的是在 Git 中使用 GPG,而 Windows 版本的 Git 发行包中,已经包含了可用的 GPG 命令行。判断方法也很简单,打开 Git Bash,输入gpg --version
,可以看到类似的 GPG 版本信息:
$ gpg --version gpg (GnuPG) 2.2.16-unknown libgcrypt 1.8.4 Copyright (C) 2019 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Home: /c/Users/---/.gnupg Supported algorithms: Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, CAMELLIA128, CAMELLIA192, CAMELLIA256 Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224 Compression: Uncompressed, ZIP, ZLIB, BZIP2
不过需要说明的是,如果所安装的 Git 版本比较久远(比如我一开始所用的 Git 发行包是 2017 年的),那么很可能其包含的 GPG 版本过低,影响后续的操作,所以建议直接更新 Git 发行包至最新版本。
生成自己的 GPG 密钥
打开 Git Bash,运行gpg --full-generate-key
,根据提示,输入相应的个人信息(需要注意的是邮箱必须要使用在 Github 中验证过的邮箱)、自定义密钥参数、设置私钥密码等等,即可生成自己的 GPG 密钥。(补充说明,使用gpg --gen-key
亦可生成密钥,但是会略去自定义密钥参数的步骤,对于一般场合的使用倒也问题不大。)
输出结果的末尾大致如下:
gpg: key DC3DB5873563E6B2 marked as ultimately trusted gpg: revocation certificate stored as '/c/Users/---/.gnupg/openpgp-revocs.d/1BA074F113915706D141348CDC3DB5873563E6B2.rev' public and secret key created and signed. pub rsa2048 2019-08-04 [SC] [expires: 2021-08-03] 1BA074F113915706D141348CDC3DB5873563E6B2 uid fortest <[email protected]> sub rsa2048 2019-08-04 [E] [expires: 2021-08-03]
需要记下的,是上述输出信息中的密钥 ID:1BA074F113915706D141348CDC3DB5873563E6B2
或者DC3DB5873563E6B2
,后者是前者的简短形式。
当然,如果没有及时将其记下也不要紧,可以运行gpg --list-keys
,列出本地存储的所有 GPG 密钥信息,大致如下:
$ gpg --list-keys # some output is omitted here pub rsa2048 2019-08-04 [SC] [expires: 2021-08-03] 1BA074F113915706D141348CDC3DB5873563E6B2 uid [ultimate] fortest <[email protected]> sub rsa2048 2019-08-04 [E] [expires: 2021-08-03]
稍微解读一下这些结果:
pub
其后的是该密钥的公钥特征,包括了密钥的参数(加密算法是 rsa,长度为 2048,生成于 2019-08-04,用途是 Signing 和 Certificating,一年之后过期)以及密钥的 ID。uid
其后的是生成密钥时所输入的个人信息。sub
其后的则是该密钥的子密钥特征,格式和公钥部分大致相同(E 表示用途是 Encrypting)。
关联 GPG 公钥与 Github 账户
还记得在上一步中记下的密钥 ID 吗?现在,我们需要根据这个 ID 来导出对应 GPG 密钥的公钥字符串。继续在 Git Bash 中,运行命令gpg --armor --export {key_id}
:
$ gpg --armor --export 1BA074F113915706D141348CDC3DB5873563E6B2 -----BEGIN PGP PUBLIC KEY BLOCK----- mQENBF1GT3wBCADC9Hb3HtDc69XzYlohVKvdL1KnK0FslJISRuF6S0sdoOiWo2wJ OiYVplWguTSkrMytjnMsoysZVolkYluY1wk67NT8YuYfnu6LSuF/doihrRldnKmz 9NZWw+15MLnENKsWCtwNwcCGDeZNJACyyUMYk7nJeIiM72k3/rnsyEpHqB25W/Zf 1VBkwf/ShePZ2W+rUktJ8j1TZuxe2bQpJdHQ9EKWG50D8O3xk+N+xEg4pcXLMfwT vnVpf2wINGLA6+3ypVMDipC0fgAnINBrrjiKsq2Sskv0O73D3sZlkOi0jgAhx+21 5dI2xHbcs3DrcZbWAF1xEA8wGsoyYQWoSCBrABEBAAG0F2ZvcnRlc3QgPHRlc3RA dGVzdC5jb20+iQFUBBMBCAA+FiEEG6B08RORVwbRQTSM3D21hzVj5rIFAl1GT3wC GwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ3D21hzVj5rJ3BQgA nUusNKaf8SIWq1w4ZR6CKhZP+kz+5kOEBs3+qIXJV++9nbjs4jnqOnXJUUdpLS9E HGYnd6XSeyqWmBAuFCcmld4VGIajYxgDbF11/ql5Gnbu26/jV7hnrBBK6Xn/6oV9 bBmLoT9xget5xFC6g2VE0EvneRqacUgMBCkvrMzcVnHmpkSOpjfXRAItnyK/bhia 8k/+5URO8v7Ao2+QO0zk8XzgGc5B8H/yItzDiKe7gpzdUyCviG8m/tkDUURzloY4 09wCmQWWzerbBHJT4RdpPqdTEtC6f4jTuT32zp5NtLpJ740WmSJly/8nAJ/0x3Vf pVkzhsg9gVHe/JSFa6/hXbkBDQRdRk98AQgAyjXZ98VOgftRThuGuYxKhqahonLf Ihu+NuNMFG6sGGzkm2T+1i4uKyM8T/kGdcTzTXE/SMHmrCMz94FNcQ77/OFLz5HY 8hjaz5Sun7iNmz5HGct8OrsP6gQeJ5ucqm3vDZmnwU/+J+wcTosv5mgWoBVob7jb PBnoNVBQSVhD2ek4CDljn3PdReqYfe+ee8yn+6K1t9c1HHHMco3WpdgofUABd+7l Q1LF8IpBRDvWgdMciAPaSthIqFT6R6xLQhXV8SUm0mr2/GXbYqIptjvy1JmUwNk3 jE3LOLYulZChRdvVg3Y+xgkVlMYLy3SBQ1EaTnUUsGYbhGQnOwDwVAlxgwARAQAB iQE8BBgBCAAmFiEEG6B08RORVwbRQTSM3D21hzVj5rIFAl1GT3wCGwwFCQPCZwAA CgkQ3D21hzVj5rLzvAf/QzfDOrhRz9AVLiAqus3Z/WfZY81sUiewNM+YdV9aODht q4VE92SYHeR/b72+Fl62SRbDqxw7qG5FJGByuqo6nJjHEpnFzqB/pepTVDzlwvdn JO46tmepFAChPBpeTTjTs2CF/BG0As0KxXQCpdFw4m8UdkZ7Olt1/LKnXrFmr1BA jp2MvmAo38j2RyPTyXKWmJW+vC8DwmOGMoHCL6fM0TeaWey3rNxST7bbxPdRVc4Z /26k450FEW5D+VInb9NuFYSoE2UXs6DgI1OWuuGvWePrtXHeQvuNbGdEdUwU14mf msQ78G2MjX4AAYR5iNnQ/IWDBKbOWt3ajIoJuebArw== =oHpZ -----END PGP PUBLIC KEY BLOCK-----
然后,在 Github 的 SSH and GPG keys 中,新增一个 GPG key,内容即是上述命令的输出结果。
再次提醒,GPG 密钥中个人信息的邮箱部分,必须使用在 Github 中验证过的邮箱,否则添加 GPG key 会提示未经验证。
利用 GPG 私钥对 Git commit 进行签名
首先,需要让 Git 知道签名所用的 GPG 密钥 ID:
git config --global user.signingkey {key_id}
然后,在每次 commit 的时候,加上-S
参数,表示这次提交需要用 GPG 密钥进行签名:
git commit -S -m "..."
如果觉得每次都需要手动加上-S
有些麻烦,可以设置 Git 为每次 commit 自动要求签名:
git config --global commit.gpgsign true
但不论是否需要手动加上-S
,在 commit 时皆会弹出对话框,需要输入该密钥的密码,以确保是密钥拥有者本人操作,如图所示:
输入正确密码后,本次 commit 便被签名验证,push 到 Github 远程仓库后,即可显示出 Verified 绿色标记(由于fortest <[email protected]>
密钥的邮箱未经验证,所以此处实际用的是我本人的密钥进行签名):
可选步骤:信任 Github 的 GPG 密钥
事实上,在完成上述步骤后,已经可以基本完全正常地同时使用 Github 和 GPG 了,那为什么还需要这一步骤呢?很简单,不妨用git log --show-signature
试试查看本地的某个 Git 仓库的 commit 记录和签名信息:
$ git log --show-signature # some output is omitted commit ec37d4af120a69dafa077052cfdf4f5e33fa1ef3 (HEAD -> master) gpg: Signature made 2019 年 08 月 4 日 12:52:29 gpg: using RSA key 1BA074F113915706D141348CDC3DB5873563E6B2 gpg: Good signature from "fortest <[email protected]>" [ultimate] Author: keithnull <[email protected]> Date: Sun Aug 4 12:52:29 2019 +0800 test GPG commit 6937d638d950362f73bfbf28bc4a39d1700bf26b gpg: Signature made 2019 年 07 月 24 日 15:58:46 gpg: using RSA key 4AEE18F83AFDEB23 gpg: Can't check signature: No public key Author: Keith Null <20233656[email protected]> Date: Wed Jul 24 15:58:46 2019 +0800 Initial commit
可以发现,虽然所有的 commit 在 Github 中查看都是 Verified,但是有一些比较特殊:在 Github 网页端进行的操作,比如创建仓库。这些 commit 并没有用我们之前生成的密钥进行签名,而是由 Github 代为签名了。这样的结果就是,我们本地无法确认这些签名的真实性。
为了解决这个问题,我们需要导入并信任 Github 所用的 GPG 密钥。
先是导入:
$ curl https://github.com/web-flow.gpg | gpg --import # curl's output is omitted gpg: key 4AEE18F83AFDEB23: public key "GitHub (web-flow commit signing) <[email protected]>" imported gpg: Total number processed: 1 gpg: imported: 1
然后是信任(用自己的密钥为其签名验证,需要输入密码):
$ gpg --sign-key 4AEE18F83AFDEB23 pub rsa2048/4AEE18F83AFDEB23 created: 2017-08-16 expires: never usage: SC trust: unknown validity: full [ full ] (1). GitHub (web-flow commit signing) <[email protected]> pub rsa2048/4AEE18F83AFDEB23 created: 2017-08-16 expires: never usage: SC trust: unknown validity: full Primary key fingerprint: 5DE3 E050 9C47 EA3C F04A 42D3 4AEE 18F8 3AFD EB23 GitHub (web-flow commit signing) <[email protected]> Are you sure that you want to sign this key with your key "Keith Null <[email protected]>" (7C4BC917F7B12E8A) Really sign? (y/N) y
至此,再尝试查看本地仓库的 commit 签名信息,则会发现所有的 commit 签名都已得到验证:
$ git log --show-signature # some output is omitted commit 6937d638d950362f73bfbf28bc4a39d1700bf26b gpg: Signature made 2019 年 07 月 24 日 15:58:46 gpg: using RSA key 4AEE18F83AFDEB23 gpg: Good signature from "GitHub (web-flow commit signing) <[email protected]>" [full] Author: Keith Null <20233656[email protected]> Date: Wed Jul 24 15:58:46 2019 +0800 Initial commit
结束
经过这一番操作,Github 和 GPG 圆满结合在了一起,而我也得到了我想要的 Verified 标记。不过,GPG 的功能远非止于此,它还可以用来对文件、邮件等进行加密,还可以进行身份验证等等,都有待我去学习研究。
共有 0 条评论