如何使用 golang 使用 ecdsa 私钥签署消息?
how to sign a message with ecdsa privatekey using golang?
我正在尝试使用 cosmos sdk 在通过 hd 钱包的私钥生成的 go
中签署一条消息。下面是 python 中的等效实现,它在 submitted/verified 正常工作但无法通过 Go
实现时按预期生成签名消息/签名。非常感谢 python 实现的等效 golang 版本的任何输入。谢谢。
Python 版本使用 sha256 , ecdsa 但当使用等效 cyrpto/ecdsa 时 return 无效签名。
Python
def test_sign_message(self):
""" Tests the ability of the signer to sing message """
# Loading up the signer object to use for the operation
signer: TestSigners = TestSigners.from_mnemonic("blast about old claw current first paste risk involve victory edit current")
sample_payload_to_sign = "75628d14409a5126e6c882d05422c06f5eccaa192c082a9a5695a8e707109842'
# print("test".encode("UTF-8").hex())
s = signer.sign(sample_payload_to_sign)
print(s)
from typing import List, Tuple, Dict, Union, Any
from hdwallet.hdwallet import HDWallet
from ecdsa.util import sigencode_der
from ecdsa.curves import SECP256k1
from ecdsa.keys import SigningKey
import mnemonic
import hashlib
import ecdsa
class TestSigners():
HD_WALLET_PARAMS: Dict[str, Tuple[int, bool]] = {
"purpose": (44, True),
"coinType": (1022, True),
"account": (0, True),
"change": (0, False),
}
def __init__(
self,
seed: Union[bytes, bytearray, str]
) -> None:
""" Instantiates a new signer object from the seed phrase
Args:
seed (Union[bytes, bytearray, str]): The seed phrase used to generate the public and
private keys.
"""
self.seed: Union[bytes, bytearray] = seed if isinstance(seed, (bytes, bytearray)) else bytearray.fromhex(seed)
@classmethod
def from_mnemonic(
cls,
mnemonic_phrase: Union[str, List[str], Tuple[str]]
) -> 'Signer':
"""
Instantiates a new Signer object from the mnemonic phrase passed.
Args:
mnemonic_phrase (Union[str, :obj:`list` of :obj:`str`, :obj:`tuple` of :obj:`str`):
A string, list, or a tuple of the mnemonic phrase. If the argument is passed as an
iterable, then it will be joined with a space.
Returns:
Signer: A new signer initalized through the mnemonic phrase.
"""
# If the supplied mnemonic phrase is a list then convert it to a string
if isinstance(mnemonic_phrase, (list, tuple)):
mnemonic_string: str = " ".join(mnemonic_phrase)
else:
mnemonic_string: str = mnemonic_phrase
mnemonic_string: str = " ".join(mnemonic_phrase) if isinstance(mnemonic_phrase,
(list, tuple)) else mnemonic_phrase
return cls(mnemonic.Mnemonic.to_seed(mnemonic_string))
def public_key(
self,
index: int = 0
) -> str:
"""
Gets the public key for the signer for the specified account index
Args:
index (int): The account index to get the public keys for.
Returns:
str: A string of the public key for the wallet
"""
return str(self.hdwallet(index).public_key())
def private_key(
self,
index: int = 0
) -> str:
"""
Gets the private key for the signer for the specified account index
Args:
index (int): The account index to get the private keys for.
Returns:
str: A string of the private key for the wallet
"""
return str(self.hdwallet(index).private_key())
def hdwallet(
self,
index: int = 0
) -> HDWallet:
"""
Creates an HDWallet object suitable for the Radix blockchain with the passed account index.
Args:
index (int): The account index to create the HDWallet object for.
Returns:
HDWallet: An HD wallet object created with the Radix Parameters for a given account
index.
"""
hdwallet: HDWallet = HDWallet()
hdwallet.from_seed(seed=self.seed.hex())
for _, values_tuple in self.HD_WALLET_PARAMS.items():
value, hardened = values_tuple
hdwallet.from_index(value, hardened=hardened)
hdwallet.from_index(index, True)
return hdwallet
def sign(
self,
data: str,
index: int = 0
) -> str:
"""
Signs the given data using the private keys for the account at the specified account index.
Arguments:
data (str): A string of the data which we wish to sign.
index (int): The account index to get the private keys for.
Returns:
str: A string of the signed data
"""
signing_key: SigningKey = ecdsa.SigningKey.from_string( # type: ignore
string=bytearray.fromhex(self.private_key(index)),
curve=SECP256k1,
hashfunc=hashlib.sha256
)
return signing_key.sign_digest( # type: ignore
digest=bytearray.fromhex(data),
sigencode=sigencode_der
).hex()
GO ( Not Working )
package main
import (
"encoding/hex"
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/go-bip39"
"github.com/decred/dcrd/bech32"
"github.com/tendermint/tendermint/crypto/secp256k1"
)
func main() {
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, err := hd.DerivePrivateKeyForPath(master, ch, path)
if err != nil {
t.Fatal(err)
}
fmt.Println("Derivation Path: ", path) // Derivation Path: m/44'/118'/0'/0/0'
fmt.Println("Private Key: ", hex.EncodeToString(priv)) // Private Key: 69668f2378b43009b16b5c6eb5e405d9224ca2a326a65a17919e567105fa4e5a
var privKey = secp256k1.PrivKey(priv)
pubKey := privKey.PubKey()
fmt.Println("Public Key: ", hex.EncodeToString(pubKey.Bytes())) // Public Key: 03de79435cbc8a799efc24cdce7d3b180fb014d5f19949fb8d61de3f21b9f6c1f8
//str := "test"
str := "75628d14409a5126e6c882d05422c06f5eccaa192c082a9a5695a8e707109842"
//hx := hex.EncodeToString([]byte(str))
//fmt.Println(hx)
sign, err := privKey.Sign([]byte(str))
if err != nil {
return
}
fmt.Println(hex.EncodeToString(sign))
}
两个代码return十六进制编码为私钥
33f34dad4bc0ce9dc320863509aed43cab33a93a29752779ae0df6dbbea33e56
和压缩的 public 密钥
026557fe37d5cab1cc8edf474f4baff67dbb2305f1764e42d31b09f83296f5de2b
由于两个代码提供相同的密钥,所以问题一定是签名!
作为测试消息签名使用test
的UTF8编码,其SHA256哈希是十六进制编码9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
.
备注 1: 如果如评论中所述使用 double SHA256 哈希,则 test
的 SHA256 哈希将用作测试消息而不是 test
。除此之外,进一步的处理是相同的。
Python 和 Go 代码目前不兼容,因为它们在签名和签名格式上不同:
关于签名:在Python代码中,传递散列消息。这是正确的,因为 sign_digest()
没有散列消息(参见 here),因此散列消息被签名。
相比之下,Go 代码中的 sign()
对消息进行哈希处理(参见 here),因此必须传递消息 本身 才能使处理在功能上与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代码中没有原始消息,只有hash,需要一个没有hash的函数来签名。
tendermint/crypto/secp256k1 包不支持这个,但是 tendermint/crypto/secp256k1 在内部使用 btcsuite/btcd/btcec 支持。
这是在 commented-out 代码中实现的。
输出为:
Seed: dd5ffa7088c0fa4c665085bca7096a61e42ba92e7243a8ad7fbc6975a4aeea1845c6b668ebacd024fd2ca215c6cd510be7a9815528016af3a5e6f47d1cca30dd
Derivation Path: m/44'/1022'/0'/0/0'
Private Key: 33f34dad4bc0ce9dc320863509aed43cab33a93a29752779ae0df6dbbea33e56
Public Key: 026557fe37d5cab1cc8edf474f4baff67dbb2305f1764e42d31b09f83296f5de2b
57624717f71fae8b5917cde0f82dfe6c2e2104183ba01c6a1c9f0a8e66d3303e5035b52876d833522aace232c1d231b3aeeff303cf02d1677a240102365ce71b
Signature, DER: 3044022057624717f71fae8b5917cde0f82dfe6c2e2104183ba01c6a1c9f0a8e66d3303e02205035b52876d833522aace232c1d231b3aeeff303cf02d1677a240102365ce71b
测试:
由于 Python 代码生成 non-deterministic 签名,因此无法通过比较签名进行验证。
相反,一个可能的测试是用相同验证码检查两个代码的签名。
为此,在 Python 的方法 sign()
代码中,行
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代码生成non-deterministic签名,即即使输入数据相同,签名也不同。
相反,Go 代码生成确定性签名,即签名对于相同的输入数据是相同的(参见 here)。
如果 Go 代码还应该生成 non-deterministic 签名,则必须在 Go 端使用其他库(但这实际上可能不是必需的,因为 non-deterministic 和确定性变体是建立算法并根据上述测试生成有效签名)。
我正在尝试使用 cosmos sdk 在通过 hd 钱包的私钥生成的 go
中签署一条消息。下面是 python 中的等效实现,它在 submitted/verified 正常工作但无法通过 Go
实现时按预期生成签名消息/签名。非常感谢 python 实现的等效 golang 版本的任何输入。谢谢。
Python 版本使用 sha256 , ecdsa 但当使用等效 cyrpto/ecdsa 时 return 无效签名。
Python
def test_sign_message(self):
""" Tests the ability of the signer to sing message """
# Loading up the signer object to use for the operation
signer: TestSigners = TestSigners.from_mnemonic("blast about old claw current first paste risk involve victory edit current")
sample_payload_to_sign = "75628d14409a5126e6c882d05422c06f5eccaa192c082a9a5695a8e707109842'
# print("test".encode("UTF-8").hex())
s = signer.sign(sample_payload_to_sign)
print(s)
from typing import List, Tuple, Dict, Union, Any
from hdwallet.hdwallet import HDWallet
from ecdsa.util import sigencode_der
from ecdsa.curves import SECP256k1
from ecdsa.keys import SigningKey
import mnemonic
import hashlib
import ecdsa
class TestSigners():
HD_WALLET_PARAMS: Dict[str, Tuple[int, bool]] = {
"purpose": (44, True),
"coinType": (1022, True),
"account": (0, True),
"change": (0, False),
}
def __init__(
self,
seed: Union[bytes, bytearray, str]
) -> None:
""" Instantiates a new signer object from the seed phrase
Args:
seed (Union[bytes, bytearray, str]): The seed phrase used to generate the public and
private keys.
"""
self.seed: Union[bytes, bytearray] = seed if isinstance(seed, (bytes, bytearray)) else bytearray.fromhex(seed)
@classmethod
def from_mnemonic(
cls,
mnemonic_phrase: Union[str, List[str], Tuple[str]]
) -> 'Signer':
"""
Instantiates a new Signer object from the mnemonic phrase passed.
Args:
mnemonic_phrase (Union[str, :obj:`list` of :obj:`str`, :obj:`tuple` of :obj:`str`):
A string, list, or a tuple of the mnemonic phrase. If the argument is passed as an
iterable, then it will be joined with a space.
Returns:
Signer: A new signer initalized through the mnemonic phrase.
"""
# If the supplied mnemonic phrase is a list then convert it to a string
if isinstance(mnemonic_phrase, (list, tuple)):
mnemonic_string: str = " ".join(mnemonic_phrase)
else:
mnemonic_string: str = mnemonic_phrase
mnemonic_string: str = " ".join(mnemonic_phrase) if isinstance(mnemonic_phrase,
(list, tuple)) else mnemonic_phrase
return cls(mnemonic.Mnemonic.to_seed(mnemonic_string))
def public_key(
self,
index: int = 0
) -> str:
"""
Gets the public key for the signer for the specified account index
Args:
index (int): The account index to get the public keys for.
Returns:
str: A string of the public key for the wallet
"""
return str(self.hdwallet(index).public_key())
def private_key(
self,
index: int = 0
) -> str:
"""
Gets the private key for the signer for the specified account index
Args:
index (int): The account index to get the private keys for.
Returns:
str: A string of the private key for the wallet
"""
return str(self.hdwallet(index).private_key())
def hdwallet(
self,
index: int = 0
) -> HDWallet:
"""
Creates an HDWallet object suitable for the Radix blockchain with the passed account index.
Args:
index (int): The account index to create the HDWallet object for.
Returns:
HDWallet: An HD wallet object created with the Radix Parameters for a given account
index.
"""
hdwallet: HDWallet = HDWallet()
hdwallet.from_seed(seed=self.seed.hex())
for _, values_tuple in self.HD_WALLET_PARAMS.items():
value, hardened = values_tuple
hdwallet.from_index(value, hardened=hardened)
hdwallet.from_index(index, True)
return hdwallet
def sign(
self,
data: str,
index: int = 0
) -> str:
"""
Signs the given data using the private keys for the account at the specified account index.
Arguments:
data (str): A string of the data which we wish to sign.
index (int): The account index to get the private keys for.
Returns:
str: A string of the signed data
"""
signing_key: SigningKey = ecdsa.SigningKey.from_string( # type: ignore
string=bytearray.fromhex(self.private_key(index)),
curve=SECP256k1,
hashfunc=hashlib.sha256
)
return signing_key.sign_digest( # type: ignore
digest=bytearray.fromhex(data),
sigencode=sigencode_der
).hex()
GO ( Not Working )
package main
import (
"encoding/hex"
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/go-bip39"
"github.com/decred/dcrd/bech32"
"github.com/tendermint/tendermint/crypto/secp256k1"
)
func main() {
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, err := hd.DerivePrivateKeyForPath(master, ch, path)
if err != nil {
t.Fatal(err)
}
fmt.Println("Derivation Path: ", path) // Derivation Path: m/44'/118'/0'/0/0'
fmt.Println("Private Key: ", hex.EncodeToString(priv)) // Private Key: 69668f2378b43009b16b5c6eb5e405d9224ca2a326a65a17919e567105fa4e5a
var privKey = secp256k1.PrivKey(priv)
pubKey := privKey.PubKey()
fmt.Println("Public Key: ", hex.EncodeToString(pubKey.Bytes())) // Public Key: 03de79435cbc8a799efc24cdce7d3b180fb014d5f19949fb8d61de3f21b9f6c1f8
//str := "test"
str := "75628d14409a5126e6c882d05422c06f5eccaa192c082a9a5695a8e707109842"
//hx := hex.EncodeToString([]byte(str))
//fmt.Println(hx)
sign, err := privKey.Sign([]byte(str))
if err != nil {
return
}
fmt.Println(hex.EncodeToString(sign))
}
两个代码return十六进制编码为私钥
33f34dad4bc0ce9dc320863509aed43cab33a93a29752779ae0df6dbbea33e56
和压缩的 public 密钥
026557fe37d5cab1cc8edf474f4baff67dbb2305f1764e42d31b09f83296f5de2b
由于两个代码提供相同的密钥,所以问题一定是签名!
作为测试消息签名使用test
的UTF8编码,其SHA256哈希是十六进制编码9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
.
备注 1: 如果如评论中所述使用 double SHA256 哈希,则 test
的 SHA256 哈希将用作测试消息而不是 test
。除此之外,进一步的处理是相同的。
Python 和 Go 代码目前不兼容,因为它们在签名和签名格式上不同:
关于签名:在Python代码中,传递散列消息。这是正确的,因为
sign_digest()
没有散列消息(参见 here),因此散列消息被签名。
相比之下,Go 代码中的sign()
对消息进行哈希处理(参见 here),因此必须传递消息 本身 才能使处理在功能上与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代码中没有原始消息,只有hash,需要一个没有hash的函数来签名。
tendermint/crypto/secp256k1 包不支持这个,但是 tendermint/crypto/secp256k1 在内部使用 btcsuite/btcd/btcec 支持。
这是在 commented-out 代码中实现的。
输出为:
Seed: dd5ffa7088c0fa4c665085bca7096a61e42ba92e7243a8ad7fbc6975a4aeea1845c6b668ebacd024fd2ca215c6cd510be7a9815528016af3a5e6f47d1cca30dd
Derivation Path: m/44'/1022'/0'/0/0'
Private Key: 33f34dad4bc0ce9dc320863509aed43cab33a93a29752779ae0df6dbbea33e56
Public Key: 026557fe37d5cab1cc8edf474f4baff67dbb2305f1764e42d31b09f83296f5de2b
57624717f71fae8b5917cde0f82dfe6c2e2104183ba01c6a1c9f0a8e66d3303e5035b52876d833522aace232c1d231b3aeeff303cf02d1677a240102365ce71b
Signature, DER: 3044022057624717f71fae8b5917cde0f82dfe6c2e2104183ba01c6a1c9f0a8e66d3303e02205035b52876d833522aace232c1d231b3aeeff303cf02d1677a240102365ce71b
测试:
由于 Python 代码生成 non-deterministic 签名,因此无法通过比较签名进行验证。
相反,一个可能的测试是用相同验证码检查两个代码的签名。
为此,在 Python 的方法 sign()
代码中,行
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代码生成non-deterministic签名,即即使输入数据相同,签名也不同。
相反,Go 代码生成确定性签名,即签名对于相同的输入数据是相同的(参见 here)。
如果 Go 代码还应该生成 non-deterministic 签名,则必须在 Go 端使用其他库(但这实际上可能不是必需的,因为 non-deterministic 和确定性变体是建立算法并根据上述测试生成有效签名)。