助记词-公私钥-子私钥派生-钱包地址原理及实现

news/2025/3/18 1:53:29/文章来源:https://www.cnblogs.com/WZM1230/p/18778097

0x01.简介

现在各种DEX、钱包插件中的钱包导入及创建,大部分是通过助记词来备份的;

助记词是明文私钥的一种表现形式,最早由BIP39提出,为了帮助用户记住复杂的私钥;

一组助记词可以生成各个链上的公私钥,进而可以算出钱包地址;掌握了助记词,就代表掌握了该组助记词上的所有资产;

0x02.助记词的生成

1.提供一个熵源

熵源是指一个位数为n * 32的随机数,位数也被称为ENT(entropy),通常在128 ~ 256之间;

熵源的位数越多,生成的助记词数量越多,代表越安全;

// go get github.com/tyler-smith/go-bip39
entropy, _ := bip39.NewEntropy(256)
fmt.Printf("Type: %T\nValue: %v\n", entropy, entropy)
Hex := hex.EncodeToString(entropy)
fmt.Printf("Hex: %s\n", Hex)// Type: []uint8
// Value: [181 226 47 80 46 190 16 74 25 168 247 25 64 215 117 86 126 144 92 143 201 222 240 138 153 15 65 82 10 218 57 206]
// Hex: b5e22f502ebe104a19a8f71940d775567e905c8fc9def08a990f41520ada39ce

2.生成checksum

有公式:checksum = (ENT / 32) bits of SHA256(熵源)

使用128位的熵源,checksum也就是sha256的前4个bit;

hash := sha256.Sum256(entropy)
// 256bit / 32 = 8bit,第一个字节的前8bit
checksum := hash[0]
fmt.Printf("Checksum: %x\n", checksum)// Checksum: 64

3.分组生成助记词

将checksum添加到熵源的末尾,组成一个新的数字,之后将该数字每11位一组进行分组,作为单词表的索引,之后分别将表中的单词拼接即可;

entropyWithChecksum := append(entropy, checksum)
fmt.Printf("Type: %T\nValue: %v\n", entropyWithChecksum, entropyWithChecksum)
Hex = hex.EncodeToString(entropyWithChecksum)
fmt.Printf("Hex: %s\n", Hex)
mnemonic, _ := bip39.NewMnemonic(entropy)
fmt.Printf("Mnemonic: %s\n", mnemonic)// Type: []uint8
// Value: [181 226 47 80 46 190 16 74 25 168 247 25 64 215 117 86 126 144 92 143 201 222 240 138 153 15 65 82 10 218 57 206 100]
// Hex: b5e22f502ebe104a19a8f71940d775567e905c8fc9def08a990f41520ada39ce64
// Mnemonic: remove badge staff frost three celery grit bus bone allow tail provide trouble comic dish design vacuum feel duck live camera home transfer smart

此处上面的前3个byte的二进制为:1011 0101 1110 0010 0010 1111,能分成2个11位,一个是1011 0101 111-1455,另一个是0 0010 0010 11-139

此时查看单词表中这两个位置:

wordList := bip39.GetWordList()
fmt.Printf("1455: %s\n139: %s\n", wordList[1455], wordList[139])// 1455: remove
// 139: badge

与上面对应;

0x03.助记词生成公私钥

1.PBKDF2函数得到种子

助记词表示的是一个长度为128 ~ 256位的熵,之后通过密钥延伸函数PBKDF2导出一个512位的种子;

其基本原理是,通过一个伪随机函数,比如HMAC函数,将明文和一个盐值作为输入,然后重复进行运算,最终产生密钥;

重复的次数只要足够的大,破解的成本就会很高;并且,添加的盐值也会增加攻击的难度;

image-20250317234637780

// 生成熵源
entropy, _ := bip39.NewEntropy(128)
// 生成助记词
mnemonic, _ := bip39.NewMnemonic(entropy)
// 生成种子
seed := bip39.NewSeed(mnemonic, "WZM")
fmt.Printf("Seed: %x\n", seed)// NewSeed creates a hashed seed output given a provided string and password.
// No checking is performed to validate that the string provided is a valid mnemonic.
// func NewSeed(mnemonic string, password string) []byte {
// 	return pbkdf2.Key([]byte(mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New)
// }

2.种子生成主密钥、链码、公钥

种子被输入到HMAC-SHA512哈希函数中,使用固定的密钥Bitcoin seed,得到一个512位的哈希值;

其中,前256位作为主私钥,后256位作为链码,用于后续派生子密钥;

之后利用椭圆曲线加密算法,得到一个33字节的,由0x020x03开头的压缩格式;或者一个65字节的非压缩格式;

image-20250318010440335

