MOAC BlockChain数据签名及验证
飞儿506
发表于 2022-12-20 23:36:45
124
0
0
本文使用智能合约完成对签名的验证,使用chain3.js完成对数据的签名以及和智能合约的交互。1 G- R6 g% N0 ?4 x+ ?- t
环境:
MOAC版本:nuwa1.0.6.win.zip(本文在mainnet进行);% O& t2 L6 z, ~$ X7 H
操作系统:64位Windows 10家庭版。; s' _5 I/ \- w" V
1
签名& o0 {: x# Y, t4 a# Q' G: M
实施签名需要两个部分:待签名的数据 + 实施签名的账户。签名过程可以使用chain3.mc.sign()来实现,具体代码为:' \! m- z: x( S+ w7 d
var Chain3 = require(‘chain3’);# C0 o; W3 `( v
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));& j+ C2 n; j+ `# X/ I0 r
chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000); //实施签名要求解锁账户7 U# K* I" {/ z/ s
var account = chain3.mc.accounts[0];
var sha3Msg = chain3.sha3(“HELLO MOAC!”);
var signedData = chain3.mc.sign(account, sha3Msg);
console.log("account: " + account);) x) a8 y- f6 }, R+ R5 h5 T
console.log("sha3Msg: " + sha3Msg);
console.log("Signed data: " + signedData);9 l# M3 U8 w) b3 m
在上面的代码中,先将要签名的数据"HELLO MOAC!"生成哈希串,使用chain3.sha3(“HELLO MOAC!”)。接着我们使用当前连接节点的第一个默认帐户进行签名。此时需要chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000)来打开数据签名所使用帐户。需要注意的是,当你打开你的帐户时,可能有安全风险。因为其它程序也可以通过访问节点进行类似的sign,这意味着,他们可以伪造你的数据,包括以你的名义发起交易,转走你的钱。
运行的结果:) D! y% \1 r, Y" [
1 z. X; t5 ]6 N8 M3 a$ ^
返回值Signed data总共132字节(去掉前面的’0x’的话是130字节)。在ECDSA(EC是“椭圆曲线”的简称,DSA是“数字签名算法”的简称)签名算法中,返回值可以分为三个部分:r, s, v。其中前0~66个字节为r, 66~130之间的字节为s, 130~132的字节为v。5 @7 P5 x: a6 w$ T( \# v6 K
接下来我们可以将它打印出来,在接下来验证签名的部分会用到。该部分完整的代码如下:& K$ ^8 p% J# @2 j) V% ?
var Chain3 = require(‘chain3’);+ u5 z/ Z1 x9 Y7 P9 _
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));9 N8 T( i' I2 t8 W0 c
chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000); //实施签名要求解锁账户
var account = chain3.mc.accounts[0];9 X1 N3 r3 ]1 C. p/ v
var sha3Msg = chain3.sha3(“HELLO MOAC!”);
var signedData = chain3.mc.sign(account, sha3Msg);- C5 T% G9 l# g' e0 ?0 w6 U/ {! l
console.log("account: " + account);
console.log("sha3Msg: " + sha3Msg);" a7 n* J6 y* P) d8 s% T; n7 ~3 z
console.log("Signed data: " + signedData);
let r = signedData.slice(0, 66);- j9 h: O: D- P
let s = ‘0x’ + signedData.slice(66, 130);
let v = ‘0x’ + signedData.slice(130, 132);! M! w' C z' T% D" V* J
v = chain3.toDecimal(v);% v$ k4 E3 @% s3 K4 T# C, Y
console.log();5 ^" {5 \4 Q! n& V" _
console.log(‘r:’, r);. w' D0 P e2 l. q. O
console.log(‘s:’, s);
console.log(‘v:’, v);0 y! C0 F6 ^# i8 _9 }9 Z
运行的结果:
29 D9 t! w- m6 {& x9 ^
验证' Z; s! y: g, J8 j$ k
签名完成了,我们如何验证某些签名后的数据是哪个账户签名的呢?+ r- A) L+ y( f6 C# a
本例中,验证签名通过智能合约的ecrecover函数来实现。0 d& P& ?( M2 q: h# d
ecrecover接收原始数据的哈希值(就是上面输出的sha3Msg)以及r/s/v等参数作为输入,返回实施该签名的账户地址。
因此我们只需要通过合约拿到实施签名的地址,和真正的地址进行对比,如果地址一致,就说明验证通过了。0 ?. D; D) b9 v8 C$ S, D
智能合约代码如下:3 T2 u) D, N+ F5 R: p
pragma solidity ^0.4.15;$ }' W. m* E8 G& q j
contract Auth {
function verify( bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(address retAddr) {
bytes memory prefix = “\x19MoacNode Signed Message:\n32”;2 T/ a* a! R" b2 \9 ?
bytes32 prefixedHash = sha3(prefix, hash);0 ?0 @- W6 T, K7 v
return ecrecover(prefixedHash, v, r, s);
}; a( c% K$ B3 h3 c+ Q0 b8 s! j
}
合约会为要签名的消息加上"\x19MoacNode Signed Message:\n32"前缀,所以实际上真正的被签名的数据并不是message的哈希值,而是message的哈希值加上前缀后再次哈希的值。要非常注意这一点,否则后期验证签名会不成功。# p9 y# o* p0 G2 ?
将该智能合约部署到墨客区块链mainnet,拿到合约的地址以及abiString,用于接下来和合约进行交互的代码。+ w( R# F9 m1 C* |. u
const contract=chain3.mc.contract(abiString).at(contractAddr);+ _) p$ ~& t1 Q1 Y+ `7 o0 J) {: h7 R
console.log(contract.verify(sha3Msg, v, r, s));
console.log(account);
运行的结果:$ _* ^6 j. p- ~3 t- f9 [1 d$ l
可以看到实施签名的地址和验证后返回的地址一致,签名通过验证。
将合约的abiString保存为文件./Auth-abi.json;& k _' U# a2 _$ D/ L
本部分完整代码 moacAuth.js:
var Chain3 = require(‘chain3’);& Q& [ H! {, ^9 f
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));
var contractAddr = “0x273C467c9404e6867D873203805aaFFAd20aAc93”;
const account = chain3.mc.accounts[0];5 O" z: a3 Z" J1 h
const abiString = require(’./Auth-abi.json’);( J2 J8 m) k- H4 A1 R: U
chain3.personal.unlockAccount(chain3.mc.accounts[0], “XXXXXXXXXXX”, 1000);, j/ u0 _8 f1 b% R. B$ x1 z. P
let sha3Msg = chain3.sha3(“HELLO MOAC!”);' Y7 V$ M/ y0 r+ j% l
let signedData = chain3.mc.sign(account, sha3Msg);
let r = signedData.slice(0, 66);
let s = ‘0x’ + signedData.slice(66, 130);% \4 e" _: Z5 L( j* d* t7 S8 e
let v = ‘0x’ + signedData.slice(130, 132);
v = chain3.toDecimal(v);+ x$ @3 S1 }* e
const contract=chain3.mc.contract(abiString).at(contractAddr);
console.log(contract.verify(sha3Msg, v, r, s));
console.log(account);
可以看到,在区块链中使用智能合约完成对数据的签名和验证还是比较简单的。逻辑图如下:
1 K/ W$ K: r( ^( z& t7 O0 F
3
直接签名与验证& Y/ [, h/ p1 ^* A. L1 W- [2 s& M1 Y
步骤1和步骤2,通过部署智能合约进行验证。7 i/ o+ c6 V' P) m9 M. H& Y+ b+ V
也可以直接使用私钥签名,然后通过解析签名信息得到公钥或者地址,这样就不需要部署智能合约。" ^, q0 l" M" Q0 J2 t2 N
//run ‘npm install eth-crypto --save’* K) A% Z3 K+ I" a3 m/ l
const EthCrypto = require(‘eth-crypto’);
const identity = EthCrypto.createIdentity();3 S) e. f+ l6 ? ?, F3 |5 D
//显示私钥、公钥和地址
console.log(‘privateKey:’+identity.privateKey);! \- p5 t; u6 R, b6 B+ T
console.log('publicKey: '+identity.publicKey);
console.log('address: '+identity.address);! c4 E. |/ `* d$ h0 ?$ j
//用私钥给明文签名
const message = ‘Hello World’;7 N& ^) T8 ?- |0 N& k+ l
const messageHash = EthCrypto.hash.keccak256(message);
const signature = EthCrypto.sign(8 I, y9 {% T' G/ R9 l0 [
identity.privateKey, // privateKey
messageHash // hash of message
);
console.log();: F. O4 d; e+ S' S, C8 G
console.log('signature: '+signature);6 y( d; Y3 M0 V
//使用签名信息+明文
//解析出签名者地址% _: L; C" |7 c& o
const signerAddress = EthCrypto.recover(3 `! Y4 `& Q/ m) ]+ o! Y3 [# j
signature, H$ D2 Z: l% L! Z
EthCrypto.hash.keccak256(message) // signed message hash$ U+ S# S, f6 U5 ~. E$ l5 x
);0 j5 ?6 c% T& T# W) u# ^; L
console.log();
console.log('signerAddress: '+signerAddress);
//使用签名信息+明文# `. f# N- C" ]" Y- c7 j* v0 X
//解析出签名者公钥3 m0 A4 l$ Q# `7 C/ B
const signerPublic = EthCrypto.recoverPublicKey(
signature, // signature
EthCrypto.hash.keccak256(message) // message hash
);
console.log();
console.log('signerPublic: '+signerPublic);" U; c4 ~' O# k
运行结果:
成为第一个吐槽的人