跳到主要内容
  1. 所有文章/

GitHub 使用 GPG 密钥提交签名认证

·3990 字·约 8 分钟

GPG 简介 #

GNU Privacy GuardGnuPG)是一个密码学软件,是 RFC4880(也称为 PGP)定义的 OpenPGP 标准的完整且免费的实现。GnuPG 允许您对您的数据和通信进行加密和签名,它具有通用的密钥管理系统,以及各种公钥目录的访问模块。GnuPG,也称为 GPG,是一个命令行工具,具有与其他应用程序轻松集成的功能,有大量的前端应用程序和库可用,GnuPG 还提供对 S/MIME 和 Secure Shell (ssh) 的支持

最先在 Spencer Woo 的博客上看到一篇文章 震惊!竟然有人在 GitHub 上冒充我的身份! 第一次知道 Git 提交记录竟然还可以伪造

GitHubGitLab 等 Git 托管平台早就支持了使用 GPG 密钥验证代码提交者的身份,使用 GPG 签名 GitHub 提交的作用有:

  • 对提交进行签名,防止伪造提交记录者冒充身份
  • 经过签名认证的 commit 记录有个小绿勾真的很好看

不必等待,现在就开始吧!

安装 GnuPG #

不同操作系统安装方法可以在GnuPG 官方下载页面 看到,macOS 和 Linux 的大部分版本应该都默认安装有 GPG(但是可能版本不是特别新),您可以参照 GnuPG 官网 的指南从源码编译安装最新版本或在对应操作系统的软件源安装较新的版本,验证 GPG 安装版本

gpg --version

在 Windows 下 ,在安装了 Git 后,默认的 Git Bash 应该是包含 GPG 的

admin@dejavu MINGW64 ~
gpg --version

gpg (GnuPG) 2.2.28-unknown
libgcrypt 1.9.3-unknown
Copyright (C) 2021 Free Software Foundation, Inc.
License GNU GPL-3.0-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/admin/.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

当然,您也可以使用 scoop 安装 GPG

scoop install gpg

或者下载安装 gpg4win (包含 Kleopatra 图形化 GPG 前端管理工具),安装完成后

gpg --version

gpg (GnuPG) 2.2.28
libgcrypt 1.8.8
Copyright (C) 2021 g10 Code GmbH
License GNU GPL-3.0-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/admin/AppData/Roaming/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

无论是哪种方式安装的,记下 gpg --version 命令输出的 Home 目录,然后继续下一步

生成密钥对 #

第一步让您选择密钥类型,默认是 RSA,使用序号选择您想使用的密钥类型,无论是对于 SSH 还是 GPG 都不建议使用D SA 和 ECDSA 类型的密钥对!不同的密钥类型差异见文末,本文只记录 RSA 类型和 ED25519 (EDDSA)类型的 GPG 密钥对配置过程,文中重要信息已脱敏

RSA 类型 #

打开终端或者 Git Bash,来生成我们的第一个密钥对

# 开始生成密钥对的完整步骤
gpg --full-generate-key

gpg (GnuPG) 2.2.28-unknown; Copyright (C) 2021 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: directory '/c/Users/admin/.gnupg' created
gpg: keybox '/c/Users/admin/.gnupg/pubring.kbx' created
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
  (14) Existing key from card
Your selection? 1

选择 1 代表使用 RSA 类型密钥对,接下来会让您选择 RSA 密钥对的长度,建议 2048 位及以上,比如 3072 或者 4096 位,相对来说,越长越安全

RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits

接下来会问密钥对什么时候过期,确保自己能保护好 key 的安全的情况,选择 0 是永不过期

Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

接下来需要输入您的个人信息,如名称、邮箱等信息用于签名,给 GitHub 提交签名使用的话最起码邮箱要对应 GitHub 的验证邮箱

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

Real name: TestGPG
Email address: test@test.mail
Comment: Demo GPG
You selected this USER-ID:
    "TestGPG (Demo GPG) <test@test.mail>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