下面的代码给出的是一个拓展公钥,版本号(4 bytes)、深度(1 byte)、父公钥指纹(4 bytes)、子密钥索引(4 bytes)、链码(32 bytes)、公钥(33 bytes);

// 生成熵源
entropy, _ := bip39.NewEntropy(128)
// 生成助记词
mnemonic, _ := bip39.NewMnemonic(entropy)
// 生成种子
seed := bip39.NewSeed(mnemonic, "WZM")
fmt.Printf("Seed: %x\n", seed)
// 生成主密钥
// go get github.com/tyler-smith/go-bip32
masterKey, _ := bip32.NewMasterKey(seed)
fmt.Printf("master key: %s\n", masterKey.String())
fmt.Printf("master key: %x\n", masterKey)
// 版本
fmt.Printf("Version: %x\n", masterKey.Version)
// 链码
fmt.Printf("Chain Code: %x\n", masterKey.ChainCode)
// 私钥
fmt.Printf("Key: %x\n", masterKey.Key)
// 拓展公钥
fmt.Printf("PublicKey: %x\n", masterKey.PublicKey())

3.主密钥派生出子密钥

主密钥、链码、索引合并在一起,并且同样是使用HMAC-SHA512哈希函数,得到的前256位为子密钥,后256位为子链码;

一开始,索引是被设为0来产生主密钥的第0个子密钥;

image-20250318010427486

0x04.子公私钥生成钱包地址

通过相应的派生逻辑得到子私钥后,按照各自的规则就可以生成地址;

1.BTC

Bitcoin的派生逻辑是:m/44'/0'/0'/0/0,其中:

m:主节点

44':表示BIP44标准

0':币种,BTC是0

0'/0/0:表示账户、外部链、地址索引

package mainimport ("fmt""github.com/btcsuite/btcd/btcutil""github.com/btcsuite/btcd/chaincfg""github.com/tyler-smith/go-bip32""github.com/tyler-smith/go-bip39"
)func main() {// 生成熵源entropy, _ := bip39.NewEntropy(128)// 生成助记词mnemonic, _ := bip39.NewMnemonic(entropy)// 生成种子seed := bip39.NewSeed(mnemonic, "WZM")// 生成主密钥masterKey, _ := bip32.NewMasterKey(seed)// 派生出Bitcoin的子私钥derivePath := []uint32{bip32.FirstHardenedChild + 44, // 44'bip32.FirstHardenedChild + 0,  // 0' (BTC)bip32.FirstHardenedChild + 0,  // 0' (账户)0,                             // 0 (外部链,即接收地址)0,                             // 0 (第一个地址)}currentKey := masterKeyfor _, index := range derivePath {currentKey, _ = currentKey.NewChildKey(index)}derivedPrivateKey := currentKeyfmt.Printf("Derived Bitcoin private key(hex): %x\n", derivedPrivateKey)fmt.Printf("Derived Bitcoin private key(string): %s\n", derivedPrivateKey)// 私钥生成公钥(压缩格式)publicKey := derivedPrivateKey.PublicKey().Keyfmt.Printf("Bitcoin Public key(hex): %x\n", publicKey)fmt.Printf("Bitcoin Public key(string): %s\n", publicKey)// 生成BTC地址// 1. PublicKey Hash = RIPEMD160(sha256(PublicKey)),20字节// 2. Version = 0x00(主网),1字节// 3. CheckSum = sha256(sha256(Version + PublicKey Hash))[:4],4字节// 4. Base58(Version + PublicKey Hash + CheckSum)// go get github.com/btcsuite/btcdpubKey, _ := btcutil.NewAddressPubKey(publicKey, &chaincfg.MainNetParams)address := pubKey.AddressPubKeyHash()fmt.Printf("Address (string): %s\n", address)
}

2.ETH

ETH的派生逻辑:m/44'/60'/0'/0/0

