NODEJS中椭圆曲线签名和验证

上文 介绍了基于任意加密货币的用户身份认证设计思路,本文承接这个思路,介绍在Nodejs中如何做基于椭圆曲线的签名和验证方法,为后续生成完整的身份认证库和Demo做准备。

我调查了两个可用的库,一个是 steem-js , 另一个是 bitsharesjs ,目前倾向于使用后者,原因本文逐步展开。为叙述方便,首先把签名和验证的概念源代码贴上

const steem = require('steem');
constbitshares= require('bitsharesjs');
const bitsharesjsws = require('bitsharesjs-ws');
const env = process.env;
const role = 'active';
// Steem 有4种角色: owner, posting, active, memo
// Bitshares 有3种角色: owner, active, memo

bitsharesjsws.ChainConfig.setChainId(
  '4018d7844c78f6a6c41c6a552b898022310fc5dec06da467ee7905a8dad512c8');
// bitshares主链, 自动设定前缀为BTS(默认为GPH)

// bitsharesjsws.ChainConfig.setPrefix('STM');
// 假如取消上面这行注释,还可以手工设定前缀,这样的话可以兼容Steem区块链。

const userName = env['username'] || 'test';
const passphrase = env['password' ] || 'the very long and stupid password';

var wifSteem = steem.auth.toWif(userName, passphrase, role);
var btsKeys = bitshares.Login.generateKeys(userName, passphrase, [role]);
// Steem的API提供wif格式的私钥,而Bitshares的库提供可以计算(签名和验证)的私钥和公钥
// Wif格式来自BitCoinWiki

console.log('private key( Wallet import format ):');
console.log('steem:', wifSteem);
console.log('bitshares:', btsKeys.privKeys[role].toWif());
// 从上面可以看出 bitshares和Steem采用相同的算法生成私钥(私钥相同)

console.log('public key:');
console.log('steem', steem.auth.wifToPublic(wifSteem));
console.log('bitshares:', btsKeys.pubKeys[role]);
// 公钥也相同,但是前缀与哪个链有关

// Signature function is not exported from steem.
const bsSignature = bitshares.Signature;

const testBuffer = new Buffer('a random buffer for sign/verify test', 'utf-8');
const testBuffer2 = new Buffer('another random buffer', 'utf-8');
var sign = bsSignature.signBuffer(testBuffer, btsKeys.privKeys[role]);

// console.log(bsSignature.verifyBuffer(signBuffer, btsKeys.pubKeys[role]));
console.log(sign.verifyBuffer(testBuffer, btsKeys.privKeys[role].public_key));
console.log(sign.verifyBuffer(testBuffer2, btsKeys.privKeys[role].public_key));
console.log(sign.verifyBuffer(testBuffer, bitshares.PublicKey
    .fromStringOrThrow(btsKeys.pubKeys[role])));
var sign2 = bsSignature.fromBuffer(sign.toBuffer());
console.log(sign2.verifyBuffer(testBuffer, btsKeys.privKeys[role].public_key));
console.log(sign2.verifyBuffer(testBuffer2, btsKeys.privKeys[role].public_key));
console.log(sign.verifyBuffer(testBuffer, bitshares.PublicKey
    .fromStringOrThrow(btsKeys.pubKeys[role])));

然后逐渐讲解。 整个代码分为两个部分,1-34行是公私钥生成;36-53行是签名和验证。

首先看公私钥生成。 前两行引用了2个库,第1行是steem, 第2行是bitsharesjs,无需多说。第3行引用了一个bitsharesjs依赖的底层库,与第10-15行有关,下文详解。第4行引用env,与第17、18行有关:可以使用环境变量设置自己的用户名和密码。第5行设定一个常数角色,第6、7行有注释。
关于角色,这里还要解释下,在bitshares和steem体系下,区块链上的广播都不涉及密码,不同的行为需要不同角色的公私钥对:私钥签名,公钥验证;或者公钥加密,私钥解密。例如active角色与转账行为有关,涉及转账的需要这对公私钥;而owner角色与账户设定有关,修改账户设定需要这对公私钥;而memo角色与备注相关,
可以在转账给他人的时候利用对方的memo公钥加密,而对方需要利用memo私钥解密。由于Steem面向内容市场,因此多了一个posting角色,用于发表文章、支持反对等等。Steem的上层(steemit网站)设定,通过用户名、密码、角色的组合,使用确定性算法生成公私钥对;而bitshares刚刚支持这种方法。

先跳过第10-15行。

第17-18行声明了两个变量,用户名和密码。第21-22行分别使用Steemjs和Bitsharesjs库生成了两对私钥,注意23-24行的注释很重要,“Steem的API提供wif格式的私钥,而Bitshares的库提供可以计算(签名和验证)的密钥,包含私钥和公钥”:这也就是本文一开始提到的我倾向于bitshares库的一个原因。“Wif格式来自BitCoinWiki”:
Wif,全称Wallet Import Format,钱包导入格式,来自 这里 。Wif的发明,完全是解决人机接口的问题:对于底层算法,私钥是一个大整数,而这个大整数不方便人们导入导出,因此用这种文本格式来记录、拷贝、导入导出;在bitshares客户端看到的私钥,以及
steemit账号设定里面看到的私钥,都是这种格式。

第26-29行分别以Wif格式输出了两个库生成的私钥,如果读者运行程序,就会发现二者相同。

第31-34行输出公钥,这里面公钥的格式是石墨烯区块链自定义的,与Wif类似,但是前缀与哪个链相关。读者运行程序可以看到这两行的输出大部分是一样的,但前缀不同。Steem公钥前缀是STM,Bitshares默认公钥前缀是GPH。程序一行不改,33行运行结果,前缀是BTS;如果将10行注释掉,可以看到33行运行结果,前缀是GPH;如果将14行注释去掉,33行运行结果,前缀是STM。
这里回过头来说明一下第10行和14行所涉及的两个API。第10行涉及一个API: bitsharesjsws.ChainConfig.setChainId,设定哪个公有链,一共包含4个,具体可查看 这里的代码 ,(不过这个列表并不包含Steem链)。设定公有链直接就会有一个副作用,同时设定了
公钥的前缀,当然也可直接设定前缀,利用的就是bitsharesjsws.ChainConfig.setPrefix 这个API,正如第14行那样。

公私钥生成的代码解析到此结束,下面阐述一下签名和验证。第36-37行,定义了一个新的常数,bsSignature,这是bitsharesjs库提供的签名接口,而Steem库并没有对外暴露类似的接口。39-40行定义了两个不相同的缓冲区,而41行利用上面生成的私钥,对第一个缓冲区做了一个签名。45-47行分别是这个签名对两个缓冲区的验证方法,运行时可以看出分别输出 true
、 false、true。其中, 45行和47行都是对签名的缓冲区进行验证,只是公钥的来源稍稍有些区别;而46行对不同的缓冲区进行验证,因此输出false。

第49行重新生成了一个签名,目的是模拟签名的序列化和反序列化过程,可以使用这种方法,序列化后将签名在网络上传输,对端反序列化得到一个签名对象,并且50-52行重新利用新得到的签名对象验证两个缓冲区,结果与45-47行完全相同。

原文链接:https://blog.xiaofuxing.name/2017/04/18/ecc_in_nodejs_introduction.html