32

I have a PGP signature of a known message. However, I am not sure who signed it.

Can I get the public key - or, at least, the fingerprint/other way of searching for it on a public keyserver - just from the message and a signature?

Example: I have this message/signature from here https://futureboy.us/pgp.html

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


I vote YES on this important measure.

Alan Eliasen
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.13 (GNU/Linux)

iEYEARECAAYFAlHZCvgACgkQ5IGEtbBWdrF5HgCfc4xhT29ouAWdo1PMlyDKIfaq
pGoAoKig5sCXukrPPoKC1ZYB5CW7BzNL
=WPPL
-----END PGP SIGNATURE-----

Can I somehow find who signed it, just by looking at it?

1
  • Bear in mind that, while it may be possible to get the public key, you should still also verify the key itself (e.g. verify trusted third-party signatures on the key, or contact the owner out-of-band) before trusting the signature.
    – Iszi
    Jul 14, 2014 at 14:29

5 Answers 5

28

Yes. The format of the signature is defined in RFC 4880. If you decode the base-64 and interpret the data, you will find that the bytes from position 19 to 26 (inclusive) are the issuer ID in this case:

ID hex: E48184B5B05676B1

which matches the "Long key ID" behind your link. If you convert the ID to base 64, you can find it in the original signature data, because 18 bytes happen to divide evenly into 24 base 64 characters:

ID b64: 5IGEtbBWdrE=
Signature: iEYEARECAAYFAlHZCvgACgkQ5IGEtbBWdrF5HgCfc4xhT29ouAWdo1PMlyDKIfaq...
3
  • 1
    I've never studied the format of the signature and I didn't know this way to recover the KeyID, interesting Could you add the commandline to recover the LingKeyID? using base64 -d I get an "invalid input" error
    – ddddavidee
    Jul 2, 2014 at 9:17
  • 1
    @ddddavidee, no but I literally just did b16encode(b64decode(sig)).
    – otus
    Jul 2, 2014 at 9:31
  • 1
    FYI: I got here from google but in my test case the key ID was showing up at byte offset 24, not 19. Reading through the RFC I guess there are a couple of different versions that the "signature packet" may be encoded in. Since my application was python I ended up using pgpy which made it easy: sig = pgpy.PGPSignature(); sig.decode(asciiarmored_signature); print(sig.signer);. Oct 16, 2019 at 21:12
26

You can get all of this information from gpg if you add the -vv command-line switch. (This means extra verbose.) For example, the easiest way to get detailed information about an OpenPGP-formatted message is to simply type:

gpg -vv

And then paste the message into it (or pass a filename as an argument.) For example, pasting in the message above gives you the following detailed and interesting information:

gpg: armor: BEGIN PGP SIGNED MESSAGE
gpg: armor header: Hash: SHA1
:packet 63: length 19 - gpg control packet
gpg: armor: BEGIN PGP SIGNATURE
gpg: armor header: Version: GnuPG v1.4.13 (GNU/Linux)
:literal data packet:
    mode t (74), created 0, name="",
    raw data: unknown length
gpg: original file name=''

I vote YES on this important measure.

Alan Eliasen
:signature packet: algo 17, keyid E48184B5B05676B1
    version 4, created 1373178616, md5len 0, sigclass 0x01
    digest algo 2, begin of digest 79 1e
    hashed subpkt 2 len 4 (sig created 2013-07-07)
    subpkt 16 len 8 (issuer key ID E48184B5B05676B1)
    data: [159 bits]
    data: [160 bits]
gpg: Signature made Sun 07 Jul 2013 12:30:16 AM MDT using DSA key ID B05676B1
gpg: using PGP trust model
gpg: key 92F88CF9: accepted as trusted key
gpg: key 6C77A726: accepted as trusted key
gpg: Good signature from "Alan Eliasen (http://futureboy.homeip.net/) <[email protected]>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 402C C0D3 D527 13E3 FB7C  7103 E481 84B5 B056 76B1
gpg: textmode signature, digest algorithm SHA1

In that output, you can see the 16-hex-digit key id clearly: E48184B5B05676B1

That will let you search for the signer in a keyserver using something like:

gpg --search-keys E48184B5B05676B1

By the way, I wrote the GPG guide that you reference, and I can assure you that I signed the above message. :)

2
  • Also note the WARNING starting on the fourth-from-last line. While you can get the public key information, it's of limited value unless you can also confirm the key itself is trustworthy.
    – Iszi
    Jul 14, 2014 at 20:31
  • 1
    @lszi That's a very important point that bears repeating. In the GPG guide that I wrote (the one referenced by the original poster,) I note very strongly that unless you've validated the key with its owner, you have no guarantee at all that they actually signed it. Anyone can generate a public key for any e-mail address, and can post it to a keyserver. See this section: futureboy.us/pgp.html#ProcedureForVerification Jul 15, 2014 at 8:06
4

Yep, I actually can. With GnuPG, for example:

gpg --verify file.txt

(with the above file)

writes, at the end

Primary key fingerprint: 402C C0D3 D527 13E3 FB7C 7103 E481 84B5 B056 76B1

OpenPGP.js works too.

openpgp.cleartext.readArmored(

['-----BEGIN PGP SIGNED MESSAGE-----',
'Hash: SHA1',
'',
'',
'I vote YES on this important measure.',
'',
'Alan Eliasen',
'-----BEGIN PGP SIGNATURE-----',
'Version: GnuPG v1.4.13 (GNU/Linux)',
'',
'iEYEARECAAYFAlHZCvgACgkQ5IGEtbBWdrF5HgCfc4xhT29ouAWdo1PMlyDKIfaq',
'pGoAoKig5sCXukrPPoKC1ZYB5CW7BzNL',
'=WPPL',
'-----END PGP SIGNATURE-----']. join('\n')

).getSigningKeyIds()[0].toHex();

=>

"e48184b5b05676b1"

1

Yes, you can.

At least you can easily recover the KeyID and with that you can recover the public key from a keyserver (if the user ever uploaded it).

You can recover the KeyID using pgpdump (locally if you install it or via the website: http://www.pgpdump.net/)

For example message you posted is signed by:

Sub: issuer key ID(sub 16)(8 bytes) Key ID - 0xE48184B5B05676B1

and finally: http://pgp.mit.edu/pks/lookup?op=get&search=0xE48184B5B05676B1 to get the Key or the key details (you can also use your gnupg installation to get the data from the commandline)

1

In golang:

package main

import (
    "fmt"
    "strings"
    "golang.org/x/crypto/openpgp/armor"
    "golang.org/x/crypto/openpgp/packet"
)

func main() {
    armored := `-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.13 (GNU/Linux)

iEYEARECAAYFAlHZCvgACgkQ5IGEtbBWdrF5HgCfc4xhT29ouAWdo1PMlyDKIfaq
pGoAoKig5sCXukrPPoKC1ZYB5CW7BzNL
=WPPL
-----END PGP SIGNATURE-----
`
    block, err := armor.Decode(strings.NewReader(armored))
    fmt.Println(err, block.Type)

    reader := packet.NewReader(block.Body)
    pkt, err := reader.Next()
    fmt.Println(err)

    key, ok := pkt.(*packet.Signature)
    fmt.Println(ok)

    fmt.Printf("%X", *key.IssuerKeyId)
}

prints:

<nil> PGP SIGNATURE
<nil>
true
E48184B5B05676B1

https://play.golang.org/p/oluTm5kNQQS

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .