Signing Git Commits & Tags with GPGgitgpgUsing GPG or S/MIME, you can sign tags or commits locally. Those tags/commits are then marked as trusted on GitHub, so others can be confident the changes came from a trusted source.
Copyright notice: This is an original article by xwi88, licensed under CC BY-NC 4.0. Commercial use is prohibited; please cite the source when reposting. Follow at https://github.com/xwi88
Enable vigilant mode
With vigilant mode on, your own unsigned commits are flagged Unverified. This alerts you and others to potential authenticity issues. The author and committer of a Git commit are easily spoofed — someone could push a commit that claims to be yours but isn’t.
Path: Settings->SSH and GPG keys>Vigilant mode->select Flag unsigned commits as unverified
GPG Command
man gpg
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
| gpg (GnuPG) 2.2.34
libgcrypt 1.9.4
Copyright (C) 2022 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: ~/.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
Syntax: gpg [options] [files]
Sign, check, encrypt or decrypt
Default operation depends on the input data
Commands:
-s, --sign make a signature
--clear-sign make a clear text signature
-b, --detach-sign make a detached signature
-e, --encrypt encrypt data
-c, --symmetric encryption only with symmetric cipher
-d, --decrypt decrypt data (default)
--verify verify a signature
-k, --list-keys list keys
--list-signatures list keys and signatures
--check-signatures list and check key signatures
--fingerprint list keys and fingerprints
-K, --list-secret-keys list secret keys
--generate-key generate a new key pair
--quick-generate-key quickly generate a new key pair
--quick-add-uid quickly add a new user-id
--quick-revoke-uid quickly revoke a user-id
--quick-set-expire quickly set a new expiration date
--full-generate-key full featured key pair generation
--generate-revocation generate a revocation certificate
--delete-keys remove keys from the public keyring
--delete-secret-keys remove keys from the secret keyring
--quick-sign-key quickly sign a key
--quick-lsign-key quickly sign a key locally
--quick-revoke-sig quickly revoke a key signature
--sign-key sign a key
--lsign-key sign a key locally
--edit-key sign or edit a key
--change-passphrase change a passphrase
--export export keys
--send-keys export keys to a keyserver
--receive-keys import keys from a keyserver
--search-keys search for keys on a keyserver
--refresh-keys update all keys from a keyserver
--import import/merge keys
--card-status print the card status
--edit-card change data on a card
--change-pin change a card's PIN
--update-trustdb update the trust database
--print-md print message digests
--server run in server mode
--tofu-policy VALUE set the TOFU policy for a key
Options controlling the diagnostic output:
-v, --verbose verbose
-q, --quiet be somewhat more quiet
--options FILE read options from FILE
--log-file FILE write server mode logs to FILE
Options controlling the configuration:
--default-key NAME use NAME as default secret key
--encrypt-to NAME encrypt to user ID NAME as well
--group SPEC set up email aliases
--openpgp use strict OpenPGP behavior
-n, --dry-run do not make any changes
-i, --interactive prompt before overwriting
Options controlling the output:
-a, --armor create ascii armored output
-o, --output FILE write output to FILE
--textmode use canonical text mode
-z N set compress level to N (0 disables)
Options controlling key import and export:
--auto-key-locate MECHANISMS use MECHANISMS to locate keys by mail address
--disable-dirmngr disable all access to the dirmngr
Options to specify keys:
-r, --recipient USER-ID encrypt for USER-ID
-u, --local-user USER-ID use USER-ID to sign or decrypt
(See the man page for a complete listing of all commands and options)
Examples:
-se -r Bob [file] sign and encrypt for user Bob
--clear-sign [file] make a clear text signature
--detach-sign [file] make a detached signature
--list-keys [names] show keys
--fingerprint [names] show fingerprints
Please report bugs to <https://bugs.gnupg.org>.
|
gpg shell command
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
| gpg> help
quit quit this menu
save save and quit
help show this help
fpr show key fingerprint
grip show the keygrip
list list key and user IDs
uid select user ID N
key select subkey N
check check signatures
sign sign selected user IDs [* see below for related commands]
lsign sign selected user IDs locally
tsign sign selected user IDs with a trust signature
nrsign sign selected user IDs with a non-revocable signature
deluid delete selected user IDs
delkey delete selected subkeys
delsig delete signatures from the selected user IDs
pref list preferences (expert)
showpref list preferences (verbose)
trust change the ownertrust
revsig revoke signatures on the selected user IDs
enable enable key
disable disable key
showphoto show selected photo IDs
clean compact unusable user IDs and remove unusable signatures from key
minimize compact unusable user IDs and remove all signatures from key
* The 'sign' command may be prefixed with an 'l' for local signatures (lsign),
a 't' for trust signatures (tsign), an 'nr' for non-revocable signatures
(nrsign), or any combination thereof (ltsign, tnrsign, etc.).
|
GPG key generation & config
Note
GPG does not come installed by default on macOS or Windows. To install GPG command line tools, see
GnuPG’s Download page.
Existing GPG keys
gpg --list-secret-keys --keyid-format=long
Example
1
2
3
4
5
| ~/.gnupg/pubring.kbx
-----------------------------------
sec rsa4096/325ACD1FD3B6AA80 2022-03-07 [SC] [expires: 2024-03-06]
1F11E9A019E23C53C11C8D4C325ACD1FD3B6AA80
uid [ultimate] xwi88 <278810732@qq.com>
|
- the GPG key ID: 325ACD1FD3B6AA80
Generate a new GPG key
If an old GPG key already exists, you don’t need to generate a new one!
gpg --default-new-key-algo rsa4096 --gen-key
Tip
Algorithms github supports for generating gpg keys
- RSA
- ElGamal
- DSA
- ECDH
- ECDSA
- EdDSA
Example
gpg --default-new-key-algo rsa4096 --gen-key
- Real name:
tmp_gpg - Email address:
278810732@qq.com
You selected this USER-ID:
“tmp_gpg 278810732@qq.com”
- Change (N)ame, (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.
Note that this key cannot be used for encryption. You may want to use
the command “–edit-key” to generate a subkey for this purpose.
1
2
3
| pub rsa4096 2022-03-09 [SC] [expires: 2024-03-08]
F09FC9FB34FA457ED2F7090AFE47519758053257
uid tmp_gpg <278810732@qq.com>
|
Latest GPG keys
gpg --list-secret-keys --keyid-format=long
1
2
3
4
5
6
7
8
| -----------------------------------
sec rsa4096/325ACD1FD3B6AA80 2022-03-07 [SC] [expires: 2024-03-06]
1F11E9A019E23C53C11C8D4C325ACD1FD3B6AA80
uid [ultimate] xwi88 <278810732@qq.com>
sec rsa4096/FE47519758053257 2022-03-09 [SC] [expires: 2024-03-08]
F09FC9FB34FA457ED2F7090AFE47519758053257
uid [ultimate] tmp_gpg <278810732@qq.com>
|
Export a GPG key
gpg --armor --export FE47519758053257- or
gpg -a -o --export FE47519758053257
1
2
3
4
5
| -----BEGIN PGP PUBLIC KEY BLOCK-----
xxxxxxxxxxxxxxx
-----END PGP PUBLIC KEY BLOCK-----
|
Export a gpg public key to a specific destination:
gpg --export --armor [uid] > gpgkey.pub.asc export to file uid: keyID/name/emailgpg --keyserver [keyserverAddress] --send-keys keyIDs export to a specific key servergpg --send-keys keyIDs export to the default key server
Output when exporting to the default key server
1
| output: gpg: sending key 90684042688CB9BE to hkps://keyserver.ubuntu.com
|
Find a GPG key
keyIDs may be: name, keyID, email
gpg --keyserver keyserverAddress --search-keys keyIDsgpg --search-keys keyIDs search the default key server
Example
gpg --search-keys xwi88
1
2
3
4
| gpg: data source: https://162.213.33.8:443
(1) xwi88 <278810732@qq.com>
4096 bit RSA key 90684042688CB9BE, created: 2022-03-09
Keys 1-1 of 1 for "xwi88". Enter number(s), N)ext, or Q)uit > q
|
gpg --search-keys 90684042688CB9BE
1
2
3
4
| gpg: data source: https://162.213.33.8:443
(1) xwi88 <278810732@qq.com>
4096 bit RSA key 90684042688CB9BE, created: 2022-03-09
Keys 1-1 of 1 for "90684042688CB9BE". Enter number(s), N)ext, or Q)uit > q
|
gpg --search-keys 278810732@qq.com
1
2
3
4
| gpg: data source: https://162.213.33.8:443
(1) xwi88 <278810732@qq.com>
4096 bit RSA key 90684042688CB9BE, created: 2022-03-09
Keys 1-1 of 1 for "278810732@qq.com". Enter number(s), N)ext, or Q)uit > y
|
Import a GPG key
Key-server import — without an address it defaults to hkps://keyserver.ubuntu.com (your address may differ; this is local output)
gpg --import [GPG public key] import from filegpg --keyserver [keyserverAddress] --recv-keys keyIDs import from key servergpg --recv-keys keyIDs import from the default key servergpg --refresh-keys update all keys from the key server
Example
gpg --recv-keys 90684042688CB9BE
1
2
3
| gpg: key 90684042688CB9BE: "xwi88 <278810732@qq.com>" not changed
gpg: Total number processed: 1
gpg: unchanged: 1
|
Secret-key backup, export & import
Mainly for: sharing across your own machines
Warning
- Never upload a secret key to a key server
- Back up the secret-key file securely
- Secret-key file permission:
600 - Public-key file permission:
644
Export a secret key
gpg -a -o test_secKey.asc --export-secret-keys keyID
Example
gpg -K view local secret keys
gpg -a -o test_secKey.asc --export-secret-keys 1F11E9A019E23C53C11C8D4C325ACD1FD3B6AA80 export the secret key
Import a secret key
gpg --import secKeyFile
Example
gpg --import test_secKey.asc import a secret key — same operation as importing a public key
1
2
3
| gpg: key 325ACD1FD3B6AA80: "xwi88 <278810732@qq.com>" not changed
gpg: Total number processed: 1
gpg: unchanged: 1
|
gpg -K view local secret keys
Key revocation
After generating a new key pair, if other key pairs are no longer used, publish a revocation certificate immediately to declare the old public key invalid and prevent malicious use.
gpg --output test_revoke.asc --gen-revoke keyID
gpg --import test_revoke.asc import the revocation certificate
gpg --send-keys keyID send the revocation certificate to the server, declaring the original GPG Key revoked
Example
gpg --output test_revoke.asc --gen-revoke 325ACD1FD3B6AA80
or gpg --output test_revoke.asc --generate-revocation 325ACD1FD3B6AA80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| sec rsa4096/325ACD1FD3B6AA80 2022-03-07 xwi88 <278810732@qq.com>
Create a revocation certificate for this key? (y/N) y
Please select the reason for the revocation:
0 = No reason specified
1 = Key has been compromised
2 = Key is superseded
3 = Key is no longer used
Q = Cancel
(Probably you want to select 1 here)
Your decision? 3
Enter an optional description; end it with an empty line:
>
Reason for revocation: Key is no longer used
(No description given)
Is this okay? (y/N) y
ASCII armored output forced.
Revocation certificate created.
Please move it to a medium which you can hide away; if Mallory gets
access to this certificate he can use it to make your key unusable.
It is smart to print this certificate and store it away, just in case
your media become unreadable. But have some caution: The print system of
your machine might store the data and make it available to others!
|
gpg --import test_revoke.asc
1
2
3
4
5
6
| gpg: key 325ACD1FD3B6AA80: "xwi88 <278810732@qq.com>" revocation certificate imported
gpg: Total number processed: 1
gpg: new key revocations: 1
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 3 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 3u
gpg: next trustdb check due at 2024-03-06
|
gpg --send-keys 325ACD1FD3B6AA80
gpg --search-keys 325ACD1FD3B6AA80
1
2
3
4
|
gpg: data source: https://162.213.33.8:443
gpg: key "325ACD1FD3B6AA80" not found on keyserver
gpg: keyserver search failed: Not found
|
Delete keys
gpg --delete-secret-keys keyID delete this firstgpg --delete-keys keyID
Restart the GPG Agent
GPG restarts it on demand
gpgconf --kill gpg-agent
Set the GPG key
Copy the GPG key you want to use, beginning with —–BEGIN PGP PUBLIC KEY BLOCK—– and ending with —–END PGP PUBLIC KEY BLOCK—–
Paste it into github: Settings->SSH and GPG keys->GPG keys. If the key already exists, ignore; otherwise create a new entry and paste.
Mind whether you want this global; with a global config, an individual project can still override it! Replace –global with local
1
2
3
4
5
| # global config
git config --global user.signingkey 325ACD1FD3B6AA80
# configure your Git client to sign commits by default for a local repository, in Git versions 2.0.0 and above
git config --global commit.gpgsign true
|
Create a signed commit
git commit -S -m "your commit message"
If you’ve set commits to be signed by default, you can also just:
git commit -m "your commit message"
Warning
If your GPG key uses a GPG key passphrase, you’ll need to enter your passphrase when committing.
You can store the GPG key passphrase to avoid entering it on every sign:
- Mac users, GPG Suite can store your GPG key passphrase in Mac OS Keychain
- Windows users, Gpg4win
- Manually configure gpg-agent to store it
Sign a tag
1
2
3
4
5
| # sign a tag
git tag -s my_tag
# verify your signed tag
git tag -v my_tag
|
View commit signatures
git log --show-signature
Tip
Format the log to show signatures — define your own log view:
git log --color --graph --pretty=format:'%C(cyan)%G?%Creset %Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset | [%GK trust:%GT] %C(yellow)%GS%Creset' --abbrev-commit
1
2
3
4
5
6
7
| * G fb56816 - fixed rsync dir (2 days ago) <xwi88> | [325ACD1FD3B6AA80 trust:ultimate] xwi88 <278810732@qq.com>
* G 63d6ec2 - fixed rsync deploy (2 days ago) <xwi88> | [325ACD1FD3B6AA80 trust:ultimate] xwi88 <278810732@qq.com>
* N 1ac5368 - workflow add remote deploy (2 days ago) <xwi88> | [ trust:undefined]
* E 289ae51 - add domain ICP info (2 days ago) <xwi88> | [EEA29F407613E698 trust:]
* E f0e44bd - switch comment store repo:x (2 days ago) <xwi88> | [EEA29F407613E698 trust:]
* E fbd5778 - fixed giscus issue (3 days ago) <xwi88> | [EEA29F407613E698 trust:]
* E 5fa8d41 - replace utterances by giscus (3 days ago) <xwi88> | [EEA29F407613E698 trust:]
|
- G: for a good (valid) signature
- B: for a bad signature
- U: for a good signature with unknown validity
- X: for a good signature that has expired
- Y: for a good signature made by an expired key
- R: for a good signature made by a revoked key
- E: if the signature cannot be checked (e.g. missing key)
- N: for no signature
Local signature-verification issues
When viewing commit signatures via git log --show-signature, you may see:
1
2
3
4
| commit e39e0920b68648b0751b7f91fffdd07080391945 (HEAD -> main, origin/main, origin/HEAD)
gpg: Signature made Thu Mar 10 00:19:37 2022 CST
gpg: using RSA key 7E7F28C4EFFD7721E0133ED490684042688CB9BE
gpg: Can't check signature: No public key
|
You can also verify a specific commit’s signature with git verify-commit e39e0920b68648b0751b7f91fffdd07080391945
This happens because your local repo doesn’t have the gpg public key used for those commits. Fix it by importing the committer’s gpg public keys.
Importing missing gpg keys & verifying
To get full signature verification, we import the missing public keys. Suggested import sources:
https://github.com/<username>.gpg requires you to have uploaded GPG keys to githubhkps://keyserver.ubuntu.com requires you to have uploaded keys
Certify the imported public key
gpg --list-keys --keyid-format=long find the key you want to certifygpg --lsign-key [GPG key ID] local signgpg --edit-key [GPG key ID] local sign — pick one of this or the above
Example
Import the missing public key from github gpg --keyserver https://github.com/xwi88.gpg --recv-keys 7E7F28C4EFFD7721E0133ED490684042688CB9BE
Certify the key gpg --lsign-key 7E7F28C4EFFD7721E0133ED490684042688CB9BE
Check the certified key gpg --list-keys --keyid-format=long
1
2
3
| pub rsa4096/90684042688CB9BE 2022-03-09 [SC] [expires: 2024-03-08]
7E7F28C4EFFD7721E0133ED490684042688CB9BE
uid [ full ] xwi88 <278810732@qq.com>
|
Verify our commit again git verify-commit e39e0920b68648b0751b7f91fffdd07080391945
1
2
3
| gpg: Signature made Thu Mar 10 00:19:37 2022 CST
gpg: using RSA key 7E7F28C4EFFD7721E0133ED490684042688CB9BE
gpg: Good signature from "xwi88 <278810732@qq.com>" [full]
|
Importing Github’s GPG public key
github public GPG key for web-flow
curl https://github.com/web-flow.gpg | gpg --import import github public gpg keygpg --edit-key noreply@github.com trust quit trust and save, you choose: 4gpg --lsign-key noreply@github.com sign selected user IDs locally
GPG key update & renewal
gpg --edit-key [GPG key ID]
Example
1
2
3
| sec rsa4096 2022-03-09 [SC] [expires: 2024-03-08]
CE70FE5A7EB462DDA68EE86913431F2AC47C4AE0
uid [ultimate] tmp_gpg_local <278810732@qq.com>
|
This GPG key is regenerated, for demo purposes only
gpg --edit-key CE70FE5A7EB462DDA68EE86913431F2AC47C4AE0
Key output:
1
2
3
4
| sec rsa4096/73758EF02856F877
created: 2022-03-09 expires: 2024-03-08 usage: SC
trust: ultimate validity: ultimate
[ultimate] (1). tmp_gpg_local <278810732@qq.com>
|
Enter expire to update the expiry
gpg> expire
1
2
3
4
5
6
7
| Changing expiration time for the primary key.
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) 180d
Key expires at Mon Sep 5 21:48:04 2022 CST
Is this correct? (y/N) y
1
2
3
4
| sec rsa4096/13431F2AC47C4AE0
created: 2022-03-09 expires: 2022-09-05 usage: SC
trust: ultimate validity: ultimate
[ultimate] (1). tmp_gpg_local <278810732@qq.com>
|
gpg> trust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| sec rsa4096/13431F2AC47C4AE0
created: 2022-03-09 expires: 2022-09-05 usage: SC
trust: ultimate validity: ultimate
[ultimate] (1). tmp_gpg_local <278810732@qq.com>
Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)
1 = I don't know or won't say
2 = I do NOT trust
3 = I trust marginally
4 = I trust fully
5 = I trust ultimately
m = back to the main menu
|
Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y
1
2
3
4
| sec rsa4096/13431F2AC47C4AE0
created: 2022-03-09 expires: 2022-09-05 usage: SC
trust: ultimate validity: ultimate
[ultimate] (1). tmp_gpg_local <278810732@qq.com>
|
gpg> save
gpg --list-secret-keys --keyid-format=long verify the expiry updated
1
2
3
4
5
6
7
| sec rsa4096/90684042688CB9BE 2022-03-09 [SC] [expires: 2024-03-08]
7E7F28C4EFFD7721E0133ED490684042688CB9BE
uid [ultimate] xwi88 <278810732@qq.com>
sec rsa4096/13431F2AC47C4AE0 2022-03-09 [SC] [expires: 2022-09-05]
CE70FE5A7EB462DDA68EE86913431F2AC47C4AE0
uid [ultimate] tmp_gpg_local <278810732@qq.com>
|
Rebinding the GPG sign key
Don’t change this unless you need to.
Warning
git config --global commit.gpgSign truegit config --global user.signingKey 13431F2AC47C4AE0
If anything changes, sync the GPG key in your git repo too.
Deleting GPG keys
gpg --delete-secret-key [uid]gpg --delete-secret-key [uid1] [uid2]
Warning
Don’t delete unless necessary — if it’s just expiry, do a renewal instead.
uid for tmp_gpg may be any of the following; prefer the GPG key ID:
- tmp_gpg
- 13431F2AC47C4AE0
- CE70FE5A7EB462DDA68EE86913431F2AC47C4AE0
Example
gpg --delete-secret-key 13431F2AC47C4AE0
1
| sec rsa4096/13431F2AC47C4AE0 2022-03-09 tmp_gpg_local <278810732@qq.com>
|
Delete this key from the keyring? (y/N) y
This is a secret key! - really delete? (y/N) y
Verify deletion: gpg --list-secret-keys
1
2
3
| sec rsa4096 2022-03-09 [SC] [expires: 2024-03-08]
7E7F28C4EFFD7721E0133ED490684042688CB9BE
uid [ultimate] xwi88 <278810732@qq.com>
|
References