Using GPG and signing git commits

Contents

1 Introduction

github, linux packages and repositories, email… a cryptographic digital signature can be used in several places to provide authenticity - a reasonable proof that the artifact was indeed generate by the author.

These are my notes on gpg signature management. gpg has changed a lot since I last used it, and I’ve decided to shift to a new strategy - might as well write it all down this time.

The notation in this article uses bold for user input.

1.1 Basics of public key infrastructure (PKI)

In broad strokes, a digital signature works in the context of the following workflow:

The wikipedia page has more details.

1.2 The strategy

Using gpg to generate a pair of cryptographic keys for digital signatures is quite trivial. That’s not what we are going to do.

Our strategy, instead, is to generate a master key pair that we then use to generate several signature subkey pairs, that we can then use in different hosts and for different services. After generating the keys, we put the master key away, and leave installed in each host only the relevant secret keys. This has some advatanges:

Keep in mind that all these points depend on the security of the master key.

2 Master key setup

The instruction in this section are the initial setup and master key creation. It should be done only once.

2.1 Using a flash drive

Part of our strategy involves keeping the master key secure. One way of doing that is by keeping it in a flash drive that is physically kept secure. We could create and work with the key locally and then move it away, but we are instead going to work with it straight from a flash drive.

The master key is already secured by a password, so there’s no need to encrypt the flash drive because of it. If you want to keep other sensitive files there, though, you should encrypt it. You can take a look at the Creating an encrypted directory-in-a-file article for basic instructions.

In my particular setup, I’m using a flash drive with a luks2-encrypted partition labeled cryptflash that I mount with:

$ cd /dev/disk/by-label
$ pmount cryptflash
Enter passphrase for cryptflash: mypassphrase

Note that pmount doesn’t need any configuration to do that - it detects luks and the filesystem, and mounts that partition in /media/cryptflash. We assume this path is being used below.

2.2 Configuring gpg

It’s worth noting that the ages-old interface design of gpg doesn’t support this approach in an intuitive way with the default configuration. The first thing we should do is add a couple of lines to ~/.gnupg/gpg.conf:

utf8-strings
keyid-format long
with-fingerprint
with-sig-list
list-options show-notations

These options make gpg show more information about the subkeys, information we are going to set and use.

2.3 Creating the master key

We start by assigning the directory in the detachable drive to GNUPGHOME:

$ export GNUPGHOME="/media/cryptflash/dotgpg"

We create the directory and copy our ~/.gnupg/gnupg.conf to it:

$ mkdir -p "$GNUPGHOME"
$ cp "$HOME/.gnupg/gpg.conf" "$GNUPGHOME/"

To keep the instructions below a bit more “copy-pasteable”, let’s use an EMAIL variable and the following FULLNAME variable:

$ FULLNAME=$(getent passwd "$USER" | sed -nE 's@^([^:]+:){4}([^,:]+).*@\2@p')

We can now generate the master key pair:

$ gpg --quick-gen-key "$FULLNAME <$EMAIL>" default sign 0
gpg: keybox 'pubring.kbx' created
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.
Enter passphrase: mypassphrase
gpg: trustdb.gpg: trustdb created
gpg: directory 'openpgp-revocs.d' created
gpg: revocation certificate stored as 'openpgp-revocs.d/A4830FEEEAF9C91DBB06CE43DA4203550A52BC2A.rev'
public and secret key created and signed.
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.
pub   rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                            Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>

That command asks you for a password, and then creates the master key pair with default options and no expiration date. For more details on why a master key expiration date is irrelevant in our scenario, read this.

Keep in mind that gpg’s interface is… weird. It has its own REPL that you can use if you want. In this article we are always invoking it from the shell and passing the commands as arguments.

We’ll use gpg -k to lists the public keys and their states quite often as we create/edit keys in this article, starting now:

$ gpg -k
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
pubring.kbx
----------------------------
pub   rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>

The DA4203550A52BC2A above is the generated ID of our master key and we’ll need it every time want to use the key. We might as well store it in a variable:

$ masterkeyid=DA4203550A52BC2A

2.4 Adding a secondary ID and email

If you have a secondary email, you should add another User ID:

