眼眸繁星
两个代码都返回十六进制编码为私钥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 端使用其他库(但这实际上可能不是必需的,因为非确定性和确定性变体是已建立的算法并根据上述测试)。