为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
喵喔喔 2022-11-08 16:51:49
我正在尝试go使用 cosmos sdk 签署通过高清钱包的私钥生成的消息。下面是 python 中的等效实现,当提交/验证正常工作但无法使其与Go实现一起工作时,它会按预期生成签名消息/签名。非常感谢python实现的等效golang版本的任何输入。谢谢你。Python 版本使用 sha256 、 ecdsa 但使用等效的 cyrpto/ecdsa 时不会返回有效签名。
查看完整描述

1 回答

?
眼眸繁星

TA贡献1873条经验 获得超9个赞

两个代码都返回十六进制编码为私钥

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 main


import (

    "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:  dd5ffa7088c0fa4c665085bca7096a61e42ba92e7243a8ad7fbc6975a4aeea1845c6b668ebacd024fd2ca215c6cd510be7a9815528016af3a5e6f47d1cca30dd

Derivation Path:  m/44'/1022'/0'/0/0'

Private Key:  33f34dad4bc0ce9dc320863509aed43cab33a93a29752779ae0df6dbbea33e56

Public Key:  026557fe37d5cab1cc8edf474f4baff67dbb2305f1764e42d31b09f83296f5de2b

57624717f71fae8b5917cde0f82dfe6c2e2104183ba01c6a1c9f0a8e66d3303e5035b52876d833522aace232c1d231b3aeeff303cf02d1677a240102365ce71b

Signature, 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_key

verified = 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 端使用其他库(但这实际上可能不是必需的,因为非确定性和确定性变体是已建立的算法并根据上述测试)。


查看完整回答
反对 回复 2022-11-08
  • 1 回答
  • 0 关注
  • 374 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号