← 返回文章

比特币学习之密码学

在比特币、以太坊等的区块链中,存在着许多密码学相关的知识,如果搞不懂的话,学习比特币将举步

比特币的密码学

在比特币、以太坊等的区块链中,存在着许多密码学相关的知识,如果搞不懂的话,学习比特币将举步维艰。

因此我们要了解以下的密码学知识:

  • Hash
  • ECC

Hash

哈希函数(Hash):是把任意长度的输入通过散列算法变换成固定长度的输出。

它的输入可以是字符串,可以是数据,可以是任何文件,经过哈希运算后,变成一个固定长度的输出,该输出就是哈希值。

哈希算法有一个很大的特点,就是你不能从结果推算出输入,所以又称为不可逆的算法。

在比特币中用于交易检验和数字签名等的 SHA-256 算法、 RIPEMD160 算法都是 Hash 函数。

  • 用 nodejs 实现 SHA-256 算法对 message 进行哈希
// 方法一:(使用 bitcoinjs-lib 包)
const bitcoin = require(\'bitcoinjs-lib\');
let message = 'a secret message!'
hash = bitcoin.crypto.sha256(message)
console.log(hash.toString('hex'))
///方法二:(使用 scrypto 包)
function ripemd160(name){
	var crypto = require('crypto');
	var hash = crypto.createHash('ripemd160');
	hash.update(name);
	return hash.digest('hex')
}
let message = 'a secret message!'
console.log(sha256(message))
  • 用 nodejs 实现 RIPEMD160 算法对 message 进行哈希
// 方法一:(使用 bitcoinjs-lib 包)
const bitcoin = require('bitcoinjs-lib');
let message = 'a secret message!'
hash = bitcoin.crypto.ripemd160(message)
console.log(hash.toString('hex'))

// 方法二:(使用 scrypto 包)
function ripemd160(name){
	var crypto = require('crypto');
	var hash = crypto.createHash('ripemd160');
	hash.update(name);
	return hash.digest('hex')
}
let message = 'a secret message!'
console.log(ripemd160(message))

ECC

椭圆曲线算法(ECC): 在密码学理论中,椭圆曲线算法是一种非对称密码,也称公钥密码。

公钥加密,可以说它现在是现代网络安全或者网络信任链的基础。公钥加密最大的特征就是通信双方各有一对公私钥。

这一对公私钥有着千丝万缕的数学关系:

  • 私钥可以推算出公钥,公钥无法推算出私钥
  • 这一对公私钥一方加密,另一方解密(公钥加密,私钥解密 或者私钥加密,公钥解密)

椭圆曲线算法的实现是非常复杂的,本文不关心如何实现,我们的重点是 ECC 可以生成一对公私钥,同时保存私钥,公开公钥。

  • 用 nodejs 实现 ECC 算法的一对公私钥
const bitcoin = require('bitcoinjs-lib');
// 随机生成秘钥
const keyPair = bitcoin.ECPair.makeRandom();
// 输出公钥
console.log(keyPair.publicKey.toString('hex'))
// 输出私钥
console.log(keyPair.privateKey.toString('hex'))

在比特币中,我们使用私钥对交易进行签名,可以确保只有持有私钥的人才能花费对用地址的资金。

接下来,我们对交易签名的原理进行简单的实现,比特币的签名只是消息的结构更加复杂一些。

原理:使用私钥进行签名,公钥进行验证,进而可以对一个消息的真伪进行确认。

  • 签名

    对消息进行签名,实际上是对消息的哈希进行签名,因为上文提到过,哈希可以将任意长度的消息转化成固定长度的哈希值。对哈希值进行签名也保证了原始消息的真伪。

    • 使用 ECDSA 通过私钥对消息 message 进行签名

      const bitcoin = require('bitcoinjs-lib');
      let message = 'a secret message!'
      // 对消息进行 SHA-256 哈希
      let hash = bitcoin.crypto.sha256(message)
      // wif 变量是 WIF 结构的私钥
      let wif = 'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617'
      // 将私钥导入
      let keyPair = bitcoin.ECPair.fromWIF(wif);
      // 用私钥对消息的哈希签名:
      let signature = keyPair.sign(hash); 
      // ECSignature对象
      // 打印签名:
      console.log(signature.toString('hex'));
      // 打印公钥以便验证签名:
      console.log(keyPair.publicKey.toString('hex'));
      // 打印的结果如下:
      //5d0b6e817e01e22ba6ab19c0ab9cdbb2dbcd0612c5b8f990431dd0634f5a9653188b989017ee7e83
      //0de581d4e0d46aa36bbe79537774d56cbe41993b3fd66686
      //02d0de0aaeaefad02b8bdc8a01a1b8b11c696bd3d66a2c5f10780d95b7df42645c
      

在得到签名、原始消息和公钥的基础上,就可以对签名验证。

  • 验证签名真伪
const bitcoin = require('bitcoinjs-lib');
let message = 'a secret message!'
let hash = bitcoin.crypto.sha256(message)
//  将上文得到签名的结果输入进来
let signature =Buffer.from(
		'5d0b6e817e01e22ba6ab19c0ab9cdbb2dbcd0612c5b8f990431dd0634f5a9653188b989017ee7e830de581d4e0d46aa36bbe79537774d56cbe41993b3fd66686',
        'hex'
     );
//  将上文得到公钥的结果输入进来
let publicKey = Buffer.from(
		'02d0de0aaeaefad02b8bdc8a01a1b8b11c696bd3d66a2c5f10780d95b7df42645c',
        'hex'
      );
pubKeyOnly = bitcoin.ECPair.fromPublicKey(publicKey);
//  通过导入公钥,以及原始消息和签名,就可以验证签名的真伪。
let result = pubKeyOnly.verify(hash, signature);
console.log('Verify result: ' + result)
//  打印结果:
//  true

当然,我们可以尝试更改原始消息或者导入的签名,运行查看结果,看是否还是 true.

上述的代码非常简单,原理也易懂。在比特币中,会比这更加复杂,对消息的验证有时候会使用公钥的地址进行验证的等等。

公钥和地址的关系是什么,私钥是如何推出公钥的,我们下节再见。

声明:本文中使用的 bitcoinjs-lib 的版本是 5.1.7

参考链接

https://www.npmjs.com/package/bitcoinjs-lib

https://www.liaoxuefeng.com/wiki/1207298049439968/1207687429747616

https://blog.csdn.net/u011583927/article/details/81432863

END