Проверка подписи с помощью go.crypto/openpgp

У меня есть бинарный файл:

foo.bin

Этот файл был подписан с помощью ключа gpg для создания:

foo.bin.sig

У меня есть файл, содержащий открытый ключ, который использовался для подписи двоичного файла.

Что я хотел бы сделать, так это иметь возможность проверить эту подпись с помощью Go.

Я читал документы go.crypto/openpgp, и они не особенно полезны для этого варианта использования.

Проверка будет выполнена на удаленном компьютере. В идеале я бы хотел избежать использования набора ключей на машине, на которой будет выполняться этот код. Открытый ключ можно тривиально хранить в самом исполняемом файле... если я смогу решить, как выполнить эту проверку.

Шаги, которые я думаю, что мне нужно сделать, следующие:

  • Создайте объект, который представляет только открытый ключ
  • Откройте бинарный файл и подпись и передайте их какой-нибудь функции проверки

В первую очередь возникает вопрос: как мне написать эту функцию проверки, используя только открытый ключ?


person thumper    schedule 15.04.2013    source источник
comment
Мне не ясно ваше требование. Можно ли заменить проверку ключа gpg другими методами, такими как SHA256/MD5/RSA...?   -  person Reck Hou    schedule 17.04.2013
comment
Согласно википедии, подпись openpgp — это подпись хеша файла. Подпись делается с использованием rsa или dsa, а хэш может быть выполнен с использованием многих алгоритмов. Я думаю, вам нужно понять файл .sig, чтобы проверить подпись. Тогда в пакете crypto должны быть все необходимые вам методы. Если вы найдете документацию по определению файла .sig (я ее не нашел), пожалуйста, разместите ее здесь. Я бы тоже хотел это увидеть.   -  person user983716    schedule 27.12.2013


Ответы (1)


API openpgp не самый простой в использовании, но я попробовал (каламбур) и вот что у меня получилось:

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
}

Как и просили, я поместил открытый ключ в код. Вы можете протестировать это так:

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

Если файл, который вы подписали, очень большой, вы можете немного изменить код, чтобы не загружать его в память.

person Florent Bayle    schedule 30.12.2013
comment
В моем случае подпись и открытый ключ были защищены, поэтому мне пришлось использовать пакет armor для декодирования соответствующих блоков: .com/FZambia/f91ddffb1a2b776d56e1988c6048e4d8 - person Alex Emelin; 27.07.2016
comment
Отличный ответ! Действительно подробный и кодовый код - person Gabriel; 13.01.2021