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 端使用其他库(但这实际上可能不是必需的,因为非确定性和确定性变体是已建立的算法并根据上述测试)。
- 1 回答
- 0 关注
- 374 浏览
添加回答
举报