Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verifying a signature using go.crypto/openpgp

Tags:

go

openpgp

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:

  • Create an Entity that represents only the public key
  • Open both the binary file and the signature and pass it to some verification function

The question primarily is: how do I write this verification function using just a public key?

like image 635
thumper Avatar asked Apr 15 '13 04:04

thumper


People also ask

How do I verify a PGP signature in Windows?

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.


1 Answers

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.

like image 120
Florent Bayle Avatar answered Sep 20 '22 06:09

Florent Bayle