$ gpg --quick-add-uid "$FULLNAME <$EMAIL>" "$FULLNAME <[email protected]>"
Enter passphrase: mypassphrase

Notice that, by default, the last user ID becomes the primary and it’s trust status is unknown:

$ gpg -k
pubring.kbx
----------------------------
pub   rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                 [ unknown] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>

To make the old user ID the primary, select it with uid 1 and invoke primary; then run gpg --check-trustdb to update the trust status of the new user ID:

$ gpg --edit-key "$masterkeyid" uid 2 primary save
gpg (GnuPG) 2.2.40; Copyright (C) 2022 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.
Secret key is available.
sec  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: ultimate      validity: ultimate
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ultimate] (2)  Leandro Lisboa Penz <[email protected]>
sec  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: ultimate      validity: ultimate
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ultimate] (2)  Leandro Lisboa Penz <[email protected]>
sec  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: ultimate      validity: ultimate
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ultimate] (2)* Leandro Lisboa Penz <[email protected]>
sec  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: ultimate      validity: ultimate
[ unknown] (1)  Leandro Lisboa Penz <[email protected]>
[ultimate] (2)* Leandro Lisboa Penz <[email protected]>
$ gpg --check-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

These last 2 lines are probably familiar: sometimes, gpg -k updates the trusted database, but not always.

We now have the 2 user IDs in our master key DA4203550A52BC2A, in the correct order:

$ gpg -k
pubring.kbx
----------------------------
pub   rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>

To see the corresponding private keys, use gpg -K (capital):

$ gpg -K
pubring.kbx
----------------------------
sec   rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>

The notable difference above is the sec keyword describing the secret part of our master key pair.

2.5 Importing the master public key in the host

We can now export the public part of the master key pair, and import it in the host system. That allows us to check the signatures of our subkeys and give them trust.

$ gpg --armor --output /tmp/master.pub.gpg --export "$masterkeyid"

We don’t actually need access to the master key flash drive after exporting its public part. We can then reset GNUPGHOME. That makes gpg use the default database, to where we import the file:

$ unset GNUPGHOME
$ gpg --import /tmp/master.pub.gpg
gpg: keybox 'pubring.kbx' created
gpg: trustdb.gpg: trustdb created
gpg: key DA4203550A52BC2A: public key "Leandro Lisboa Penz <[email protected]>" imported
gpg: Total number processed: 1
gpg:               imported: 1

And trust it:

$ gpg --edit-key "$masterkeyid" trust save
gpg (GnuPG) 2.2.40; Copyright (C) 2022 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.
pub  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: unknown       validity: unknown
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ unknown] (2)  Leandro Lisboa Penz <[email protected]>
pub  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: unknown       validity: unknown
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ unknown] (2)  Leandro Lisboa Penz <[email protected]>
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
pub  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: ultimate      validity: unknown
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ unknown] (2)  Leandro Lisboa Penz <[email protected]>
Please note that the shown key validity is not necessarily correct
unless you restart the program.
Key not changed so no update needed.

Result:

$ gpg -k
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
pubring.kbx
----------------------------
pub   rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>

As we have imported only the public part of the master key pair, gpg -K won’t show us anything:

$ gpg -K

And this is the setup. After it is done, we can do the following to get the flash drive unmounted:

$ pumount cryptflash

We can now remove the flash drive. Keep it safe.

3 Adding a subkey

The single purpose of the master key is the generation of subkeys - one for each combination of host and service, in a matrix-like fashion. That allows us to track down all services affected by a vulnerable host, and act accordingly.

First, we insert the master key flash drive, mount it and set GNUPGHOME:

$ cd /dev/disk/by-label
$ pmount cryptflash
Enter passphrase for cryptflash: mypassphrase
$ export GNUPGHOME="/media/cryptflash/dotgpg"

3.1 Creating the subkey

Before adding the subkey, we have to decide what we are going to use it for so that we can put this information inside a notation. Notations are key-value tags assigned to a signature, where the key has the format id@domain, and domain acts as a namespace - more information in RFC4880. We can also use a second notation to identify the host where the key is installed. I’m actually using [email protected] for the hosts and [email protected] for the services.

So, to add a subkey for github that is installed in the host darkstar:

