Signature verification using go.crypto / openpgp

I have a binary file:

foo.bin 

This file was signed using the gpg key to create:

 foo.bin.sig 

I have a file containing the public key that was used to sign the binary.

What I would like to do is verify this signature with Go.

I read go.crypto / openpgp docs and they are not particularly useful for this use case.

Verification will be performed on the remote machine. Ideally, I would like to avoid using a key fob on a machine that will run this code. The public key can be trivially stored in the executable itself ... if I can decide how to perform this check.

The steps that I think I need to take are as follows:

  • Create an object representing only the public key
  • Open the binary and signature and pass it to some validation function

First of all, the question arises: how to write this verification function using only the public key?

+6
source share
1 answer

The openpgp API is not the easiest to use, but I gave it a pass (pun intended) and this 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 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 check it as follows:

 $ go run testpgp.go foo.bin foo.bin.sig 

If the file you signed is very large, you can change the code a bit so as not to load it into memory.

+4
source

All Articles