如何使用 golang 使用 ecdsa 私钥签署消息?

我正在尝试go使用 cosmos sdk 签署通过高清钱包的私钥生成的消息。下面是 python 中的等效实现,当提交/验证正常工作但无法使其与Go实现一起工作时,它会按预期生成签名消息/签名。非常感谢python实现的等效golang版本的任何输入。谢谢你。

Python 版本使用 sha256 、 ecdsa 但使用等效的 cyrpto/ecdsa 时不会返回有效签名。


喵喔喔
浏览 375回答 1
1回答

眼眸繁星

两个代码都返回十六进制编码为私钥33f34dad4bc0ce9dc320863509aed43cab33a93a29752779ae0df6dbbea33e56并作为压缩公钥026557fe37d5cab1cc8edf474f4baff67dbb2305f1764e42d31b09f83296f5de2b由于两个代码都提供了相同的密钥,所以问题一定出在签名上!作为用于签署 UTF8 编码的测试消息test,其 SHA256 哈希为 hex 编码9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08。备注 1:如果按照注释中的说明使用双SHA256 散列,则 SHA256 散列test将用作测试消息,而不是test. 除此之外,进一步的处理是相同的。Python 和 Go 代码目前不兼容,因为它们的签名和签名格式不同:关于签名:在 Python 代码中,传递的是散列消息。这是正确sign_digest()的,因为不散列消息(请参阅此处),因此散列消息已签名。相反,sign()在 Go 代码中对消息进行哈希处理(请参见此处),因此必须传递消息本身才能使处理在功能上与 Python 代码相同。关于签名格式:Python代码使用ASN.1/DER格式,Go代码使用IEEE P1363格式。因此,必须在 Go 代码中执行从 IEEE P1363 到 ASN.1/DER 的转换:有了这个,固定的 Go 代码是:package mainimport (    "encoding/hex"    "fmt"    "math/big"    "github.com/cosmos/cosmos-sdk/crypto/hd"    "github.com/cosmos/go-bip39"    "github.com/tendermint/tendermint/crypto/secp256k1"    //"github.com/btcsuite/btcd/btcec"    "golang.org/x/crypto/cryptobyte"    "golang.org/x/crypto/cryptobyte/asn1")func main() {    //    // Derive private and public key (this part works)    //    seed := bip39.NewSeed("blast about old claw current first paste risk involve victory edit current", "")    fmt.Println("Seed: ", hex.EncodeToString(seed)) // Seed:  dd5ffa7088c0fa4c665085bca7096a61e42ba92e7243a8ad7fbc6975a4aeea1845c6b668ebacd024fd2ca215c6cd510be7a9815528016af3a5e6f47d1cca30dd    master, ch := hd.ComputeMastersFromSeed(seed)    path := "m/44'/1022'/0'/0/0'"    priv, _ := hd.DerivePrivateKeyForPath(master, ch, path)    fmt.Println("Derivation Path: ", path)                 // Derivation Path:  m/44'/1022'/0'/0/0'    fmt.Println("Private Key: ", hex.EncodeToString(priv)) // Private Key:  33f34dad4bc0ce9dc320863509aed43cab33a93a29752779ae0df6dbbea33e56    var privKey = secp256k1.PrivKey(priv)    pubKey := privKey.PubKey()    fmt.Println("Public Key: ", hex.EncodeToString(pubKey.Bytes())) // Public Key:  026557fe37d5cab1cc8edf474f4baff67dbb2305f1764e42d31b09f83296f5de2b    //    // Sign (this part needs to be fixed)    //    data := "test"    signature, _ := privKey.Sign([]byte(data))    fmt.Println(hex.EncodeToString(signature))    rVal := new(big.Int)    rVal.SetBytes(signature[0:32])    sVal := new(big.Int)    sVal.SetBytes(signature[32:64])    var b cryptobyte.Builder    b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {        b.AddASN1BigInt(rVal)        b.AddASN1BigInt(sVal)    })    signatureDER, _ := b.Bytes()    fmt.Println("Signature, DER: ", hex.EncodeToString(signatureDER))    /*        hash, _ := hex.DecodeString("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")        // Sign without hashing        privateKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), priv)        signature, _ := privateKey.Sign(hash[:])        // Convert to ASN1/DER        rVal := new(big.Int)        rVal.SetBytes(signature.R.Bytes())        sVal := new(big.Int)        sVal.SetBytes(signature.S.Bytes())        var b cryptobyte.Builder        b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {            b.AddASN1BigInt(rVal)            b.AddASN1BigInt(sVal)        })        signatureDER, _ := b.Bytes()        fmt.Println("Signature, DER: ", hex.EncodeToString(signatureDER))    */}备注 2:如果 Go 代码中没有原始消息,而只有哈希,则需要一个不哈希的函数进行签名。该tendermint/crypto/secp256k1 包不支持这一点,但tendermint/crypto/secp256k1 在内部使用btcsuite/btcd/btcec 支持。这是在注释掉的代码中实现的。输出是:Seed:  dd5ffa7088c0fa4c665085bca7096a61e42ba92e7243a8ad7fbc6975a4aeea1845c6b668ebacd024fd2ca215c6cd510be7a9815528016af3a5e6f47d1cca30ddDerivation Path:  m/44'/1022'/0'/0/0'Private Key:  33f34dad4bc0ce9dc320863509aed43cab33a93a29752779ae0df6dbbea33e56Public Key:  026557fe37d5cab1cc8edf474f4baff67dbb2305f1764e42d31b09f83296f5de2b57624717f71fae8b5917cde0f82dfe6c2e2104183ba01c6a1c9f0a8e66d3303e5035b52876d833522aace232c1d231b3aeeff303cf02d1677a240102365ce71bSignature, DER:  3044022057624717f71fae8b5917cde0f82dfe6c2e2104183ba01c6a1c9f0a8e66d3303e02205035b52876d833522aace232c1d231b3aeeff303cf02d1677a240102365ce71b测试:由于 Python 代码会生成不确定的签名,因此无法通过比较签名进行验证。相反,一个可能的测试是使用相同的验证码检查两个代码的签名。为此,在sign()Python 代码的方法中,行return signing_key.sign_digest(  # type: ignore    digest=bytearray.fromhex(data),    sigencode=sigencode_der).hex()可以替换为from ecdsa.util import sigdecode_der signature = signing_key.sign_digest(  # from Python Code    digest=bytearray.fromhex(data),    sigencode=sigencode_der)#signature = bytes.fromhex('3044022057624717f71fae8b5917cde0f82dfe6c2e2104183ba01c6a1c9f0a8e66d3303e02205035b52876d833522aace232c1d231b3aeeff303cf02d1677a240102365ce71b') # from Go code    verifying_key = signing_key.verifying_keyverified = verifying_key.verify_digest(signature, digest=bytearray.fromhex(data), sigdecode=sigdecode_der)print(verified)return signature.hex()测试表明,Python 和 Go 代码签名均成功验证,证明使用 Go 代码生成的签名是有效的。备注 3: Python 代码生成一个非确定性签名,即即使输入数据相同,签名也是不同的。相反,Go 代码生成确定性签名,即相同输入数据的签名相同(请参见此处)。如果 Go 代码还应该生成非确定性签名,则必须在 Go 端使用其他库(但这实际上可能不是必需的,因为非确定性和确定性变体是已建立的算法并根据上述测试)。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go