使用go.crypto/openpgp验证签名

huangapple go评论86阅读模式
英文:

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 (
&quot;bytes&quot;
&quot;code.google.com/p/go.crypto/openpgp/packet&quot;
&quot;encoding/hex&quot;
&quot;errors&quot;
&quot;fmt&quot;
&quot;io/ioutil&quot;
&quot;os&quot;
)
// gpg --export YOURKEYID --export-options export-minimal,no-export-attributes | hexdump /dev/stdin -v -e &#39;/1 &quot;%02X&quot;&#39;
var publicKeyHex string = &quot;99[VERY LONG HEX STRING]B6&quot;
func main() {
if len(os.Args) != 3 {
fmt.Println(&quot;Usage: &quot; + os.Args[0] + &quot; &lt;file&gt; &lt;signature file&gt;&quot;)
return
}
err := checkSig(os.Args[1], os.Args[2])
if err != nil {
fmt.Println(&quot;Invalid signature : &quot;)
fmt.Println(err)
} else {
fmt.Println(&quot;Valid signature&quot;)
}
}
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] + &quot; is not a valid signature file.&quot;)
}
// 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(&quot;Invalid public key.&quot;)
}
// Get the hash method used for the signature
hash := signature.Hash.New()
// Hash the content of the file (if the file is big, that&#39;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.

huangapple
  • 本文由 发表于 2013年4月15日 12:33:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/16007695.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定