比特币入门之使用分层确定性密钥

一、概述

一旦我们开始自己管理密钥与地址,很快就会发现,备份密钥 是一件很痛苦的事情:只要生成一个新的地址,你就需要备份一次。

这是因为我们生成的密钥之间没有什么关联,你不可能从一个 密钥推导出另一个密钥。通常情况下,这不是问题。但是,如果 你的网站每天需要为成千上万的订单生成地址,就是另一回事了。 而分层确定性密钥(Hierarchical Deterministic Key)就是 为解决这一密钥管理问题而提出的解决方案:

分层(Hierarchichal)指的是密钥之间存在层级关系,从父密钥可以 生成子密钥。例如在上图中,从主密钥m可以生成第一层子密钥m/0m/1... 而从第一层的密钥又可以继续生成第二层的密钥密钥,例如m/1/0、 m/1/1...如此不断延伸,就构成了以主密钥为根节点的一颗分层密钥树了。

确定性(Deterministic)指的是,根据密钥在层级中的编号,就可以从 父密钥确定性地推导出该密钥的具体内容。例如在上图中,我们可以从主密钥 m推导出任何一个指定编号的后代密钥,例如m/1/1/3。层级密钥的确定性使得我们 只需要备份主密钥并记录后代密钥的编号就可以了。

二、生成主密钥

使用分层确定型密钥树的第一步,是首先生成层级密钥树的主密钥,下图展示 了主密钥生成的主要流程。和普通的密钥生成类似,种子数据(熵)用来增加 密钥的不可预测性:

熵经过HAMC哈希变换后,得到的512位数据拆分为两部分:主链码和主私钥。主私钥 可以继续推导出主公钥,而主链码则可以作为子密钥生成的熵,提高子密钥的 不可预测性。

在NBitcoin中,使用ExtKey类来表征层级确定密钥:

可以利用种子或者传入一个Key实例来生成层级主密钥对象,例如:

Key key = new Key();
ExtKey masterKey = new ExtKey(key);
string cc = Encoders.Hex.EncodeData(masterKey.ChainCode); //链码
string prv = Encoders.Hex.EncodeData(masterKey.PrivateKey); //私钥
string pub = masterKey.PrivateKey.PubKey.ToHex();   //公钥

不过为了便于备份层级密钥树,通常我们会选择使用助记词来生成种子, 进而推导出主密钥。例如,下面的代码将生成助记词,最后将 助记词转换为层级主密钥:

//生成并保存助记词
Mnemonic mc = new Mnemonic(Wordlist.Englisht);
File.WriteAllText("./mnemonic.txt",mc.ToString());

//载入助记词,生成主密钥
string words = File.ReadAllText("./mnemonic.txt");
Mnemonic mc2 =  new Mnemonic(words,Wordlist.Englisth);
ExtKey masterKey = mc2.DeriveExtKey("whoami"/*password*/);
using NBitcoin;
using NBitcoin.DataEncoders;
using System;
using System.IO;

namespace Newmnemonic
{
    class Program
    {
        static void Main(string[] args)
        {
            Mnemonic mnemonic = new Mnemonic(Wordlist.English);
            Console.WriteLine("mnemonic => {0}", mnemonic);
            byte[] seed = mnemonic.DeriveSeed("whoami");
            Console.WriteLine("seed => {0}", Encoders.Hex.EncodeData(seed));
            File.WriteAllText("../mnemonic.txt", mnemonic.ToString());

            string sentence = File.ReadAllText("../mnemonic.txt");
            mnemonic = new Mnemonic(sentence, Wordlist.English);
            Console.WriteLine("mnenomic => {0}", mnemonic);
            ExtKey xkey = mnemonic.DeriveExtKey("whoami");
            Console.WriteLine("master private key => {0}", Encoders.Hex.EncodeData(xkey.PrivateKey.ToBytes()));
            Console.WriteLine("master public key => {0}", xkey.PrivateKey.PubKey.ToHex());
            Console.WriteLine("master chaincode => {0}", Encoders.Hex.EncodeData(xkey.ChainCode));

            Console.ReadLine();

        }
    }
}

三、派生子密钥

在层级密钥树中,使用父密钥(Parent Key)和父链码(Parent Chaincode), 就可以推导出指定序号的子密钥:

在上图中参与单向哈希运算的三个信息:父公钥、父链码和子密钥 序号一起决定了HMAC哈希的512位输出,而这512位输出的一半将作为子密钥的链码, 另一半则分别用于生成子公钥和子私钥。

