How to query for all recipient OpenPGP keys with GnuPG?

1

I cannot find a reliable method for asking gpg the list of (public) keys used to encrypt a given file. One plausible solution doesn't work for me -- it never lists my key:

gpg --list-only --no-default-keyring --secret-keyring /dev/null $testfile

Another solution I derived from this thread is multi-step and undoubtedly has some corner-case that will show it's fragility:

(gpg -vv --list-only $testfile  2>&1 | grep 'gpg: public key' | awk '{print $NF}' | xargs -L1 gpg --list-keys --with-colons) | egrep '^pub' | awk -F: '{print $5}'

After digging further, I have another potential option that involves tricking gpg in ways that are undoubtedly fragile over time:

gpg --dry-run --pinentry-mode cancel --output /dev/null "$testfile" 2>&1

Anyone have a better solution than any of those?
Anyone know why the obvious --list-only option doesn't do the "right" thing?

In case it matters, using gpg (GnuPG) 2.1.10 on Mac OS X and gpgv (GnuPG) 2.0.26 on Debian (Jessie).

crimson-egret

Posted 2016-03-04T15:50:46.007

Reputation: 1 900

Answers

2

The probably safest way is to list the OpenPGP packets. You can either use GnuPG's --list-packets command, or the separate tool pgpdump. You will only get subkeys; the primary keys are not referenced in the encrypted message. If you want to query for primary keys, you'll have to resolve them through the local keyring (or in case they're missing, a key server) separately.

For a document encrypted to two subkeys of mine, output looks like this (--list-only added so GnuPG does not query for the passphrase if available):

$ gpg2 --list-only --list-packets < encrypted.gpg
# off=0 ctb=85 tag=1 hlen=3 plen=524
:pubkey enc packet: version 3, algo 1, keyid CC73B287A4388025
    data: [4090 bits]
# off=527 ctb=85 tag=1 hlen=3 plen=268
:pubkey enc packet: version 3, algo 1, keyid 5C88F5D83E2554DF
    data: [2048 bits]
# off=798 ctb=d2 tag=18 hlen=2 plen=63 new-ctb
:encrypted data packet:
    length: 63
    mdc_method: 2

This will list all (known) recipients. If hidden recipients are used, obviously this will not be possible:

# off=0 ctb=85 tag=1 hlen=3 plen=524
:pubkey enc packet: version 3, algo 1, keyid 0000000000000000
    data: [4093 bits]

Similar output for pgpdump:

Old: Public-Key Encrypted Session Key Packet(tag 1)(524 bytes)
    New version(3)
    Key ID - 0x0000000000000000
    Pub alg - RSA Encrypt or Sign(pub 1)
    RSA m^e mod n(4096 bits) - ...
        -> m = sym alg(1 byte) + checksum(2 bytes) + PKCS-1 block type 02
Old: Public-Key Encrypted Session Key Packet(tag 1)(268 bytes)
    New version(3)
    Key ID - 0x5C88F5D83E2554DF
    Pub alg - RSA Encrypt or Sign(pub 1)
    RSA m^e mod n(2047 bits) - ...
        -> m = sym alg(1 byte) + checksum(2 bytes) + PKCS-1 block type 02
New: Symmetrically Encrypted and MDC Packet(tag 18)(63 bytes)
    Ver 1
    Encrypted data [sym alg is specified in pub-key encrypted session key]
        (plain text + MDC SHA1(20 bytes))

Sadly neither of those have well-defined, stable output. GnuPG's --with-colon option is not available for dumping packets.


GnuPG only prints other's keys, not your own (without --list-only, it would try to use the key instead). Whether this is intended and desirable behavior might be subject of debate, passing a single -v/--verbose seems to be sufficient to also list your own keys.

The issue with your first attempt trying to "hide" your secret keys is a problem with the --no-default-keyring statement. From man gpg2:

--no-default-keyring

Do not add the default keyrings to the list of keyrings. Note that GnuPG will not operate without any keyrings, so if you use this option and do not provide alternate keyrings via --keyring or --secret-keyring, then GnuPG will still use the default public or secret keyrings.

GnuPG always reads the public keyring. Just passing an unreadable/empty secret keyring is not enough, instead only passing a public keyring is fine:

gpg2 --no-default-keyring --keyring /dev/null --list-only

Jens Erat

Posted 2016-03-04T15:50:46.007

Reputation: 14 141

Why do you think the --list-only --list-packets is more "stable" or "reliable" (long term) than the --vv --list-only option I mentioned that achieves the same thing? If so, why? Regardless, thanks for the additional alternative. – crimson-egret – 2016-03-07T15:53:10.977

--list-packets does exactly one thing, listing packets, while having at least defined semantics (although this is not valid for the syntax, which is not really defined to be stable). While indeed returning the same information, "very verbose" list-only output does not have this semantics attached, and probably is less robust to changes. A well-defined --with-colons output meant to be stable for parsing would be preferable, but sadly is not available. And there is one minor difference: --list-packets prints to STDOUT, -vv --list-only prints the packets to STDERR (amongst other output). – Jens Erat – 2016-03-07T17:02:44.923

All good points, especially the part about STDERR. Sadly, I still cannot come up with a rationale why the simpler --list-only (only) would not list myself. – crimson-egret – 2016-03-08T02:15:45.613

I extended my answer with some words on the issue you have/had with your first attempt. – Jens Erat – 2016-03-08T14:47:24.980

Great additions. From all of this, I've got a recipe that uses the --list-packets for my purpose. – crimson-egret – 2016-03-08T21:23:52.437