$ gpg --cert-notation [email protected]=darkstar --cert-notation [email protected]=github --edit-key "$masterkeyid" addkey save
gpg (GnuPG) 2.2.40; Copyright (C) 2022 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.
Secret key is available.
sec  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: ultimate      validity: ultimate
[ultimate] (1). Leandro Lisboa Penz <[email protected]>
[ultimate] (2)  Leandro Lisboa Penz <[email protected]>
Please select what kind of key you want:
   (3) DSA (sign only)
   (4) RSA (sign only)
   (5) Elgamal (encrypt only)
   (6) RSA (encrypt only)
  (14) Existing key from card
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 
Requested keysize is 3072 bits
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) 
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y
Enter passphrase: mypassphrase
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.
sec  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa3072/7EFD8E82EA679D59
     created: 2024-05-05  expires: never       usage: S
[ultimate] (1). Leandro Lisboa Penz <[email protected]>
[ultimate] (2)  Leandro Lisboa Penz <[email protected]>

3.2 Importing the new subkey in the target system

After creating the new subkey in the master key flash drive, we have to export the pair and then import it in the host where it will be used.

$ gpg --armor --output /tmp/newkey.sec.gpg --export-secret-subkey 7EFD8E82EA679D59!
Enter passphrase: mypassphrase

After exporting the file, we unset GNUPGHOME and import the file into the default database:

$ unset GNUPGHOME
$ gpg --import /tmp/newkey.sec.gpg
gpg: key DA4203550A52BC2A: "Leandro Lisboa Penz <[email protected]>" 1 new signature
gpg: key DA4203550A52BC2A: "Leandro Lisboa Penz <[email protected]>" 1 new subkey
Enter passphrase: mypassphrase
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key DA4203550A52BC2A: secret key imported
gpg: Total number processed: 1
gpg:            new subkeys: 1
gpg:         new signatures: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1

Results:

$ gpg -k
pubring.kbx
----------------------------
pub   rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
sub   rsa3072/7EFD8E82EA679D59 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=darkstar
   Signature notation: [email protected]=github

$ gpg -K
pubring.kbx
----------------------------
sec#  rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
ssb   rsa3072/7EFD8E82EA679D59 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=darkstar
   Signature notation: [email protected]=github

We can check that we can’t add a new subkey without using cryptflash as GNUPGHOME:

$ gpg --edit-key "$masterkeyid" addkey save
gpg (GnuPG) 2.2.40; Copyright (C) 2022 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.
Secret subkeys are available.
pub  rsa3072/DA4203550A52BC2A
     created: 2024-05-05  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa3072/7EFD8E82EA679D59
     created: 2024-05-05  expires: never       usage: S
[ultimate] (1). Leandro Lisboa Penz <[email protected]>
[ultimate] (2)  Leandro Lisboa Penz <[email protected]>
Need the secret key to do this.
Key not changed so no update needed.

We can also test that the signature still works with by signing a “test” message:

$ echo "test" | gpg --clearsign
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
test
Enter passphrase: mypassphrase
-----BEGIN PGP SIGNATURE-----
iQGzBAEBCgAdFiEEYeO0iNuxBDEPbkuIfv2OgupnnVkFAmY3hecACgkQfv2Ogupn
nVlkOAv+I1NQ8tXfo9+UH3aA1T2krx0+5gHJ5nCfHwn7gHsR6k1RdrTKEv4nAZZY
e7ajqjE0HXedqToWItMFgy2PBr9Co3rVE5a6eB4kbnkxSnGjdg8sVrkHzlqHhe1i
GOr42Lwyt2kjqLAX+lhzvv9hS9mES9L9/sSept4rU92wxPNE7w6O3GLRY/F82GQ3
qj9Nl21qIIfJiX7OamX7CM8exoqkIeS3ven2wAIYEZuHvsfWLjJwD7eL4usX25AM
dijhN59pr8JWpOfjK+mxsEDH0EqvFgh5jkSr9X4DTuMQKsZLjV6BYryIj98Q0xSr
+rwXyFbs6F2o0ThsMsAQItg6WL4S6NHveKDhFOcyk3eawO+yQpEdZyW1xqa0wymm
7p5S+EYF2ErJYYEHY3Df76psIcB5Jz5VHXKi95hOiWY+n+VsZAaq++RcbmcVBg6k
oHhsi8dMtb61xuvPOSDpwIvLEyfsJ+Qqm4CWJL2WqDIubQ6cD4wCsTNoTqdXsAgS
89F4Bbbh
=llqv
-----END PGP SIGNATURE-----

