英文:
Verifying a signature using go.crypto/openpgp
问题
我有一个二进制文件:
foo.bin
这个文件已经使用gpg密钥签名,创建了:
foo.bin.sig
我有一个包含用于签名二进制文件的公钥的文件。
我想做的是使用Go来验证这个签名。
我阅读了go.crypto/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?
答案1
得分: 6
openpgp API并不是最直接使用的,但我尝试了一下(用Go语言),下面是我写的代码:
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[非常长的十六进制字符串]B6"
func main() {
if len(os.Args) != 3 {
fmt.Println("用法: " + os.Args[0] + " <文件> <签名文件>")
return
}
err := checkSig(os.Args[1], os.Args[2])
if err != nil {
fmt.Println("无效的签名:")
fmt.Println(err)
} else {
fmt.Println("有效的签名")
}
}
func checkSig(fileName string, sigFileName string) error {
// 首先,获取我们签名的文件的内容
fileContent, err := ioutil.ReadFile(fileName)
if err != nil {
return err
}
// 获取签名文件的Reader
sigFile, err := os.Open(sigFileName)
if err != nil {
return err
}
defer func() {
if err := sigFile.Close(); err != nil {
panic(err)
}
}()
// 读取签名文件
pack, err := packet.Read(sigFile)
if err != nil {
return err
}
// 它真的是一个签名文件吗?如果是,获取签名
signature, ok := pack.(*packet.Signature)
if !ok {
return errors.New(os.Args[2] + " 不是一个有效的签名文件。")
}
// 为了方便起见,我们的密钥是十六进制的,将其转换为二进制
publicKeyBin, err := hex.DecodeString(publicKeyHex)
if err != nil {
return err
}
// 读取密钥
pack, err = packet.Read(bytes.NewReader(publicKeyBin))
if err != nil {
return err
}
// 它真的是一个公钥文件吗?如果是,获取公钥
publicKey, ok := pack.(*packet.PublicKey)
if !ok {
return errors.New("无效的公钥。")
}
// 获取用于签名的哈希方法
hash := signature.Hash.New()
// 对文件的内容进行哈希(如果文件很大,你可能需要改变代码,避免将整个文件加载到内存中,可以通过读取和写入小块来实现)
_, err = hash.Write(fileContent)
if err != nil {
return err
}
// 验证签名
err = publicKey.VerifySignature(hash, signature)
if err != nil {
return err
}
return nil
}
根据您的要求,我将公钥放在了代码中。
您可以这样测试它:
$ go run testpgp.go foo.bin foo.bin.sig
如果您签名的文件非常大,您可能需要稍微修改代码,以避免将其加载到内存中。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论