接下来会开始生成密钥对,在此过程中可以移动鼠标、读写硬盘、敲击键盘来加速熵的生成(极端的情况下可能会因为熵不够生成密钥对很慢甚至失败)

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.
gpg: /c/Users/admin/.gnupg/trustdb.gpg: trustdb created
gpg: key BC46E492FFB39503 marked as ultimately trusted
gpg: directory '/c/Users/admin/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/c/Users/admin/.gnupg/openpgp-revocs.d/B4BA3998165B3D080D89F915BC46E492FFB39503.rev'
public and secret key created and signed.

pub   rsa4096 2021-08-15 [SC]
      B4BA3998165B3D080D89F915BC46E492FFB39503
uid                      TestGPG (Demo GPG) <test@test.mail>
sub   rsa4096 2021-08-15 [E]

上面信息包含您的密钥对保存路径别忘记了,当弹出提示时需要使用一个密码保护您的 key,记得强一点,自己能记住的

不要使用弱密码
不要使用弱密码

ED25519 类型 #

GnuPG 2.1.x 支持 ECC(椭圆曲线加密),ECC 是通用术语,ECC 的安全性取决于所使用的曲线,不幸的是,没有人愿意使用 NIST 的标准化曲线

从 GnuPG 2.1.0 开始,我们可以使用 Ed25519 进行数字签名,从 2015 年 8 月的 GnuPG 2.1.7 开始,支持 Curve25519 加密。虽然它在 OpenPGP WG 中尚未标准化,但被认为更安全。

Ed25519 已经被引入到 OpenSSH 中,因此,我们可以使用 OpenPGP 的身份验证子密钥来使用 gpg-agent 的 ssh-agent 功能。但是,没有对相应曲线的加密支持。

打开终端或者 Git Bash,来生成 ED25519 位主密钥,CV25519 为加密子密钥

# 开始生成密钥对的完整步骤
gpg --expert --full-gen-key

gpg (GnuPG) 2.2.28; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC and ECC
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (13) Existing key
  (14) Existing key from card
Your selection? 9

输入序号 9 使用 ECC 椭圆曲线加密,然后输入 1 使用 CV25519 加密子密钥

Please select which elliptic curve you want:
   (1) Curve 25519
   (3) NIST P-256
   (4) NIST P-384
   (5) NIST P-521
   (6) Brainpool P-256
   (7) Brainpool P-384
   (8) Brainpool P-512
   (9) secp256k1
Your selection? 1

选择密钥对过期时间(根据实际情况选择)

Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

输入自己的信息

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

Real name: DejavuMoe
Email address: xxx@gmail.com
Comment: administrator of dejavu.moe
You selected this USER-ID:
    "DejavuMoe (administrator of dejavu.moe) <xxx@gmail.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

当弹出提示时需要使用一个密码保护您的 key,记得强一点,自己能记住的

Pinentry
Pinentry

熵不够容易导致失败,记得动动鼠标、读写硬盘、敲击键盘,这样可以生成更多的随机数,让熵足够

生成完成
生成完成

列出密钥对 #

打开终端/Git Bash,可以使用下面命令列出本地生成的密钥对信息

公钥 #

# 正常列出
gpg --list-keys
# 列出长的形式
gpg --list-keys --keyid-format LONG
# 列出短的形式
gpg --list-keys --keyid-format SHORT

私钥 #

# 正常列出
gpg --list-secret-keys
# 列出长的形式
gpg --list-secret-keys --keyid-format LONG
# 列出短的形式
gpg --list-secret-keys --keyid-format SHORT

添加 GPG 公钥 #

接下来要把我们的 GPG 公钥添加到相应的 Git 托管平台,以 GitHub 为例

# 列出密钥信息
gpg --list-secret-keys --keyid-format SHORT
C:/Users/admin/AppData/Roaming/gnupg/pubring.kbx
------------------------------------------------
sec   rsa4096/294514E1 2021-09-12 [C]
      6EB00F140C11B273E20B750A6B7D42C5294514E1
uid         [ultimate] ROOT
ssb   rsa4096/5BCACEC7 2021-09-12 [E]

sec   ed25519/F45262B5 2021-09-12 [SC]  # 这是即将使用的密钥
      4EB9C328545A046D93C1AB5A5FCA8C58F45262B5