在NBitcoin中,使用ExtKey实例的Derive()方法, 就可以生成指定指定编号的子密钥及链码了:

例如,下面的代码生成主密钥的7878#子密钥并显示其链码、私钥WIF和公钥:

ExtKey key7878 = masterKey.Derive(7878);
Console.WriteLine("hd-key 7878 chaincode => {0}",key7878.ChainCode);   //链码
Console.WriteLine("hd-key 7878 private key => {0}", key7878.PrivateKey);  //私钥
Console.WriteLine("hd-key 7878 public key => {0}",key7878.PrivateKey.PubKey);  //公钥

无私钥派生

值得指出的是,只需要父公钥和父链码就可以推导出指定编号的子公钥和 子链码,这意味着可以在不泄露主私钥的情况下动态生成子公钥(以及地址), 当你为网站增加比特币支付功能时,这一特性非常有意义:

ExtPubKey masterPub = masterKey.Neuter();  //剔除私钥
ExtPubKey pub7878 = masterPub.Derive(7878);
Console.WriteLine("pub key 7878 public only derivation => {0}",pub7878.PubKey); //仅公钥推导
using NBitcoin;
using NBitcoin.DataEncoders;
using System;

namespace DeriveChildkey
{
    class Program
    {
        static void Main(string[] args)
        {
            ExtKey xkeyMaster = new ExtKey();
            ExtKey xkey_38 = xkeyMaster.Derive(38);
            Console.WriteLine("child#38 prv key => {0}", Encoders.Hex.EncodeData(xkey_38.PrivateKey.ToBytes()));
            Console.WriteLine("child#38 pub key => {0}", xkey_38.PrivateKey.PubKey.ToHex());
            ExtPubKey xpkey_38 = xkey_38.Neuter();
            ExtPubKey xpkey_38_6 = xpkey_38.Derive(6);
            Console.WriteLine("child#38#6 pub key => {0}", xpkey_38_6.PubKey.ToHex());
            Console.ReadLine();

        }
    }
}

四、使用扩展密钥

在生成子密钥的过程中,最重要的两个参数,就是密钥和链码了。因此 如果在父密钥的表示当中包含这两部分信息,就可以直接使用父密钥来 生成子密钥了 —— 这就是扩展密钥/Extended Key的直观含义:

我们可以使用层级密钥对象的serializePubB58()serializePrivB58()方法将 其转换为扩展密钥形式,也可以使用层级密钥类的静态方法deserializeB58()将一个扩展 密钥恢复为层级密钥:

例如,下面的代码创建一个随机主密钥,派生7878#子密钥,然后分别 生成其扩展私钥和扩展公钥:

ExtKey masterKey = new ExtKey();  //随机生成层级主密钥
ExtKey key7878 = masterKey.Derive(7878);
BitcoinExtKey bxk7878 = new BitcoinExtKey(key7878,Network.RegTest); //扩展私钥
BitcoinExtPubKey bxpk7878 = bxk7878.Neuter(); //扩展公钥

需要指出的是,扩展密钥使用前缀区分不同的网络,因此我们也需要在生成扩展密钥 时,传入特定的网络对象:

也可以从从扩展密钥恢复出对应的层级密钥,例如

String xprv = "tprv....";
BitcoinExtKey bxk = new BitcoinExtKey(xprv,Network.RegTest); //导入扩展密钥
ExtKey key = bxk.ExtKey;  //获得层级密钥

using NBitcoin;
using NBitcoin.DataEncoders;
using System;

namespace Extendedkey
{
    class Program
    {
        static void Main(string[] args)
        {
            ExtKey xkMaster = new ExtKey();
            ExtKey xk_78 = xkMaster.Derive(78);
            Console.WriteLine("child#78 prv key => {0}", Encoders.Hex.EncodeData(xk_78.PrivateKey.ToBytes()));
            BitcoinExtKey bxk_78 = new BitcoinExtKey(xk_78, Network.RegTest);
            Console.WriteLine("child#78 extended prv key => {0}", bxk_78);
            Console.WriteLine("child#78 extended pub key => {0}", bxk_78.Neuter());

            string bxkText = bxk_78.ToString();
            BitcoinExtKey bxkRestored = new BitcoinExtKey(bxkText, Network.RegTest);
            Console.WriteLine("restored child#78 prv key => {0}", Encoders.Hex.EncodeData(bxkRestored.ExtKey.PrivateKey.ToBytes()));
            Console.ReadLine();

        }
    }
}

