Get started uncertainty of Bitcoin hierarchical key

I. Overview

Once we start to manage their own keys and addresses, will soon find, backup keys is a very painful thing: just create a new address, you'll need to back up again.

This is because there is little correlation between the keys of our generation, you can not derive from one key to another key. Under normal circumstances, this is not a problem. However, if your site needs to generate addresses for thousands of orders per day, it is another matter. The hierarchical deterministic key (Hierarchical Deterministic Key) is to solve the key management problems and solutions:

Layered ( Hierarchichal) means that there is a hierarchical relationship between keys, the master key may be generated from sub-keys. For example, in the figure above, the master key may be generated from the first m sub-layer key m/0, m/1... and can continue to generate the key from the key of the second layer of the first layer, e.g. m/1/0m/1/1... so continue to extend, to constitute a master key as the root node of the hierarchical key tree.

Uncertainty ( Deterministic) refers, based on the key number in the hierarchy, the key uncertainties can be derived from the parent key of the specific content. For example, in the figure above, we can derive the master key, m offspring from any given number of keys, for example, m / 1/1/3. Level of uncertainty makes the key we just need to back up the master key and record the number of offspring key on it.

Second, the master key generation

The first step in determining the type using a hierarchical key tree, is to first generate a master key hierarchy key tree, the lower diagram shows the main processes of the master key generation. Common key generation and the like, seed data (entropy) for increasing the unpredictability of the key:

After hashing the entropy HAMC transformed data 512 obtained split into two parts: the main backbone and the code private key. Master private key can continue to derive the master public key, and the main chain may be used as the entropy codes subkey generation improve unpredictability subkey.

In NBitcoin, the key used to characterize the level determined ExtKey classes:

Seed can pass or a Key instance the master key to generate the object hierarchy, for example:

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

        }
    }
}
View Code

创建一个随机主密钥
从主密钥派生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();
        }
    }
}
View Code

六、路径表示法

在使用层级确定性密钥时,使用路径表示法可以方便地定位到一个远离 若干层的后代密钥。例如,在下面的图中分别表示了密钥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();

        }
    }
}

 

Guess you like

Origin www.cnblogs.com/5ishare/p/10941076.html