主頁 > 後端開發 > 如何使用golang使用ecdsa私鑰簽署訊息?

如何使用golang使用ecdsa私鑰簽署訊息?

2022-02-21 23:01:25 後端開發

我正在嘗試go使用 cosmos sdk 簽署通過高清錢包的私鑰生成的訊息。下面是 python 中的等效實作,當提交/驗證正常作業但無法使其與Go實作一起作業時,它會按預期生成簽名訊息/簽名。非常感謝python實作的等效golang版本的任何輸入。謝謝你。

Python 版本使用 sha256 、 ecdsa 但使用等效的 cyrpto/ecdsa 時不會回傳有效簽名。

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()

去(不作業)

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))
}

uj5u.com熱心網友回復:

兩個代碼都回傳十六進制編碼為私鑰

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 端使用其他庫(但這實際上可能不是必需的,因為非確定性和確定性變體是已建立的演算法并根據上述測驗)。

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/429823.html

標籤:Python 密码学 ecdsa 宇宙区块链

上一篇:從切片呼叫函式名并回傳一個值

下一篇:如何測驗使用中間件的處理程式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more