4 Using the keys

4.1 Configuring git

To configure your signing key in git:

$ git config --global user.signingkey 7EFD8E82EA679D59

To sign a commit, use -S:

$ git commit -S -m 'Add a commit message here'
[main (root-commit) ca51072] Add a commit message here
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt

And we can see the signature with --show-signature:

$ git log -1 --show-signature
commit ca510721d718d8d3b1c43e1a15dd755a338da02c (HEAD -> main)
gpg: Signature made Sun May  5 13:13:12 2024 UTC
gpg:                using RSA key 61E3B488DBB104310F6E4B887EFD8E82EA679D59
gpg: Good signature from "Leandro Lisboa Penz <[email protected]>" [ultimate]
gpg:                 aka "Leandro Lisboa Penz <[email protected]>" [ultimate]
Primary key fingerprint: A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
     Subkey fingerprint: 61E3 B488 DBB1 0431 0F6E  4B88 7EFD 8E82 EA67 9D59
Author: Leandro Lisboa Penz <[email protected]>
Date:   Sun May 5 13:13:12 2024 +0000
    Add a commit message here

We can also configure git to sign all commits by default:

$ git config --global commit.gpgsign true

4.2 Setting up key validation in service providers

One need the public key to be able to validate the corresponding private key signature. To get that in a format that can be configured in most services, we use:

$ gpg --armor --export 7EFD8E82EA679D59
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQGNBGY3hdUBDADgI7KqkzOdy+uw2BnnuUC2Hanw82ErIxzZeK0viObysr+T8u9V
FKrRrbeZCWaiP2FRlKO2gCsGn5Air1sErXDoqI8vwldRasiVjgoXFFuF4gcF0O82
EaAz8tBLH2ZUVXekIb2gScTL/bpNrRJdqvreedVnzXABsoQtXXcNYNp1PMEhZBCf
zjQfzNaTZOqKfmrgZrloJJ6J7aXlzJHTSX6z1A1hYeio3vu4rK2yjaaSCWbCPVf5
gC7kBJzuT3LjiVr5RsGJToyGmRrIBPSI/soaj6/SX8pXgtmA4Q4j3Kp7M+oTzTv1
/MlMv7eFd5pJB0HuF/MBUO7K49NQ3qEm4L6+Ld0S5LTjtOhqdaMFFOfynFsYHZKB
LeXKDMjgIiFNf/Pe7ghuUD+oUhV+MSQ/JRh1VTRvGe1SFKHB7Y1wv4fdoitjRrlh
lCZ+gtYtR1RO3NwBK9uaNrA/8tuZfCnIwoX9KdVS7pf+6mDOgDJD0k46AtZXm5UX
mHR3ah3DPhfRBPsAEQEAAbQmTGVhbmRybyBMaXNib2EgUGVueiA8bGxwZW56QGdt
YWlsLmNvbT6JAc4EEwEKADgWIQSkgw/u6vnJHbsGzkPaQgNVClK8KgUCZjeF2AIb
AwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRDaQgNVClK8KnsdC/9KG9pEsHoH
3ZORvHb9/O+i21exdoVwRC+EbOSpVpI216a1WejVyMOS9iIiZuxlXbw19WsJ6yQl
Ld1/zrNwTkG1NbclUlGDlJ9GVe3/NqDu5L5AyF1kzpLsHzQzk72RXw8xi6lorSu/
lxUstbiRAw4HV1aqt9b60YrXlx++DDIoQzdxvsYmqm2fyfhqvAYeTqn6D+tvMlXh
StoU+/lrue1aBXtr6m+04X4BXuJmUTmgkkssy9BWa56ZyNTYF7HfQNlFZZ46Aewb
f7sN8vE55oQ7kB2hG4tTn3WxdZmdLDV0FDVf5P4zDWYpagDKBTL0GRj3QPlrOGIt
BHbfUQZkdm5hCCqrNBXJhdwVD05UittIpKFICbkPDuMjoHyY7wd+UA+yxyxT82Lz
PINbwVBV8gczIwA1MCVGG3SisrdDu8IZxv2RJkgPXz+Qp1cip/4Lyunvouq5n7bg
+3IJAdoZ4nG7ld6foTwXJ7MhofDWxYARiVfLrf5XS/fCWzozVG0XJay0JUxlYW5k
cm8gTGlzYm9hIFBlbnogPGxwZW56QGxwZW56Lm9yZz6JAdEEEwEKADsCGwMFCwkI
BwIGFQoJCAsCBBYCAwECHgECF4AWIQSkgw/u6vnJHbsGzkPaQgNVClK8KgUCZjeF
2AIZAQAKCRDaQgNVClK8KlawC/oCkdv4WmfD0Y/2xNbi/P28kVOZqwDEqisuE+v7
FxQlgq5l9shlDIkCTlp6OodBSmbOultaXPkoT/1O68o4YQUsV9+pYvvEVTLClKHR
miKQ3ic+yqL//3aWF9HtII2G0akZkurcjFHs908EJZJa7wwgOqi1RJEWrYFP2zG8
zuhjsbLPf3xZ++cMP22FrqcUrLb2dpc47bzti+L3SIwjkPi9AVcKusweQmmOUMMg
ZYrPIYXjxgTsz7T/trMJjrBB2K73u+5zp7zivV6PaEfE1LYDnzJzH+dPluaLDBnq
sVKN0iCsJ5GQRtXaLmAQZSpuebtdwFgIDf/P5NxOXYfsVpb4XTFO8OFASlRgDA9q
u4TEBOujLurfjmF0T6TwX3l6mBar0beyhO6izLdODzptvgIlSSSXRDOTNJnFCk9E
IfDz2iPAdEmyiFXSeztUHe7nLDTdEIKB/FSi4eQOdGe428oW/YgfVjG8MBLuh36m
t4hsfGKGuB7mF+uDHeS8qXb+w6e5AY0EZjeF2QEMAMDxNoUYtYhYUeCLfI9SSpSp
pCvneffMRm6/ZaeT0yQ+2lQPfRQxFRzAl14q6BAFwakb9tsrF+YeG3s0BvdtLC4w
FhscHQTE8ifmq6mf/wimjbyHRdwi8SLGjmkMYYrTWTd2MNGGVA6K5dBCgkvSxfJD
ZGxu8kzqFki38Udaz2j7LyDI95iKLQ+JQ9wsXWDsKN2VxVdOzo/n1zjsCKgcXS6f
L0xXeFcS4E8DOs/le/oujob1159cNPCmg0y0ogF2r/PE/U32PPr+amZ6IfZm7DW7
IczWw5VCPU66tdY4OZro43xA/zJedyeUhjrSb7a1KU6Vp4l1O+hI5KGdQlnOlwpp
VnScOxyDULRF5jcbbBR6TYHSeY7cNzB2ov4KKdEgeYHZPJZ+f2vvaWHK5Ht2cQnf
b3YpoHXyJNI7Q0bNnmIsIm5HKsxC2xKlFNYhfkUm5iGGknpdpC2POoggVVoa3yHQ
/Z8Hulj/U48DEMMRT6KfMLGJTkdvpMGl/+J2Ux3g8wARAQABiQOtBBgBCgBhFiEE
pIMP7ur5yR27Bs5D2kIDVQpSvCoFAmY3hdkgFIAAAAAAEQAGc2VydmljZUBscGVu
ei5vcmdnaXRodWIfFIAAAAAADgAIaG9zdEBscGVuei5vcmdkYXJrc3RhcgIbAgHA
CRDaQgNVClK8KsD0IAQZAQoAHRYhBGHjtIjbsQQxD25LiH79joLqZ51ZBQJmN4XZ
AAoJEH79joLqZ51Z/TML/3KRgGdAFDI0umND8oRjpBckpI6vZKtsv0GW/WA/qE7a
TixW5vlYVPXEs8TeJF+GJ8t4IFL/WtBivre9HTlxXxACMl3wE8MH4XCYRpn5psAv
mLuOPfDHIKdHXJQSpn4J1CbQQjSHbv8R+epVnqqKQ7n1P7WixyYrLEVweNmbW78M
klcTur72UYtpaykK2NkNm97NQ/tmIEBzJdUC+1WWjp4DJVT0LoB8FOq4jT4Gd/lt
rCSt2tJFYWoA+3zX8hPIbMziQWFZ9zh4tsDVPgtN54pycPXkGpsNv6qnv1oF3KAv
wXF02lKCl5+sIn6V5M1jksAcbfXkQ0ZYD61VH90p27xvjL3MfligWXZ2C5TeYOFk
5nT7UrMRsvjcyleFoyiB2MizJAryRJ9fpkuWrtIF5VJgmXIw3pI4/kNzYxTMuBxv
+SuIjBd8W1+l2ZOaIFzXrKNyGAhZFL3ltErskwqqKKh5piLuO0Ft4tXX1gwBHgCi
/V6krQYL5uSstoGTEXNAoo6qDADRBKsRa5eEgulm91EGMGYkzjdkQVEvjWFcZYWf
/NN2hnYpKiSjCK2Xu8QTvh7lNfj39PP2oMBLyqHrrxhiMZdnFlE8x/Zk+2z68N47
jkZ3JmdARclSSlaQGsPErdU6/h3Q8ajwLfthuEdWVUv9cqP49wr/ApR3/e961ehJ
K5l+Dsl4p5Cps1Ys8/kbYVNO2O4BNxu1HT6YRQR5yXjZaygpzHO5eVriFHm1CEBa
N2klbXmyknUrVVVT21qYNatM12Ys/dpxWy5ebHBfqVwyIEA8ZnLp3IOTKKvcrs4O
fwQxrC2yrxyjLjjZGiEd4RpV18lyEJdUACYsUPxknsWlE3VO1qF4GZ1ZrgSrl3N2
4LxfATG5TVTM2nqp3P89AEW55zdtolQWXBhUAIM0ZfoOIEYMPMfN+j88jAXCW9JD
4LWWbdzcmJLCRycqvWkhwrUH98kzbitwOZk/PaqPnj1mDvQ/0sveE62qT2wnOvJP
Fvd3Qg/5djNvleR1c3JLOvZtoY4=
=ebpE
-----END PGP PUBLIC KEY BLOCK-----

