MOAC BlockChain数据签名及验证
飞儿506
发表于 2022-12-20 23:36:45
128
0
0
本文使用智能合约完成对签名的验证,使用chain3.js完成对数据的签名以及和智能合约的交互。
环境:
MOAC版本:nuwa1.0.6.win.zip(本文在mainnet进行);7 y" j0 u: u# [1 Z6 h4 d) V
操作系统:64位Windows 10家庭版。' I' m& z' b; g/ n% s) k
19 J }9 p- \4 r
签名
实施签名需要两个部分:待签名的数据 + 实施签名的账户。签名过程可以使用chain3.mc.sign()来实现,具体代码为:
var Chain3 = require(‘chain3’);6 q& D" [% J" Q
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));
chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000); //实施签名要求解锁账户' b- ^( u& _$ P# ^3 o' [- o
var account = chain3.mc.accounts[0];- A. p7 Y5 I: J3 o H: G
var sha3Msg = chain3.sha3(“HELLO MOAC!”);' y& b" H( X, p% [8 }- S
var signedData = chain3.mc.sign(account, sha3Msg);
console.log("account: " + account);
console.log("sha3Msg: " + sha3Msg);5 k/ z; m3 i U
console.log("Signed data: " + signedData);8 u' n* `, K! N: f5 S4 O+ d
在上面的代码中,先将要签名的数据"HELLO MOAC!"生成哈希串,使用chain3.sha3(“HELLO MOAC!”)。接着我们使用当前连接节点的第一个默认帐户进行签名。此时需要chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000)来打开数据签名所使用帐户。需要注意的是,当你打开你的帐户时,可能有安全风险。因为其它程序也可以通过访问节点进行类似的sign,这意味着,他们可以伪造你的数据,包括以你的名义发起交易,转走你的钱。
运行的结果:1 p* p! F* v& D/ l# `7 D7 f
( @3 P9 t% C3 Z( K' h& m% l
返回值Signed data总共132字节(去掉前面的’0x’的话是130字节)。在ECDSA(EC是“椭圆曲线”的简称,DSA是“数字签名算法”的简称)签名算法中,返回值可以分为三个部分:r, s, v。其中前0~66个字节为r, 66~130之间的字节为s, 130~132的字节为v。
接下来我们可以将它打印出来,在接下来验证签名的部分会用到。该部分完整的代码如下:
var Chain3 = require(‘chain3’);
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));
chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000); //实施签名要求解锁账户
var account = chain3.mc.accounts[0];
var sha3Msg = chain3.sha3(“HELLO MOAC!”);6 M" w3 \6 D; e3 {8 q
var signedData = chain3.mc.sign(account, sha3Msg);
console.log("account: " + account);: R: }5 x3 G$ A- p+ @2 m+ V
console.log("sha3Msg: " + sha3Msg);
console.log("Signed data: " + signedData);
let r = signedData.slice(0, 66);
let s = ‘0x’ + signedData.slice(66, 130); Y7 E' l. T( B. F
let v = ‘0x’ + signedData.slice(130, 132);
v = chain3.toDecimal(v);2 a3 R6 H$ |+ _/ V. B2 E
console.log();; e" H2 y4 K$ t
console.log(‘r:’, r);2 N+ _* k9 J$ p; V' w" n1 ~8 d
console.log(‘s:’, s);) T" s" P# `& m. N
console.log(‘v:’, v);
运行的结果:+ ^1 s3 V4 w7 y1 x k5 \* n
2
验证
签名完成了,我们如何验证某些签名后的数据是哪个账户签名的呢?
本例中,验证签名通过智能合约的ecrecover函数来实现。! t e9 O! r& s# o
ecrecover接收原始数据的哈希值(就是上面输出的sha3Msg)以及r/s/v等参数作为输入,返回实施该签名的账户地址。% `3 D3 k1 {* q7 n2 n- y$ Z
因此我们只需要通过合约拿到实施签名的地址,和真正的地址进行对比,如果地址一致,就说明验证通过了。
智能合约代码如下:
pragma solidity ^0.4.15;& Q, X1 F! D: a+ Y" z* z3 A
contract Auth {$ {5 x6 E8 s5 t D2 C* E
function verify( bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(address retAddr) {0 e, s; L1 c, u) M
bytes memory prefix = “\x19MoacNode Signed Message:\n32”;
bytes32 prefixedHash = sha3(prefix, hash);- b5 u) ?* t' H' ^6 ?' P
return ecrecover(prefixedHash, v, r, s);/ i" P; n7 v& C2 E0 ?$ |
}
}
合约会为要签名的消息加上"\x19MoacNode Signed Message:\n32"前缀,所以实际上真正的被签名的数据并不是message的哈希值,而是message的哈希值加上前缀后再次哈希的值。要非常注意这一点,否则后期验证签名会不成功。) c5 S2 l; p& q
将该智能合约部署到墨客区块链mainnet,拿到合约的地址以及abiString,用于接下来和合约进行交互的代码。* M9 C& j: D( B2 B, a
const contract=chain3.mc.contract(abiString).at(contractAddr);3 I) z& N# L( J9 n- [% T& {7 A$ V
console.log(contract.verify(sha3Msg, v, r, s));4 A: B2 w. f9 d! ^5 u
console.log(account);
运行的结果:
可以看到实施签名的地址和验证后返回的地址一致,签名通过验证。( k; O1 R7 c9 E) B4 a
将合约的abiString保存为文件./Auth-abi.json;
本部分完整代码 moacAuth.js:4 d2 A/ t" Y! u/ F( p1 P
var Chain3 = require(‘chain3’);( s; G2 b! J( q- v+ k; \/ y& d: M
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));2 p+ s/ u1 E' X N5 v0 u5 B
var contractAddr = “0x273C467c9404e6867D873203805aaFFAd20aAc93”;
const account = chain3.mc.accounts[0];0 M+ S1 z* ?7 L" _6 s, u) w
const abiString = require(’./Auth-abi.json’);
chain3.personal.unlockAccount(chain3.mc.accounts[0], “XXXXXXXXXXX”, 1000);
let sha3Msg = chain3.sha3(“HELLO MOAC!”);
let signedData = chain3.mc.sign(account, sha3Msg);5 J; ?5 g6 Q2 }( a
let r = signedData.slice(0, 66);
let s = ‘0x’ + signedData.slice(66, 130);# T1 Y% o; W" S( K$ n. c; J
let v = ‘0x’ + signedData.slice(130, 132);
v = chain3.toDecimal(v);
const contract=chain3.mc.contract(abiString).at(contractAddr);$ b/ O1 a9 Z% Y( V6 h2 _
console.log(contract.verify(sha3Msg, v, r, s));
console.log(account);
可以看到,在区块链中使用智能合约完成对数据的签名和验证还是比较简单的。逻辑图如下:
9 T; M( \5 n6 K1 N& a; V- V; I) r
3
直接签名与验证
步骤1和步骤2,通过部署智能合约进行验证。- P0 ?; T( g3 u! D& |; E. J, `
也可以直接使用私钥签名,然后通过解析签名信息得到公钥或者地址,这样就不需要部署智能合约。3 q7 U2 M4 t( e: `" H
//run ‘npm install eth-crypto --save’$ s! V+ b0 t4 s3 I m1 W" U
const EthCrypto = require(‘eth-crypto’);: Y5 @3 r1 u2 R0 E, w
const identity = EthCrypto.createIdentity();
//显示私钥、公钥和地址9 P( I# Z1 ]/ ^- ~
console.log(‘privateKey:’+identity.privateKey);. ^3 Y2 A" G7 V- a$ [
console.log('publicKey: '+identity.publicKey);: Y; ~8 a+ U6 w b' {/ r1 D
console.log('address: '+identity.address);
//用私钥给明文签名" ~$ C6 O7 x d' a; \1 N2 {. r
const message = ‘Hello World’;
const messageHash = EthCrypto.hash.keccak256(message);( @& K; f, L6 B; Y' v9 Q
const signature = EthCrypto.sign(
identity.privateKey, // privateKey
messageHash // hash of message2 K0 o5 a6 T' C8 z
);
console.log();
console.log('signature: '+signature);
//使用签名信息+明文* H- U6 n+ @( c D
//解析出签名者地址
const signerAddress = EthCrypto.recover(
signature,
EthCrypto.hash.keccak256(message) // signed message hash
);
console.log();
console.log('signerAddress: '+signerAddress);" N3 k/ x$ R0 t
//使用签名信息+明文
//解析出签名者公钥
const signerPublic = EthCrypto.recoverPublicKey(
signature, // signature: Z& I; ?6 T. A) v) S, b+ l
EthCrypto.hash.keccak256(message) // message hash
);6 t- K. n, Q1 x
console.log();9 T' {0 q# q) _2 B- v
console.log('signerPublic: '+signerPublic);
运行结果:
成为第一个吐槽的人