I have a binary file:
foo.bin
This file has been signed using a gpg key to create:
foo.bin.sig
I have a file containing the public key that was used to sign the binary file.
What I'd like to do is to be able to verify this signature using Go.
I was reading the go.crypto/openpgp docs and they aren't particularly helpful for this use case.
The verification will be done on a remote machine. Ideally I'd like to avoid using the keyring on the machine that will run this code. The public key can trivially be stored in the executable itself... if I can work out how to get this verification done.
The steps that I think I need to do are as follows:
The question primarily is: how do I write this verification function using just a public key?
OpenPGP signatures Since 2021 the signatures are created by one of the official GnuPG release keys (aka certificates) they can be obtained from the GnuPG Homepage or downloaded from public keyservers. Checking the signature is best done via the File Explorer: Right click on the file and use GpgEX options -> verify.
The openpgp API is not the most straightforward to use, but I gave it a go (pun intended), and here is what I came up with :
package main
import (
"bytes"
"code.google.com/p/go.crypto/openpgp/packet"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"os"
)
// gpg --export YOURKEYID --export-options export-minimal,no-export-attributes | hexdump /dev/stdin -v -e '/1 "%02X"'
var publicKeyHex string = "99[VERY LONG HEX STRING]B6"
func main() {
if len(os.Args) != 3 {
fmt.Println("Usage: " + os.Args[0] + " <file> <signature file>")
return
}
err := checkSig(os.Args[1], os.Args[2])
if err != nil {
fmt.Println("Invalid signature : ")
fmt.Println(err)
} else {
fmt.Println("Valid signature")
}
}
func checkSig(fileName string, sigFileName string) error {
// First, get the content of the file we have signed
fileContent, err := ioutil.ReadFile(fileName)
if err != nil {
return err
}
// Get a Reader for the signature file
sigFile, err := os.Open(sigFileName)
if err != nil {
return err
}
defer func() {
if err := sigFile.Close(); err != nil {
panic(err)
}
}()
// Read the signature file
pack, err := packet.Read(sigFile)
if err != nil {
return err
}
// Was it really a signature file ? If yes, get the Signature
signature, ok := pack.(*packet.Signature)
if !ok {
return errors.New(os.Args[2] + " is not a valid signature file.")
}
// For convenience, we have the key in hexadecimal, convert it to binary
publicKeyBin, err := hex.DecodeString(publicKeyHex)
if err != nil {
return err
}
// Read the key
pack, err = packet.Read(bytes.NewReader(publicKeyBin))
if err != nil {
return err
}
// Was it really a public key file ? If yes, get the PublicKey
publicKey, ok := pack.(*packet.PublicKey)
if !ok {
return errors.New("Invalid public key.")
}
// Get the hash method used for the signature
hash := signature.Hash.New()
// Hash the content of the file (if the file is big, that's where you have to change the code to avoid getting the whole file in memory, by reading and writting in small chunks)
_, err = hash.Write(fileContent)
if err != nil {
return err
}
// Check the signature
err = publicKey.VerifySignature(hash, signature)
if err != nil {
return err
}
return nil
}
As requested, I put the public key in the code. You can test it like that :
$ go run testpgp.go foo.bin foo.bin.sig
If the file you have signed is very big, you may want to change the code a little bit to avoid loading it in memory.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With