uid         [ultimate] Dejavu Moe <admin@dejavu.moe>
ssb   cv25519/343B3B96 2021-09-12 [E]
  • GPG 私钥 ID:F45262B5
  • GPG 指纹: 4EB9C328545A046D93C1AB5A5FCA8C58F45262B5

对应您的实际密钥

上传公钥到 GitHub #

# 导出 GPG 公钥
gpg --armor --export <GPG 指纹>
# 以上面的为例
gpg --armor --export 4EB9C328545A046D93C1AB5A5FCA8C58F45262B5

会输出类似以下公钥内容

-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEYT11SxYJKwYBBAHaRw8BAQdA+Ai1bw15IhLQttPfFdgaZ2/MQ1Rfc68pPukE
dkoPbVe0HURlamF2dSBNb2UgPGFkbWluQGRlamF2dS5tb2U+iJAEExYIADgWIQRO
ucMoVFoEbZPBq1pfyoxY9FJitQUCYT11SwIbAwULCQgHAgYVCgkICwIEFgIDAQIe
AQIXgAAKCRBfyoxY9FJiteKWAP46wDE9A10F7cvB7o4iNcjPHO7cvtNr4LwTaReT
GDEwHwEAs5MyESeoAsNPF2mRJdTiVplAO/sbCpkJcQkyDRpZBA+4OARhPXVLEgor
BgEEAZdVAQUBAQdAX//jLpUapGmUSR+2/v0hLhppLTCOml06JJqpJwRwOzYDAQgH
iHgEGBYIACAWIQROucMoVFoEbZPBq1pfyoxY9FJitQUCYT11SwIbDAAKCRBfyoxY
9FJitf1aAP962Ed70pH9SjCF2jzQ4k/ej+QOyznTUKGfTVQgAH/7mQD7BzqkgRsR
LBHsRpg0cgLNrC0ktjOkrWqdCpqz13ULCA0=
=Y0Zc
-----END PGP PUBLIC KEY BLOCK-----

将其内容粘贴到 GitHub GPG Keys 设置里,然后点击 Add GPG key 即可

Git 提交使用签名 #

# 指定用于 git 提交时签名的 GPG key
git config --global user.signingkey F45262B5
# 以后提交的时候加上 -S 参数进行 GPG 签名,比如
git commit -S -m "first commit" 
# 强制当前仓库使用 GPG key 签名
git config commit.gpgsign true
# 强制全局使用 GPG key 签名
git config --global commit.gpgsign true
# 强制 GPG key 签名后提交时就不需要加 -S 参数了,比如
git commit -m "second commit"
# 查看 git 提交时的签名信息
git log --show-signature

现在提交时应该会让您输入保护 GPG Key 的密码,提交完成去 GitHub 上看看,应该可以看到效果了

签名提交
签名提交

Windows 平台注意

Windows 平台上的 Git 可能还需要特别设定 gpg.program 参数,找出 gpg.exe 可执行程序的路径并指定,否则可能会出错,找到路径后使用以下命令进行设定

git config --global gpg.program "C:\Program Files (x86)\GnuPG\bin\gpg.exe"

GPG 密钥导入导出 #

导出 #

打开 Terminal/Bash

# 列出所有密钥
gpg --list-keys
C:/Users/admin/AppData/Roaming/gnupg/pubring.kbx
------------------------------------------------
pub   rsa4096 2021-09-12 [C]
      6EB00F140C11B273E20B750A6B7D42C5294514E1
uid           [ultimate] ROOT
sub   rsa4096 2021-09-12 [E]

pub   ed25519 2021-09-12 [SC]
      4EB9C328545A046D93C1AB5A5FCA8C58F45262B5
uid           [ultimate] Dejavu Moe <admin@dejavu.moe>
sub   cv25519 2021-09-12 [E]

pub   rsa2048 2017-08-16 [SC]
      5DE3E0509C47EA3CF04A42D34AEE18F83AFDEB23
uid           [  full  ] GitHub (web-flow commit signing) <noreply@github.com>

# 根据密钥指纹:4EB9C328545A046D93C1AB5A5FCA8C58F45262B5 导出密钥对
# 导出公钥到用户目录的 gpg_pub_key.gpg 文件里
gpg --output gpg_pub_key.gpg --armor --export 4EB9C328545A046D93C1AB5A5FCA8C58F45262B5

# 导出私钥到用户目录的 gpg_sec_key.gpg
gpg --output gpg_sec_key.gpg --armor --export-secret-key 4EB9C328545A046D93C1AB5A5FCA8C58F45262B5

将公钥文件 gpg_pub_key.gpggpg_sec_key.gpg 备份到您觉得安全的地方!

导入 #

进入备份有 GPG 公钥/私钥文件所在的路径打开 Terminal/Bash,比如:

ls

gpg_pub_key.gpg  # GPG 公钥文件
gpg_sec_key.gpg  # GPG 私钥文件

进入备份有 GPG 公钥/私钥文件所在的路径打开 Terminal/Bash,开始导入密钥对:

# 导入公钥
gpg --import gpg_pub_key.gpg
# 导入私钥
gpg --allow-secret-key-import --import gpg_sec_key.gpg

导入 GitHub 的 GPG 密钥【可选】 #

这个步骤是可选的,仅仅用于 GitHub 提交签名认证,前面的步骤已经够了。但是在进行一些操作诸如 GitHub 网页端新建仓库时,我们本地会会无法信任这些签名的真实性,我们可以导入 GitHub 的 GPG 密钥并用自己的 GPG 密钥签名它,以支持本地对 GitHub 网页操作的信任

# 我们可以看一下 GitHub web-flow 的 GPG 公钥
curl https://github.com/web-flow.gpg
-----BEGIN PGP PUBLIC KEY BLOCK-----

xsBNBFmUaEEBCACzXTDt6ZnyaVtueZASBzgnAmK13q9Urgch+sKYeIhdymjuMQta
x15OklctmrZtqre5kwPUosG3/B2/ikuPYElcHgGPL4uL5Em6S5C/oozfkYzhwRrT
SQzvYjsE4I34To4UdE9KA97wrQjGoz2Bx72WDLyWwctD3DKQtYeHXswXXtXwKfjQ
7Fy4+Bf5IPh76dA8NJ6UtjjLIDlKqdxLW4atHe6xWFaJ+XdLUtsAroZcXBeWDCPa
buXCDscJcLJRKZVc62gOZXXtPfoHqvUPp3nuLA4YjH9bphbrMWMf810Wxz9JTd3v
yWgGqNY0zbBqeZoGv+TuExlRHT8ASGFS9SVDABEBAAHNNUdpdEh1YiAod2ViLWZs
b3cgY29tbWl0IHNpZ25pbmcpIDxub3JlcGx5QGdpdGh1Yi5jb20+wsBiBBMBCAAW
BQJZlGhBCRBK7hj4Ov3rIwIbAwIZAQAAmQEIACATWFmi2oxlBh3wAsySNCNV4IPf
DDMeh6j80WT7cgoX7V7xqJOxrfrqPEthQ3hgHIm7b5MPQlUr2q+UPL22t/I+ESF6
9b0QWLFSMJbMSk+BXkvSjH9q8jAO0986/pShPV5DU2sMxnx4LfLfHNhTzjXKokws
+8ptJ8uhMNIDXfXuzkZHIxoXk3rNcjDN5c5X+sK8UBRH092BIJWCOfaQt7v7wig5
4Ra28pM9GbHKXVNxmdLpCFyzvyMuCmINYYADsC848QQFFwnd4EQnupo6QvhEVx1O
j7wDwvuH5dCrLuLwtwXaQh0onG4583p0LGms2Mf5F+Ick6o/4peOlBoZz48=
=HXDP
-----END PGP PUBLIC KEY BLOCK-----

首先需要导入 GitHub 的 GPG 公钥到本地

curl https://github.com/web-flow.gpg | gpg --import
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--    100   919  100   919    0     0    919      0  0:00:01 --:--:--  0:00:01  12100   919  100   919    0     0    919      0  0:00:01 --:--:--  0:00:01  1252
gpg: key 4AEE18F83AFDEB23: public key "GitHub (web-flow commit signing) <noreply@github.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1

