Keyrings

Public keys, encrypted messages and other OpenPGP artifacts consist of packets with a specific sequence. Certificates for instance start with a Public-Key Packet, followed by a string of packets which contain the actual content of the certificate (User IDs, subkeys, etc.), intermingled with Signature Packets which bind components to the primary key.

A stream of these packets can contain multiple artifacts. If such a stream consists only of certificates (with or without secret key material), such a stream is called a keyring.

Keyrings are a way to store a collection of certificates in a single file.

Creating a keyring

A keyring can be created by exporting certificates from the cert store:

$ sq cert export --cert $FPR_A --cert $FPR_B --cert $FPR_C ... > $FILE

Likewise sq key export ... will also create a keyring.

Listing the certificates in a keyring

A summary of certificates in a keyring can be listed by:

$ sq keyring list example_keyring.pgp
0. 159F7D1D498EF2BFC489D13FB5A86AAD248300C8 Bob
1. 722E3D4101B82C0AE80A3EF07A512226FF0D14E7 Alice
2. 8B19816EE960EEBBD09A1256CF755B2FB77C6171 Dave
3. CA0CB6126046DE2E91770FDBBBC18569610E3ACF Carol

Extracting certificates from a keyring

Certificates can be extracted from a keyring by "filtering" it. sq keyring filter ... takes a keyring as input and produces a new keyring as output containing the certificates which match the filter.

$ sq keyring filter --experimental --userid Alice example_keyring.pgp
-----BEGIN PGP PUBLIC KEY BLOCK-----

xjMEZ7Wz3hYJKwYBBAHaRw8BAQdAuhfXxD0ewpB0BrkDpMH5YhmobPkp/eGpJRxu
[...]
=adZu
-----END PGP PUBLIC KEY BLOCK-----

Please note that you have to pass --experimental as a parameter to the command, as its API isn't stable yet and might change in the future.

Filters can be combined:

$ sq keyring filter --experimental --userid Alice --userid Bob example_keyring.pgp

sq keyring filter builds the union of all supplied filters - the result contains all certificates which match at least one of the filters.

You can filter by

  • --userid
  • --name
  • --email
  • --domain - --domain applies to User IDs containing an email address.
  • --cert - matches on the fingerprint of the primary key.
  • --key - like --cert, but also matches on subkeys.

The filtering doesn't change the certificates themselves. If you want the filter to also modify the components of a certificate - dropping user ids for instance - you can pass --prune-certs to the command.

Consider a certificate containing user ids from different domains:

$ sq key generate --email alice@example.com --email alice@foo.org --own-key --without-password
[...]
      Fingerprint: 49294AF69CF035A5F1FD45FAB96A1FE8D78692A1
[...]
           UserID: <alice@example.com>
   Certifications: 1, use --certifications to list

           UserID: <alice@foo.org>
   Certifications: 1, use --certifications to list
[...]
$ sq cert export --cert 49294AF69CF035A5F1FD45FAB96A1FE8D78692A1 | sq keyring filter --experimental --domain example.com --prune-certs | sq inspect
-: OpenPGP Certificate.

      Fingerprint: 49294AF69CF035A5F1FD45FAB96A1FE8D78692A1
  Public-key algo: EdDSA
  Public-key size: 256 bits
    Creation time: 2025-05-12 20:00:42 UTC
  Expiration time: 2028-05-12 13:27:03 UTC (creation time + 2years 11months 30days 9h 16m 45s)
        Key flags: certification

           Subkey: C5B009B5025C3F10C748EB7864374DFEB4FEF8CC
  Public-key algo: EdDSA
  Public-key size: 256 bits
    Creation time: 2025-05-12 20:00:42 UTC
  Expiration time: 2028-05-12 13:27:03 UTC (creation time + 2years 11months 30days 9h 16m 45s)
        Key flags: authentication

           Subkey: 5EE2DC667CC6B3740D7030284A85C1D90AEC4E15
  Public-key algo: EdDSA
  Public-key size: 256 bits
    Creation time: 2025-05-12 20:00:42 UTC
  Expiration time: 2028-05-12 13:27:03 UTC (creation time + 2years 11months 30days 9h 16m 45s)
        Key flags: signing

           Subkey: C7F5F97A82B48A40530B08F305AE78D605641B6F
  Public-key algo: ECDH
  Public-key size: 256 bits
    Creation time: 2025-05-12 20:00:42 UTC
  Expiration time: 2028-05-12 13:27:03 UTC (creation time + 2years 11months 30days 9h 16m 45s)
        Key flags: transport encryption, data-at-rest encryption

           UserID: <alice@example.com>

The user id alice@foo.org didn't pass the filter and is removed from the certificate in the output of the command.

If you want to build the intersection of several filters, you have to chain multiple invocations sq keyring filter:

$ sq keyring filter --experimental --domain example.com example_keyring.pgp | sq keyring filter --experiment --userid Alice

sq keyring filter manipulates keyrings - it doesn't care about authenticity at all! If you want to use this command in a security sensitive context, make sure to take extra measures to assert authenticity.

Split and merge

A keyring can be split up into individual certificates by:

$ sq keyring split $FILE

Taking the above keyring:

$ mkdir out
$ cd out
$ sq keyring split ../example_keyring.pgp 
$ ls
example_keyring.pgp-0-159F7D1D498EF2BFC489D13FB5A86AAD248300C8-bob@example.com
example_keyring.pgp-1-722E3D4101B82C0AE80A3EF07A512226FF0D14E7-alice@example.com
example_keyring.pgp-2-8B19816EE960EEBBD09A1256CF755B2FB77C6171-dave@example.com
example_keyring.pgp-3-CA0CB6126046DE2E91770FDBBBC18569610E3ACF-carol@example.com
$

This operation can be reversed using sq keyring merge ...:

$ sq keyring merge *
-----BEGIN PGP PUBLIC KEY BLOCK-----

xjMEZ7Wz3hYJKwYBBAHaRw8BAQdAuhfXxD0ewpB0BrkDpMH5YhmobPkp/eGpJRxu
[...]
=MuLv
-----END PGP PUBLIC KEY BLOCK-----

or any other selection of files.

$ sq keyring merge *alice* *dave* | sq keyring list
0. 722E3D4101B82C0AE80A3EF07A512226FF0D14E7 Alice
1. 8B19816EE960EEBBD09A1256CF755B2FB77C6171 Dave