创建一个随机主密钥
从主密钥派生78号子密钥,导出其扩展公钥和扩展私钥并存入文件
从文件中读取扩展私钥,并将其转化为对应的层级密钥

五、使用强化派生密钥

扩展密钥同时包含了链码和密钥信息,这对于继续派生子密钥很方便, 但同时也带来了安全上的隐患。下图中展示了第N层链码和公钥及其某个 后代私钥泄漏的情况下,受影响的公钥和私钥:

解决的办法是改变密钥派生的算法,使用父私钥而不是父公钥来生成子链码 及子密钥,这样得到的子密钥被称为强化密钥(hardened key):

比特币根据子密钥序号来区分派生普通密钥还是强化密钥:当序号 小于0x80000000时,生成普通子密钥,否则生成强化子密钥。

例如,下面的代码分别生成普通子密钥和强化子密钥:

int id = 123;
ExtKey normalKey = masterKey.Derive(id);
ExtKey hardenedKey = masterKey.Derive(id,true);

显然,你需要从一个包含私钥的层级密钥才能派生强化子密钥

using NBitcoin;
using System;

namespace Hardenedkey
{
    class Program
    {
        static void Main(string[] args)
        {
            ExtKey xkey = new ExtKey();
            ExtKey normalChild = xkey.Derive(12);
            Console.WriteLine("normal child key => {0}", normalChild.IsHardened);
            ExtKey hardenedChild = xkey.Derive(12, true);
            Console.WriteLine("hardened child key => {0}", hardenedChild.IsHardened);
            Console.ReadLine();
        }
    }
}

六、路径表示法

在使用层级确定性密钥时,使用路径表示法可以方便地定位到一个远离 若干层的后代密钥。例如,在下面的图中分别表示了密钥m/1‘/1‘和 M/2/3在整个层级密钥树中的亲缘关系:

路径的各层之间使用/符号隔开,M表示主公钥,密钥序号之后使用H则表示 这是一个强化派生密钥,否则就是一个普通派生密钥。

在NBitcoin中首先使用KeyPath类静态方法ParsePath()将指定的路径字符串 解析为KeyPath实例,然后再调用Derive()方法创建密钥。例如:

KeyPath path = KeyPath.Parse("M/1H/2H/3");
ExtKey descentKey = masterKey.Derive(path);

BIP44 给出了一种五层路径划分的建议,可用于多个币种:

你可以根据自己的需求决定是否采用这一建议方案。

using NBitcoin;
using NBitcoin.DataEncoders;
using System;

namespace DeriveChildkeyPath
{
    class Program
    {
        static void Main(string[] args)
        {
            ExtKey master = new ExtKey();
            KeyPath path = KeyPath.Parse("m/44‘/0‘/0‘/0/123");
            ExtKey derived = master.Derive(path);
            Console.WriteLine("descent prv key => {0}", Encoders.Hex.EncodeData(derived.PrivateKey.ToBytes()));
            Console.WriteLine("descent pub key => {0}", derived.PrivateKey.PubKey.ToHex());
            Console.ReadLine();

        }
    }
}

原文地址:https://www.cnblogs.com/5ishare/p/10941076.html

时间: 2024-10-13 08:12:09

比特币入门之使用分层确定性密钥的相关文章

比特币入门教程

转 http://www.ruanyifeng.com/blog/2018/01/bitcoin-tutorial.html 一个署名为中本聪的人,提出了革命性的构想:让我们创造一种不受政府或其他任何人控制的货币!这个想法堪称疯狂:一串数字,背后没有任何资产支持,也没有任何人负责,你把它当作钱付给对方,怎么会有人愿意接受? 但是,狂想居然变成了现实.随后的几年,在全世界无数爱好者的支持下,比特币网络运行起来了,越来越多的人和资本参与,星星之火,终成燎原.刚刚过去的2017年,比特币迎来了爆发式的

TCP/IP入门学习---OSI分层(2)

一.会话层以上的处理 1.表示层 将数据从主机特有的格式转换为网络标准传输格式.以此使得不同环境之间的通信成为可能. 2.会话层 即决定使用哪个连接或者哪种连接方式将数据发送出去.会话层也会在数首部添加标签,这些标签记录着数据传递顺序的信息. 二.传输层以下的处理 1.传输层 这层主要就是TCP UDP所在的层,因此其作用不言而喻,即保证连接的建立和断开,数据的传输可靠性. 2.网络层 IP协议即在这一层,固这一层的主要作用就是将数据分片加上IP包头.然后将这些数据和地址信息等一起发送给下面的数

