9

使用包https://github.com/golang/crypto/tree/master/ed25519我试图获取给定私钥的公钥。

这些数据来自http://www.bittorrent.org/beps/bep_0044.html:测试2(盐可变)

问题是,当我用给定的私钥喂它时,ed25519.Public() 不会返回相同的公钥。golang 实现返回 PVK 的最后 32 个字节。但在我的测试数据中,这是出乎意料的。

这里的代码https://play.golang.org/p/UJNPCyuGQB

package main

import (
    "encoding/hex"
    "golang.org/x/crypto/ed25519"
    "log"
)

func main() {
    priv := "e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74db7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d"
    pub := "77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548"
    sig := "6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17ddf9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08"
    // d := hex.EncodeToString([]byte(priv))
    privb, _ := hex.DecodeString(priv)
    pvk := ed25519.PrivateKey(privb)
    buffer := []byte("4:salt6:foobar3:seqi1e1:v12:Hello World!")
    sigb := ed25519.Sign(pvk, buffer)
    pubb, _ := hex.DecodeString(pub)
    sigb2, _ := hex.DecodeString(sig)
    log.Println(ed25519.Verify(pubb, buffer, sigb))
    log.Printf("%x\n", pvk.Public())
    log.Printf("%x\n", sigb)
    log.Printf("%x\n", sigb2)
}

如何使用 golang 生成与 bep 相同的公钥?

4

1 回答 1

12

这是由于不同的 ed25519 私钥格式。ed25519 密钥以 32 字节种子开始。这个种子使用 SHA512 进行散列以产生 64 个字节(也翻转了几个位)。其中前 32 个字节用于生成公钥(也是 32 个字节),后 32 个字节用于生成签名。

Golang 私钥格式是与 32 字节公钥连接的 32 字节种子。您正在使用的 Bittorrent 文档中的私钥是 64 字节的散列结果(或者可能只是 64 个随机字节,其使用方式与散列结果相同)。

由于无法反转哈希,因此您无法将 Bittorrent 密钥转换为 Golang API 可以接受的格式。

您可以基于现有包生成 Golang lib 的版本。

以下代码取决于内部包golang.org/x/crypto/ed25519/internal/edwards25519,因此如果您想使用它,您需要将该包复制出来,以便您的代码可以使用它。它也非常“粗略和准备就绪”,我基本上只是从现有代码中复制了所需的代码块以使其正常工作。

请注意,公钥和签名格式是相同的,因此只要您不共享私钥,就不需要使用此代码来获得有效的实现。仅当您想检查测试向量时才需要它。

首先从私钥生成公钥:

// Generate the public key corresponding to the already hashed private
// key.
//
// This code is mostly copied from GenerateKey in the
// golang.org/x/crypto/ed25519 package, from after the SHA512
// calculation of the seed.
func getPublicKey(privateKey []byte) []byte {
    var A edwards25519.ExtendedGroupElement
    var hBytes [32]byte
    copy(hBytes[:], privateKey)
    edwards25519.GeScalarMultBase(&A, &hBytes)
    var publicKeyBytes [32]byte
    A.ToBytes(&publicKeyBytes)

    return publicKeyBytes[:]
}

接下来生成签名:

// Calculate the signature from the (pre hashed) private key, public key
// and message.
//
// This code is mostly copied from the Sign function from
// golang.org/x/crypto/ed25519, from after the SHA512 calculation of the
// seed.
func sign(privateKey, publicKey, message []byte) []byte {

    var privateKeyA [32]byte
    copy(privateKeyA[:], privateKey) // we need this in an array later
    var messageDigest, hramDigest [64]byte

    h := sha512.New()
    h.Write(privateKey[32:])
    h.Write(message)
    h.Sum(messageDigest[:0])

    var messageDigestReduced [32]byte
    edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
    var R edwards25519.ExtendedGroupElement
    edwards25519.GeScalarMultBase(&R, &messageDigestReduced)

    var encodedR [32]byte
    R.ToBytes(&encodedR)

    h.Reset()
    h.Write(encodedR[:])
    h.Write(publicKey)
    h.Write(message)
    h.Sum(hramDigest[:0])
    var hramDigestReduced [32]byte
    edwards25519.ScReduce(&hramDigestReduced, &hramDigest)

    var s [32]byte
    edwards25519.ScMulAdd(&s, &hramDigestReduced, &privateKeyA, &messageDigestReduced)

    signature := make([]byte, 64)
    copy(signature[:], encodedR[:])
    copy(signature[32:], s[:])

    return signature
}

最后我们可以使用这两个函数来演示测试向量:

privateKeyHex := "e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74db7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d"

expectedPublicKey := "77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548"
expectedSig := "6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17ddf9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08"

privateKey, _ := hex.DecodeString(privateKeyHex)
publicKey := getPublicKey(privateKey)

fmt.Printf("Calculated key: %x\n", publicKey)
fmt.Printf("Expected key:   %s\n", expectedPublicKey)
keyMatches := expectedPublicKey == hex.EncodeToString(publicKey)
fmt.Printf("Public key matches expected: %v\n", keyMatches)

buffer := []byte("4:salt6:foobar3:seqi1e1:v12:Hello World!")
calculatedSig := sign(privateKey, publicKey, buffer)

fmt.Printf("Calculated sig: %x\n", calculatedSig)
fmt.Printf("Expected sig:   %s\n", expectedSig)
sigMatches := expectedSig == hex.EncodeToString(calculatedSig)
fmt.Printf("Signature matches expected: %v\n", sigMatches)
于 2017-07-02T18:45:58.710 回答