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 '[email protected]^([^:]+:){4}([^,:]+).*@\[email protected]')

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: key B6778CF25DC1F8F2 marked as ultimately trusted
gpg: directory 'openpgp-revocs.d' created
gpg: revocation certificate stored as 'openpgp-revocs.d/A47CBB2599D33316C9BCEF2FB6778CF25DC1F8F2.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/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                            Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  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/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>

The B6778CF25DC1F8F2 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=B6778CF25DC1F8F2

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/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                 [ unknown] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  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.27; 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.
Secret key is available.
sec  rsa3072/B6778CF25DC1F8F2
     created: 2022-05-22  expires: never       usage: SC
     trust: ultimate      validity: ultimate
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ultimate] (2)  Leandro Lisboa Penz <[email protected]>
sec  rsa3072/B6778CF25DC1F8F2
     created: 2022-05-22  expires: never       usage: SC
     trust: ultimate      validity: ultimate
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ultimate] (2)  Leandro Lisboa Penz <[email protected]>
sec  rsa3072/B6778CF25DC1F8F2
     created: 2022-05-22  expires: never       usage: SC
     trust: ultimate      validity: ultimate
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ultimate] (2)* Leandro Lisboa Penz <[email protected]>
sec  rsa3072/B6778CF25DC1F8F2
     created: 2022-05-22  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 B6778CF25DC1F8F2, in the correct order:

$ gpg -k
pubring.kbx
----------------------------
pub   rsa3072/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>

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

$ gpg -K
pubring.kbx
----------------------------
sec   rsa3072/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  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 B6778CF25DC1F8F2: 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.27; 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.
pub  rsa3072/B6778CF25DC1F8F2
     created: 2022-05-22  expires: never       usage: SC
     trust: unknown       validity: unknown
[ unknown] (1). Leandro Lisboa Penz <[email protected]>
[ unknown] (2)  Leandro Lisboa Penz <[email protected]>
pub  rsa3072/B6778CF25DC1F8F2
     created: 2022-05-22  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/B6778CF25DC1F8F2
     created: 2022-05-22  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/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  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 [email protected], 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.27; 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.
Secret key is available.
sec  rsa3072/B6778CF25DC1F8F2
     created: 2022-05-22  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/B6778CF25DC1F8F2
     created: 2022-05-22  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa3072/198BE5B0874D646F
     created: 2022-05-22  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 198BE5B0874D646F!
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 B6778CF25DC1F8F2: "Leandro Lisboa Penz <[email protected]>" 1 new signature
gpg: key B6778CF25DC1F8F2: "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 B6778CF25DC1F8F2: 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/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
sub   rsa3072/198BE5B0874D646F 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=darkstar
   Signature notation: [email protected]=github

$ gpg -K
pubring.kbx
----------------------------
sec#  rsa3072/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
ssb   rsa3072/198BE5B0874D646F 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  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.27; 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.
Secret subkeys are available.
pub  rsa3072/B6778CF25DC1F8F2
     created: 2022-05-22  expires: never       usage: SC
     trust: ultimate      validity: ultimate
ssb  rsa3072/198BE5B0874D646F
     created: 2022-05-22  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-----
iQGzBAEBCgAdFiEEfoZtvcr6BejgewVTGYvlsIdNZG8FAmKKhygACgkQGYvlsIdN
ZG+S1gwA7JB/uE5IqGIOUMofUw7dvOd+tDIozjdZlgwXn1iGeWpn8cNylywJ/QXb
kezZJBiU1/F7QqPk2nB7kpllhC/sMch7kYVP16Yo29DM1fxTZFcneM9sZ74gqaia
ZVeklmEvNufmJZ8CtTqEubQHHtfuSAxv6C18xZNqHNoZZNOfqoj0fEWRXwnNuizn
IpSzvoxel8FqNReHUBmKzpPlHcNMypp13ym1dp6+ZKf9N1LeJksuLXWxijOUS67C
XDmUDy7eGrFF3UZE3mrAxL2R2e0jms8QfBy+thUmAe8X92hce/sNXwmvn5cFBLRG
2VYr0hFwUXEeuKL7oEhqZYzyG+ZEgX8RdqK/EZV1nkSmqbz4GERejAf73LcIm/u+
kfuIHZ9KIy/2Z/gliw3I6HMXVHox7KZ9s/cNtgkmslvZNMbE8fseRUoTmjyZSIJf
Oo2aIy8jryDKmszWyQLkUHAWECi4FTYVI0/29hLnbuzZXiQhry6GYBgGnBlGlZAf
jn6ONYNe
=v/Io
-----END PGP SIGNATURE-----

4 Using the keys

4.1 Configuring git

To configure your signing key in git:

$ git config --global user.signingkey 198BE5B0874D646F

To sign a commit, use -S:

$ git commit -S -m 'Add a commit message here'
[main (root-commit) a4a247f] 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 a4a247fabe0f7cddeb27810ba2543931d605714d (HEAD -> main)
gpg: Signature made Sun May 22 18:55:37 2022 UTC
gpg:                using RSA key 7E866DBDCAFA05E8E07B0553198BE5B0874D646F
gpg: Good signature from "Leandro Lisboa Penz <[email protected]>" [ultimate]
gpg:                 aka "Leandro Lisboa Penz <[email protected]>" [ultimate]
Primary key fingerprint: A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
     Subkey fingerprint: 7E86 6DBD CAFA 05E8 E07B  0553 198B E5B0 874D 646F
Author: Leandro Lisboa Penz <[email protected]>
Date:   Sun May 22 18:55:37 2022 +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 198BE5B0874D646F
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
tPh4953WOCH/9hpov+UqtE2oRgo=
=NgCl
-----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/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
sub   rsa3072/198BE5B0874D646F 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=darkstar
   Signature notation: [email protected]=github
sub   rsa3072/399C523BE8F2D2B1 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=redplanet
   Signature notation: [email protected]=github
sub   rsa3072/12A99A666C068A15 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=redplanet
   Signature notation: [email protected]=email
sub   rsa3072/49CB827312E72FED 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=brightmoon
   Signature notation: [email protected]=github
sub   rsa3072/F684C10E218B9E2C 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  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/B6778CF25DC1F8F2 2022-05-22 [SC]
      Key fingerprint = A47C BB25 99D3 3316 C9BC  EF2F B677 8CF2 5DC1 F8F2
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
uid                 [ultimate] Leandro Lisboa Penz <[email protected]>
sig 3        B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
ssb   rsa3072/198BE5B0874D646F 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=darkstar
   Signature notation: [email protected]=github
ssb   rsa3072/399C523BE8F2D2B1 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=redplanet
   Signature notation: [email protected]=github
ssb   rsa3072/12A99A666C068A15 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=redplanet
   Signature notation: [email protected]=email
ssb   rsa3072/49CB827312E72FED 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=brightmoon
   Signature notation: [email protected]=github
ssb   rsa3072/F684C10E218B9E2C 2022-05-22 [S]
sig      N   B6778CF25DC1F8F2 2022-05-22  Leandro Lisboa Penz <[email protected]>
   Signature notation: [email protected]=brightmoon
   Signature notation: [email protected]=email

In the output above:

6 References