Hi all,
I am currently working on a Go program where I need to verify GPG signatures. I have the detached armored signature and the signed message but no respective public key nor the key's id.
I have used the following files in my examples below:
On command-line I would do the following:
$ gpg --verify Release.gpg Release
gpg: Signature made Sat Jun 17 10:57:48 2017 CEST
gpg: using RSA key 8B48AD6246925553
gpg: Can't check signature: No public key
gpg: Signature made Sat Jun 17 10:57:48 2017 CEST
gpg: using RSA key 7638D0442B90D010
gpg: Can't check signature: No public key
gpg: Signature made Sat Jun 17 11:15:12 2017 CEST
gpg: using RSA key 6FB2A1C265FFB764
gpg: Can't check signature: No public key
$
$ gpg --keyserver pool.sks-keyservers.net --receive-keys 8B48AD6246925553 7638D0442B90D010 6FB2A1C265FFB764
...
gpg: no ultimately trusted keys found
gpg: Total number processed: 3
gpg: imported: 2
gpg: unchanged: 1
$
$ gpg --verify Release.gpg Release
gpg: Signature made Sat Jun 17 10:57:48 2017 CEST
gpg: using RSA key 8B48AD6246925553
gpg: Good signature from "Debian Archive Automatic Signing Key (7.0/wheezy) <ftpm...@debian.org>" [unknown]
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: A1BD 8E9D 78F7 FE5C 3E65 D8AF 8B48 AD62 4692 5553
gpg: Signature made Sat Jun 17 10:57:48 2017 CEST
gpg: using RSA key 7638D0442B90D010
gpg: Good signature from "Debian Archive Automatic Signing Key (8/jessie) <ftpm...@debian.org>" [unknown]
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: 126C 0D24 BD8A 2942 CC7D F8AC 7638 D044 2B90 D010
gpg: Signature made Sat Jun 17 11:15:12 2017 CEST
gpg: using RSA key 6FB2A1C265FFB764
gpg: Good signature from "Wheezy Stable Release Key <debian-...@lists.debian.org>" [expired]
gpg: Note: This key has expired!
Primary key fingerprint: ED6D 6527 1AAC F0FF 15D1 2303 6FB2 A1C2 65FF B764
In Go, provided that I have the key-id(s), I can:
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"golang.org/x/crypto/openpgp"
)
func main() {
keyIDs := []string{
"8B48AD6246925553",
"7638D0442B90D010",
"6FB2A1C265FFB764",
}
var keys []byte
for _, id := range keyIDs {
k, err := getPublicKey(id)
if err != nil {
log.Fatal(err)
}
keys = append(keys, k...)
}
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(keys))
if err != nil {
log.Fatal(err)
}
sig, err := os.Open("./Release.gpg")
if err != nil {
log.Fatal(err)
}
target, err := os.Open("./Release")
if err != nil {
log.Fatal(err)
}
signer, err := openpgp.CheckArmoredDetachedSignature(keyring, target, sig)
if err != nil {
log.Fatal(err)
}
fmt.Println("All good")
fmt.Println(signer)
}
func getPublicKey(stub string) ([]byte, error) {
uri := fmt.Sprintf("pool.sks-keyservers.net/pks/lookup?search=0x%s&options=mr&op=get", stub)
// try different mirrors in case of failure
resp, err := http.Get("http://" + "hkps." + uri)
if err != nil {
resp, err = http.Get("http://" + "eu." + uri)
if err != nil {
resp, err = http.Get("http://" + "na." + uri)
if err != nil {
resp, err = http.Get("http://" + uri)
}
}
}
if err != nil {
return nil, err
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if strings.Contains(strings.ToLower(string(b)), "no results found") {
return nil, fmt.Errorf("no public key found for %q", stub)
}
return b, nil
}
The problem arises when I don't have the key-id(s) to begin with. I could do a `os.Exec(...)` but I would like to do this entirely in Go (if possible) and not have to depend on GPG.
Now to my question: is there a way that I can get the public key-id from just the signature and signed message similar to how the `gpg --verify` command gives this info?