We then copy that to the clipboard, and paste in the settings page of the services. Here we have the instructions for some git hosting services:

5 Addendum: key list details

To list the public keys:

$ gpg -k
pubring.kbx
----------------------------
pub   rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
sub   rsa3072/7EFD8E82EA679D59 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=darkstar
   Signature notation: [email protected]=github
sub   rsa3072/55D310B2F07DA7A7 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=redplanet
   Signature notation: [email protected]=github
sub   rsa3072/D8AECDDC7AA78D49 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=redplanet
   Signature notation: [email protected]=email
sub   rsa3072/FE1CA94217DCCA64 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=brightmoon
   Signature notation: [email protected]=github
sub   rsa3072/D9DE69434F415690 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=brightmoon
   Signature notation: [email protected]=email

To list the private keys:

$ gpg -K
pubring.kbx
----------------------------
sec   rsa3072/DA4203550A52BC2A 2024-05-05 [SC]
      Key fingerprint = A483 0FEE EAF9 C91D BB06  CE43 DA42 0355 0A52 BC2A
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
ssb   rsa3072/7EFD8E82EA679D59 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=darkstar
   Signature notation: [email protected]=github
ssb   rsa3072/55D310B2F07DA7A7 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=redplanet
   Signature notation: [email protected]=github
ssb   rsa3072/D8AECDDC7AA78D49 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=redplanet
   Signature notation: [email protected]=email
ssb   rsa3072/FE1CA94217DCCA64 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=brightmoon
   Signature notation: [email protected]=github
ssb   rsa3072/D9DE69434F415690 2024-05-05 [S]
sig      N   DA4203550A52BC2A 2024-05-05  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=brightmoon
   Signature notation: [email protected]=email

In the output above:

6 References