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 關注
- 358 瀏覽
添加回答
舉報