package mainimport ("crypto/ecdsa""fmt""github.com/ethereum/go-ethereum/crypto""github.com/tyler-smith/go-bip32""github.com/tyler-smith/go-bip39"
)func main() {// 生成熵源entropy, _ := bip39.NewEntropy(128)// 生成助记词mnemonic, _ := bip39.NewMnemonic(entropy)// 生成种子seed := bip39.NewSeed(mnemonic, "WZM")// 生成主密钥masterKey, _ := bip32.NewMasterKey(seed)// 派生出Ethereum的子私钥derivePath := []uint32{bip32.FirstHardenedChild + 44, // 44'bip32.FirstHardenedChild + 60, // 60' (ETH)bip32.FirstHardenedChild + 0,  // 0' (账户)0,                             // 0 (外部链,即接收地址)0,                             // 0 (第一个地址)}currentKey := masterKeyfor _, index := range derivePath {currentKey, _ = currentKey.NewChildKey(index)}derivedPrivateKey := currentKey// 转换成以太坊的ECDSA私钥// go get github.com/ethereum/go-ethereumethPrivateKey, _ := crypto.ToECDSA(derivedPrivateKey.Key)fmt.Printf("Derived Ethereum private key(hex): %x\n", ethPrivateKey)fmt.Printf("Derived Ethereum private key(string): %s\n", ethPrivateKey)// 私钥生成公钥ethPublicKey := ethPrivateKey.Public().(*ecdsa.PublicKey)publicKey := crypto.FromECDSAPub(ethPublicKey)fmt.Printf("Ethereum Public key(hex): %x\n", publicKey)fmt.Printf("Ethereum Public key(string): %s\n", publicKey)// 生成ETH地址// 1. 对公钥(去掉开头的0x04前缀)进行Keccak256哈希// 2. 取哈希的最后20字节作为地址address := crypto.PubkeyToAddress(*ethPublicKey).Hex()fmt.Printf("Ethereum address: %s\n", address)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/900725.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI 代理的未来是事件驱动的

AI 代理即将彻底改变企业运营,它们具备自主解决问题的能力、适应性工作流以及可扩展性。但真正的挑战并不是构建更好的模型。 代理需要访问数据、工具,并且能够在不同系统之间共享信息,其输出还需要能被多个服务(包括其他代理)使用。这不是一个 AI 问题,而是一个基础设施…

树莓派 3B + Bookworm:mjpg-streamer 正确安装全流程(原创)

在树莓派 OS Bookworm 版本上安装 mjpg-streamer 并非像旧版本一样简单,许多网上的教程已经过时,甚至存在错误。我在尝试过程中遇到了多个问题,例如依赖库缺失、编译失败等,但最终成功解决并搭建了 远程视频流监控系统。本教程基于 树莓派 3B,整理了一套 完整、可复现 的 …

1.匀速圆周运动

1.平面中的匀速圆周运动 例子:一个物体在半径为r的圆形路径中以恒定大小的速度s移动。 建立一个二维坐标系,物体位于平面上,圆心在原点上。物体的瞬时速度v(t)总是与其运动轨迹相切,所以物体任意时刻的速度与轨迹圆相切,并且速度的大小:$|v(t)|=s$ 下图右侧的两个三角形,…

Fiddler如何抓取HTTPS请求

如果发现fiddler只能抓取http请求,但是抓取不到HTTPS请求,看查看是不是没有勾选解密https流量入口:Tools——>Options——>HTTPS,勾选以下选框设置完成过后可以正常抓取HTTPS的请求了

愿景2025|未来已来 各地未来产业加速布局

各地2025年政府工作报告显示,从东部沿海到中西部内陆,从人工智能到低空经济,从量子科技到生物制造,新兴产业和未来产业的布局正在加速展开,这些产业不仅成为各地抢占发展新赛道的重要抓手,更是推动经济高质量发展的新增长极。

Fiddler工具无法抓取请求的几种原因

1、设置了过滤: fiddler中支持我们设置过滤条件,这样fiddler就不会抓取所有的请求,比如我们要抓取一个指定ip地址的请求,就可以设置对应的过滤信息,但是结束过后可能忘记删除了,导致下一次使用fiddler的时候抓不到请求。 1、首先进入Fiddler界面 2、点击Filters,如果设置…

使用 INFINI Gateway 保护 Elasticsearch 集群之修改查询不合理参数(二)

本文将探讨如何使用 INFINI Gateway 修改查询不合理的参数,此方法同样适用于 Opensearch 和 Easysearch 。 在之前的文章中,我们介绍了如何使用 request_body_json_set 处理器修改不合理的查询参数,本篇将继续探讨如何使用 request_body_regex_replace 处理器修改不合理参数…

11判断

C 语言把任何非零和非空的值假定为 true,把零或 null 假定为 false。判断语句语句 描述if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。if...else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。嵌套 if 语句 您可以在一个…

2.4G 5G 频率 Wi-Fi 信道 All In One

2.4G & 5G 频率 Wi-Fi 信道 All In One2.4G & 5G 频率 Wi-Fi 信道 All In One demos荣耀路由 XD28Wi-Fi 信道:以无线信号作为传输媒体的数据信号传送通道,若选“自适应”,则路由器会根据周围环境选择一个最好的信道。 模式:设置路由器的无线工作模式。2.4G Wi-Fi 推…

win系统部署deepseek、ollama,修改模型路径

安装ollama 1、ollama官网下载对应版本的安装包:https://ollama.com/download 2、ollama默认安装到C盘,如果希望自定义安装路径,可以考虑该命令:OllamaSetup.exe /DIR=路径, 比如我想安装到D:\ollama文件下,我要在D盘下创建ollama文件夹,并将Ollama的安装包放在里面,然…