列出本地 GPG 公钥,可以看到 GitHub web-flow 的 GPG 密钥已经导入了

gpg --list-keys --keyid-format SHORT
C:/Users/admin/AppData/Roaming/gnupg/pubring.kbx
------------------------------------------------
pub   rsa4096/294514E1 2021-09-12 [C]
      6EB00F140C11B273E20B750A6B7D42C5294514E1
uid         [ultimate] ROOT
sub   rsa4096/5BCACEC7 2021-09-12 [E]

pub   ed25519/F45262B5 2021-09-12 [SC]  # 这是等下我即将用来签名 GitHub web-flow GPG 的密钥
      4EB9C328545A046D93C1AB5A5FCA8C58F45262B5
uid         [ultimate] Dejavu Moe <admin@dejavu.moe>  
sub   cv25519/343B3B96 2021-09-12 [E]

pub   rsa2048/3AFDEB23 2017-08-16 [SC]
      5DE3E0509C47EA3CF04A42D34AEE18F83AFDEB23
uid         [ unknown] GitHub (web-flow commit signing) <noreply@github.com>

然后使用自己的密钥为其签名

gpg -u admin@dejavu.moe --sign-key 3AFDEB23 # -u 后面是邮箱

pub  rsa2048/4AEE18F83AFDEB23
     created: 2017-08-16  expires: never       usage: SC
     trust: unknown       validity: unknown
[ unknown] (1). GitHub (web-flow commit signing) <noreply@github.com>


pub  rsa2048/4AEE18F83AFDEB23
     created: 2017-08-16  expires: never       usage: SC
     trust: unknown       validity: unknown
 Primary key fingerprint: 5DE3 E050 9C47 EA3C F04A  42D3 4AEE 18F8 3AFD EB23

     GitHub (web-flow commit signing) <noreply@github.com>

Are you sure that you want to sign this key with your
key "Dejavu Moe <admin@dejavu.moe>" (5FCA8C58F45262B5)

Really sign? (y/N) y # 输入 y 确定

完成,之后可以使用 git log --show-signature 验证

对密钥类型的选择:

GitHub 支持多种 GPG 密钥算法:

  • RSA
  • ElGamal
  • DSA
  • ECDH
  • ECDSA
  • EdDSA

RSARivest–Shamir–Adleman)是最早的公钥密码系统之一,被广泛用于安全数据传输,因此似乎得到最好的支持。它的安全性取决于整数分解,因此永远不需要安全的 RNG(随机数生成器)。与 DSA 相比,RSA 的签名验证速度更快,但生成速度较慢。

DSA数字签名算法)是用于数字签名的联邦信息处理标准。它的安全性取决于离散的对数 问题。与RSA相比,DSA 的签名生成速度更快,但验证速度较慢。如果使用错误的数字生成器,可能会破坏安全性。从 OpenSSH 7.0开始,默认情况下SSH不再支持 DSA 密钥(ssh-dss),根据 SSH 标准(RFC 4251及更高版本),DSA 密钥可用于任何地方。

ECDSA椭圆曲线数字签名算法)是DSA(数字签名算法)的椭圆曲线实现。椭圆曲线密码术能够以较小的密钥提供与RSA相对相同的安全级别。它还具有DSA对不良RNG敏感的缺点。

EdDSA爱德华兹曲线数字签名算法)是一种使用基于 扭曲爱德华兹曲线Schnorr 签名 变体的数字签名方案。签名创建在 EdDSA 中是确定性的,其安全性是基于某些离散对数问题的难处理性,因此它比 DSA 和 ECDSA 更安全,后者要求每个签名都具有高质量的随机性。

Ed25519EdDSA 签名方案 ,但使用 SHA-512/256Curve25519;它是一条 安全的椭圆形曲线,比 DSA,ECDSA 和 EdDSA 提供更好的安全性,并且具有更好的性能,在 openSSH 6.5 中引入。

参考资料:

Dejavu Moe
作者
Dejavu Moe
Not for success, just for growing.