NBitcoin:加密技术 第二部分






4.94/5 (22投票s)
双因素密钥和隐身地址

引言
在我发布了关于NBitcoin的第一篇文章之后,我决定写一系列关于NBitcoin的文章,NBitcoin是.NET开发者的比特币网关。
本文将跳过内部数学细节,以便您可以了解整体概念和数据流。我将在本文末尾提供一些链接,这些链接可以比我更好地解释数学原理。
我的第一篇文章讨论了比特币工作原理的一些基础知识,以确保您能够花费您声称拥有的资金。
本文将讨论一些提高隐私和安全性的解决方案。
就隐私而言,问题的根源在于比特币交易是公开发布的。因此,如果您始终使用同一个地址,有人可以看到您有多少钱以及您接收了多少钱以及交易时间戳,从而推断出关于您的信息。
斯诺登的泄密事件,让许多人意识到了“默认隐私”的重要性。这并非坏事,因为这种行为还可以防御其他威胁。
我们在上一篇文章中探索了一种解决方案,称为HD钱包。这很好,但如果根公钥与您用于派生子密钥的ID一起泄露,那么您又会暴露。
我们将探讨**隐身地址**,以应对此类问题。
我们还将了解如何使用密码加密您的密钥,我们将此类密钥称为**加密密钥**。
而且,与上一篇文章中的HD钱包一样,我们将看到,有可能授权第三方在不向其透露您的密码或底层私钥的情况下,为您生成**加密密钥**。
如果这个第三方是您自己的支付服务器,那么即使服务器被黑客攻击,也无法访问您的私钥,从而使黑客攻击无效。(但您仍然容易受到物理和个人威胁 )
加密密钥:双因素 (BIP 38)
加密密钥有两种方式:
- 通过知道**密钥**,
- 通过知道**密码码**(如果您将流程委托给不受信任的第三方,则很有用)
通过知道您的密钥进行加密
想象一下,您拥有一个比特币密钥来取回您的资金。
通常情况下,谁拥有密钥,谁就拥有花费您资金的权利,这从根本上来说是一个因素。
但是,您可以用密码加密它,所以如果有人想花费您的资金,就必须同时拥有:您的密钥和密码。
代码示例
Key key = new Key(); //Create a new key
BitcoinSecret secret = key.GetBitcoinSecret(Network.Main);
Console.WriteLine(secret); //Will print the key in base58 check format
BitcoinEncryptedSecret encrypted = secret.Encrypt("This is my secret password");
Console.WriteLine(encrypted); //Will print the encrypted key in base58 check format
key = encrypted.GetKey("This is my secret password"); //Get the same key as before
加密过程由BIP 38定义。它通过密钥派生算法Scrypt来防止暴力破解,这会减慢任何尝试的速度。
通过知道密码码进行加密
在某些情况下,您希望允许第三方(例如支付服务器)为您生成**比特币地址**和相关的**加密密钥**……而无需向其透露**密钥或密码**。
在这种情况下,您需要给第三方一个**密码码**,第三方将为您生成密钥。(例如,每次业务交易)
在代码中
BitcoinPassphraseCode result = new BitcoinPassphraseCode("This is my secret password", Network.Main, null); //Generate a passphrase, even if the password is used in the constructor, it is not embedded in the PassphraseCode
Console.WriteLine(result); //Print passphraseCode in base 58 check format
//Give the passphrase code to the third party.
var generationResult = result.GenerateEncryptedSecret(); //Generate a new encrypted key
Console.WriteLine(generationResult.GeneratedAddress); //Print the generated address
Console.WriteLine(generationResult.EncryptedKey); //Print the BitcoinEncryptedKey
//...Give the generated address back to the owner
Key key = generationResult.EncryptedKey.GetKey("This is my secret password");
**比特币密码码**是提供给第三方的唯一信息,不包含任何敏感数据。第三方可以使用它来为您生成加密密钥,但无法解密它们。
保护您的隐私:隐身地址
如果您想要一个静态、固定的比特币地址的简洁性,同时又具有**比特币密码码**或HD密钥所能提供的隐私性,那么**隐身地址**就是为您准备的。
**隐身地址**背后的原始思想是这样的:
作为接收者,您将公开您的**支出公钥 (spending PubKey)**,并秘密保存相关的**支出密钥 (spending Key)**。
作为发送者,您将**支出公钥**与一个临时的、随机的密钥(称为**临时密钥 (Ephem Key)**)结合,生成一个**隐身公钥 (Stealth PubKey)**,从而生成一个**比特币地址**来支付。
作为接收者,您将收到**临时公钥 (Ephem PubKey)**,并将您的**支出密钥**结合以检索**隐身密钥 (Stealth Key)**,以花费发送到生成的**比特币地址**的资金。
然而,这种设计存在一个根本性问题。
没有自动方法可以将**临时公钥**从**发送者**发送给**接收者**。
解决方案是将**临时公钥**包含在发送资金到**比特币地址**的同一笔交易中。
在该交易中,将生成两个**交易输出 (TxOut)**:支付到**比特币地址**的(可花费输出)**,以及一个包含临时密钥的不可花费的**交易输出 (TxOut)**。
我们将这种不可花费的**交易输出 (TxOut)**称为**隐身元数据 (Stealth Metadata)**。我将此类**交易输出 (TxOut)**对称为**隐身支付 (Stealth Payment)**。
作为接收者,您将扫描比特币网络上所有带有此类**隐身支付 (Stealth Payment)**的交易,提取**临时公钥 (Ephem PubKey)**,然后生成**隐身密钥 (Stealth Key)**,并检查其地址是否与**可花费输出 (spendable output)**中的地址匹配。
作为接收者,您希望自动化此扫描,但是,您不想将您的**支出密钥 (Spend Key)**提供给扫描程序,因为扫描程序或黑客将能够使用它来窃取您的钱。
解决方案是引入一个**扫描密钥 (Scan Key)**,**扫描程序**和**发送者**都将使用它来生成和扫描**隐身公钥 (Stealth PubKey)**。
这样,扫描程序就可以通过扫描区块链来报告您的余额,而无需**支出密钥 (Spend Key)**。
但是,现在发送者需要两个信息才能向您付款:**扫描公钥 (Scan PubKey)** + **支出公钥 (Spend PubKey)**。这两个信息被打包在接收者公开分享的**比特币隐身地址 (BitcoinStealthAddress)**中。
现在,**发送者**想给您汇款,以下是他生成**隐身公钥 (Stealth PubKey)**的方式。(请注意,现在他需要两个密钥,来自**隐身地址 (StealthAddress)**)
作为**接收者**或**扫描程序**,您不再需要**支出密钥 (Spend Key)**来检查交易是否是给您的。
如果您想花费资金,请手动使用**支出密钥 (Spend Key)**来获取**隐身密钥 (Stealth Key)**。
这是DarkWallet交易的当前设计,也是以下NBitcoin代码中所表达的设计。
Key scan = new Key();
Key spend = new Key();
BitcoinStealthAddress address = spend.PukKey.CreateStealthAddress(scan.PubKey,Network.Main);
//The receiver publish the address on a forum or whatever....
//Sender then create payment
Key ephem = new Key(); //Optional, CreatePayment create one if not specified
StealthPayment payment = address.CreatePayment(ephem);
//In you want to include the payment to a transaction
Transaction tx = new Transaction();
payment.AddToTransaction(tx);
//Receiver receive the payment via the block chain with (address.Bitfield.GetPayments(tx))
Key key = spend.Uncover(scan,payment.Metadata.EphemKey);
//Or, if you just want the public key (equals to key.PubKey)
PubKey pubkey = spend.PubKey.UncoverReceiver(scan, payment.Metadata.EphemKey);
您可以在Linux命令行实用程序**sx**中并行重现这些步骤,以验证实现。
有一个确定性的**单元测试**。
结论
本文力求对开发者友好。底层的数学文档发布在下面的链接中。总体流程对我来说很难理解,因为我对底层的数学(椭圆曲线)不太熟悉,我希望本文能帮助其他开发人员在不迷失于数学细节的情况下掌握这个概念。
接下来,我将撰写关于比特币网络方面的文章。
底层数学和扫描程序:https://wiki.unsystem.net/index.php/DarkWallet/Stealth#Theory
命令行实用程序:http://sx.dyne.org/index.html
早期比特币关于此想法的页面:https://en.bitcoin.it/wiki/Sx/Stealth