MOAC BlockChain数据签名及验证
飞儿506
发表于 2022-12-20 23:36:45
158
0
0
本文使用智能合约完成对签名的验证,使用chain3.js完成对数据的签名以及和智能合约的交互。! M( G: h! b+ H6 Y
环境:3 a! ]! N4 ? J: r4 F4 e' c) [
MOAC版本:nuwa1.0.6.win.zip(本文在mainnet进行);' L: Z: ^8 z0 p- g
操作系统:64位Windows 10家庭版。% N5 N9 a. |$ w+ r2 z1 x/ Q
12 a) P9 K$ K5 d+ x6 H
签名% ~* E" V7 l( \5 k- _7 @
实施签名需要两个部分:待签名的数据 + 实施签名的账户。签名过程可以使用chain3.mc.sign()来实现,具体代码为:
var Chain3 = require(‘chain3’);
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));
chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000); //实施签名要求解锁账户7 h( O- C/ W+ Y* ?' l, F
var account = chain3.mc.accounts[0];$ e$ f+ @) U* n M: L
var sha3Msg = chain3.sha3(“HELLO MOAC!”);
var signedData = chain3.mc.sign(account, sha3Msg);
console.log("account: " + account);
console.log("sha3Msg: " + sha3Msg);
console.log("Signed data: " + signedData);
在上面的代码中,先将要签名的数据"HELLO MOAC!"生成哈希串,使用chain3.sha3(“HELLO MOAC!”)。接着我们使用当前连接节点的第一个默认帐户进行签名。此时需要chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000)来打开数据签名所使用帐户。需要注意的是,当你打开你的帐户时,可能有安全风险。因为其它程序也可以通过访问节点进行类似的sign,这意味着,他们可以伪造你的数据,包括以你的名义发起交易,转走你的钱。
运行的结果:
6 F. X7 ?" j$ s: D) [# s
返回值Signed data总共132字节(去掉前面的’0x’的话是130字节)。在ECDSA(EC是“椭圆曲线”的简称,DSA是“数字签名算法”的简称)签名算法中,返回值可以分为三个部分:r, s, v。其中前0~66个字节为r, 66~130之间的字节为s, 130~132的字节为v。, m, T7 ?2 V, D3 H
接下来我们可以将它打印出来,在接下来验证签名的部分会用到。该部分完整的代码如下:
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];' \( y( V( Z; N9 U- r
var sha3Msg = chain3.sha3(“HELLO MOAC!”);9 `: @1 B( U% T$ f
var signedData = chain3.mc.sign(account, sha3Msg);
console.log("account: " + account);* Z, j4 e+ x& }1 S6 W0 w
console.log("sha3Msg: " + sha3Msg);3 k# U$ C3 X5 c0 M9 k# K
console.log("Signed data: " + signedData);. D$ K4 V" A# L" {
let r = signedData.slice(0, 66);" k: @- _) o' N6 C. t! h+ V
let s = ‘0x’ + signedData.slice(66, 130);
let v = ‘0x’ + signedData.slice(130, 132);* r$ T* Y) {& s4 F
v = chain3.toDecimal(v);
console.log();- b9 F5 f1 X& A+ b5 F! A
console.log(‘r:’, r);
console.log(‘s:’, s); j+ Q7 k# ?) r2 K8 u! b1 O8 T
console.log(‘v:’, v);
运行的结果:; G `1 D. J2 q% X( j
2/ e3 x1 J( B: W2 s1 Y6 T
验证
签名完成了,我们如何验证某些签名后的数据是哪个账户签名的呢?5 F8 k# A2 v5 T9 F% Y8 s
本例中,验证签名通过智能合约的ecrecover函数来实现。
ecrecover接收原始数据的哈希值(就是上面输出的sha3Msg)以及r/s/v等参数作为输入,返回实施该签名的账户地址。. J+ n* p' F- K6 f3 e
因此我们只需要通过合约拿到实施签名的地址,和真正的地址进行对比,如果地址一致,就说明验证通过了。5 j( ?- U( D7 o# ]! G
智能合约代码如下:9 m' i0 W' V8 c3 z
pragma solidity ^0.4.15;% [4 m* n/ |- Z& [1 O$ p* q
contract Auth {
function verify( bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(address retAddr) {2 R/ X/ v- S9 l& C( X
bytes memory prefix = “\x19MoacNode Signed Message:\n32”;
bytes32 prefixedHash = sha3(prefix, hash);
return ecrecover(prefixedHash, v, r, s);6 p9 N' z: S7 C
}1 x3 x7 O+ s0 A7 r- \4 R; _
}
合约会为要签名的消息加上"\x19MoacNode Signed Message:\n32"前缀,所以实际上真正的被签名的数据并不是message的哈希值,而是message的哈希值加上前缀后再次哈希的值。要非常注意这一点,否则后期验证签名会不成功。
将该智能合约部署到墨客区块链mainnet,拿到合约的地址以及abiString,用于接下来和合约进行交互的代码。
const contract=chain3.mc.contract(abiString).at(contractAddr);
console.log(contract.verify(sha3Msg, v, r, s));
console.log(account);
运行的结果:3 k& O4 A3 ^+ q* U, T# D
可以看到实施签名的地址和验证后返回的地址一致,签名通过验证。
将合约的abiString保存为文件./Auth-abi.json;/ ~8 e, u/ p9 v2 K
本部分完整代码 moacAuth.js:- A( G2 _- E A/ C5 s, H; h* k
var Chain3 = require(‘chain3’);
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));- V) R$ `8 H, Q0 x: Q4 E; i5 y% B) [
var contractAddr = “0x273C467c9404e6867D873203805aaFFAd20aAc93”;
const account = chain3.mc.accounts[0];7 v3 o& d6 C Y, V+ A8 V
const abiString = require(’./Auth-abi.json’);- B& l0 K }* t3 N% A4 Z
chain3.personal.unlockAccount(chain3.mc.accounts[0], “XXXXXXXXXXX”, 1000);) ?, p" k' m: D- e
let sha3Msg = chain3.sha3(“HELLO MOAC!”);
let signedData = chain3.mc.sign(account, sha3Msg);
let r = signedData.slice(0, 66);
let s = ‘0x’ + signedData.slice(66, 130);
let v = ‘0x’ + signedData.slice(130, 132);' S3 l! |0 I8 v' N H4 `8 s) B
v = chain3.toDecimal(v);
const contract=chain3.mc.contract(abiString).at(contractAddr);
console.log(contract.verify(sha3Msg, v, r, s));
console.log(account);1 [+ V/ i2 {8 \3 ?( d6 U
可以看到,在区块链中使用智能合约完成对数据的签名和验证还是比较简单的。逻辑图如下:! @% Z* o, [; T
' Y/ N( `2 B+ U& V# }
3; G3 ?9 A ]. @+ g7 O" \
直接签名与验证
步骤1和步骤2,通过部署智能合约进行验证。
也可以直接使用私钥签名,然后通过解析签名信息得到公钥或者地址,这样就不需要部署智能合约。
//run ‘npm install eth-crypto --save’/ e8 g. O4 Q" _) y j
const EthCrypto = require(‘eth-crypto’);
const identity = EthCrypto.createIdentity(); [. b4 `7 q) r; E$ ?
//显示私钥、公钥和地址
console.log(‘privateKey:’+identity.privateKey);# }% B M' C* H ~/ s! {
console.log('publicKey: '+identity.publicKey);6 R) F; ^# ?+ Z
console.log('address: '+identity.address);
//用私钥给明文签名
const message = ‘Hello World’;
const messageHash = EthCrypto.hash.keccak256(message);0 u& E: o/ l: B5 u- O; d7 W
const signature = EthCrypto.sign(
identity.privateKey, // privateKey
messageHash // hash of message
);
console.log();" o* x2 S; i! Q n5 E j
console.log('signature: '+signature);6 {% _! [: n k
//使用签名信息+明文& S, s. b9 ^0 C+ `: V+ c: ^! D4 ^
//解析出签名者地址4 U& f; U/ r/ O& p5 e$ J
const signerAddress = EthCrypto.recover(
signature,, q# ~, h& L5 k: \# K- c; C; { b6 s: s8 Y
EthCrypto.hash.keccak256(message) // signed message hash
);
console.log();/ d( k" u d& O( U: p) G
console.log('signerAddress: '+signerAddress);2 R5 e" `- b- D/ |7 w, o
//使用签名信息+明文3 { Y6 l0 \0 M6 f0 R1 a
//解析出签名者公钥
const signerPublic = EthCrypto.recoverPublicKey(/ Y7 i" E6 u. E4 o
signature, // signature
EthCrypto.hash.keccak256(message) // message hash2 p3 b. l% U6 ], \
);
console.log();. }; C- Y. V& M ~. Q4 C% c
console.log('signerPublic: '+signerPublic);
运行结果:
成为第一个吐槽的人