比特币相关文档备案

使用Java生成比特币钱包地址的过程 https://blog.csdn.net/slfq6of5o7ah/article/details/79991179 精通比特币-第4章密钥.地址.钱包 比特币的所有权是通过数字密钥.比特币地址和数字签名来确立的. https://blog.csdn.net/amds123/article/details/73478735 https://www.jianshu.com/p/7e4a8867a838 比特币改进协议BIP32(翻译) 题目: 分层确定性钱包

分成确定性钱包开发的代码实现(HD钱包服务)

HD Wallets的全称是Hierachical Deterministic Wallets, 对应中文是 分层确定性钱包. 这种钱包能够使用一组助记词来管理所有的账户的所有币种,在比特币的BIP32提案中提出,通过种子来生成主私钥,然后派生海量的子私钥和地址.种子很长,为了方便记录,转换为一组单词记录,这是BIP39提出的. 生成钱包地址的基本流程:1 生成一组助记词 2 助记词转化成种子(通过PBKDF2) 3 种子生成根私钥(通过HMAC-SHA512) 4 通过根私钥生成子私钥 本文的

比特币概念

比特币概念 bitcoin源码地址  https://github.com/bitcoin/bitcoin 作者:不如假如链接:https://www.jianshu.com/p/ed92cd055c40 一.快速术语检索 比特币地址:(例如:1DSrfJdB2AnWaFNgSbv3MZC2m74996JafV)由一串字符和数字组成,以阿拉伯数字“1”开头.就像别人向你的email地址发送电子邮件一样,他可以通过你的比特币地址向你发送比特币. 比特币:既可以指这种虚拟货币单位,也指比特币网络或者

BTC比特币HD钱包开发教程1|简单知识

我们今天 来看看开发BTC 的HD钱包. 我们先来看看什么是HD钱包. HD 钱包,即分层确定性钱包,我们知道数字钱包是用来保存我们的密钥和地址的,而数字货币是被记录在区块链网络中的每个区块上的.因此,如何安全方便的生成.保存和备份恢复密钥才是钱包的关键.为此,钱包已经进化了三次,从最初的非确定(随机)钱包到第二代的确定性(种子)钱包,直到现在的分层确定性钱包,钱包的更新迭代经过了三个阶段. 比特币最早的客户端(Satoshi client)就是非确定性钱包,钱包是一堆随机生成的私钥的集合. 客

比特币的发行和挖矿

概述 挖矿的用途 1. 抢夺区块打包权 2. 验证交易事务 3. 奖励发行新币 4. 广播新区块 大家约定一个规则,共同按照这个规则竞争,竞争成功谁就有数据打包权,打包完成后广播给别人,别人验证无问题就存入自己的数据文件中. 规则------->工作量证明 难度值: 一个门槛 规定一个256位的证书,作为难度为1的目标值: X00000000FFFFFFF....... 当时全网的算力,大约需要10分钟左右的运算能得到一个符合这个难度为1的值. 0号区块的难度信息 "nonce"

比特币深层技术原理浅析

摘要: 这是一篇主要针对技术小白的文章.本文尽量使用通俗易懂但又不失要义的文字,带你深入剖析当前备受追捧的比特币背后的深层技术原理,最后会简要介绍一下业界市场的现状. 这是一篇主要针对技术小白的文章.本文尽量使用通俗易懂但又不失要义的文字,带你深入剖析当前备受追捧的比特币背后的深层技术原理,最后会简要介绍一下业界市场的现状. 目前加密货币这个圈子可简要分为币圈和链圈,币圈专注于投资炒币,而链圈专注于区块链(blockchain)技术的创新发展.虽然币圈是高回报的利益驱动,但也正是他们的天使资金推

精读比特币论文

上一次读比特币论文还是几年前,在区块链大火的今天,决定仔细重读一下,并写下读后感. 本文试图以一种从宏观到微观,先静态后动态的叙述方式,描述比特币系统,文中绝大多数内容来自于中本聪的比特币论文,加上了一些自己的理解. 世界地图上看比特币 时至今日,比特币在物理上已是包含一万两千多个节点的,遍布全球的分布式系统.其中的每一个节点可能是普通的笔记本电脑.台式机.服务器.也可能是专用的"矿机",归根结底,是一台具备计算和存储能力的计算机.这些节点通过Internet连接在一起,使用TCP/I