Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

MOAC BlockChain数据签名及验证

飞儿506
126 0 0
数据的签名及验证过程是密码学在区块链项目里一个非常重要的应用。本文基于MOAC区块链实现数据签名及验证。
/ P, q$ B$ y6 X6 l0 n) A$ F* w8 V本文使用智能合约完成对签名的验证,使用chain3.js完成对数据的签名以及和智能合约的交互。/ I( u1 C; ^6 J$ Z1 y
环境:. m" y4 r% F# x) z) @. t5 j% d
MOAC版本:nuwa1.0.6.win.zip(本文在mainnet进行);
3 w: d! D" z, z" X操作系统:64位Windows 10家庭版。; F: n8 X* k. A7 ]* p5 S
1. n' }7 F0 d$ j$ e: G4 U
签名
, |- f" j8 X3 {  r/ n! D7 c实施签名需要两个部分:待签名的数据 + 实施签名的账户。签名过程可以使用chain3.mc.sign()来实现,具体代码为:
4 Y6 W# o, E+ X. q  h" V# Y: Dvar Chain3 = require(‘chain3’);
9 x! i+ i# k  R# z) V9 Mvar chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));9 Q$ F/ |+ f: D! ?+ i8 ~
chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000);  //实施签名要求解锁账户
3 ]2 J4 R' b, H9 e  K1 ?0 ^2 l* Cvar account = chain3.mc.accounts[0];
  y) Z+ P$ w( I+ g0 H# _var sha3Msg = chain3.sha3(“HELLO MOAC!”);
4 f8 Q) H, f1 i: s; wvar signedData = chain3.mc.sign(account, sha3Msg);
$ t- Q9 J8 d4 h) Gconsole.log("account: " + account);( J7 r5 s- k4 ^  l
console.log("sha3Msg: " + sha3Msg);
( v! z3 |& A) v( l5 U8 Tconsole.log("Signed data: " + signedData);( J7 n7 b! c' k1 M
在上面的代码中,先将要签名的数据"HELLO MOAC!"生成哈希串,使用chain3.sha3(“HELLO MOAC!”)。接着我们使用当前连接节点的第一个默认帐户进行签名。此时需要chain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000)来打开数据签名所使用帐户。需要注意的是,当你打开你的帐户时,可能有安全风险。因为其它程序也可以通过访问节点进行类似的sign,这意味着,他们可以伪造你的数据,包括以你的名义发起交易,转走你的钱。  `4 D3 a. a9 z8 \5 G# N* `
运行的结果:. I( n# a! K2 Z! R; e$ O; J' K1 W

( l4 d- C0 ^% Z5 w) y7 p返回值Signed data总共132字节(去掉前面的’0x’的话是130字节)。在ECDSA(EC是“椭圆曲线”的简称,DSA是“数字签名算法”的简称)签名算法中,返回值可以分为三个部分:r, s, v。其中前0~66个字节为r, 66~130之间的字节为s, 130~132的字节为v。- O. c' b2 C! m6 i% x1 t0 y
接下来我们可以将它打印出来,在接下来验证签名的部分会用到。该部分完整的代码如下:0 c& V( W; e/ Y+ M
var Chain3 = require(‘chain3’);  O4 @9 Y7 x& ?* n- q; L
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));
. b% }" j/ t& u4 \8 w1 Y2 x! Ichain3.personal.unlockAccount(chain3.mc.accounts[0], “xxxxxxxxxxxx”, 1000);  //实施签名要求解锁账户
2 {/ j0 A5 o$ _- D1 hvar account = chain3.mc.accounts[0];
! B4 P! y, u8 N2 ~var sha3Msg = chain3.sha3(“HELLO MOAC!”);
1 Y4 R& [! }8 H9 b8 qvar signedData = chain3.mc.sign(account, sha3Msg);( i4 _4 H7 C$ t
console.log("account: " + account);1 k2 y6 I, X# T* I1 Q/ P
console.log("sha3Msg: " + sha3Msg);
$ w. a' D) }6 y+ V5 Q; Yconsole.log("Signed data: " + signedData);6 `) a( B( m- E$ b# P4 W, H- S) ~
let r = signedData.slice(0, 66);
8 S. Y# p- j* o: e& Nlet s = ‘0x’ + signedData.slice(66, 130);. r. b% C+ b2 @6 @, R) V5 l, o+ h
let v = ‘0x’ + signedData.slice(130, 132);
: j8 l" \# V+ ^" i' lv = chain3.toDecimal(v);1 D7 b4 }" U: J$ M6 N$ j
console.log();
+ n: n4 S: W2 n# ?7 g7 q  h1 Wconsole.log(‘r:’, r);
. g" v, @/ C2 lconsole.log(‘s:’, s);' ]+ G0 O( j3 r5 k8 A2 ?$ R
console.log(‘v:’, v);* y$ g: M8 ~: z9 P
运行的结果:
1 j8 g8 z( h4 Y, w. o( b/ w
& m+ B0 @+ W* u# E$ }, _) N2
7 F6 k- N  U* L; I- J. j* c验证
; ^- |3 F$ c$ @' J  n; `3 q: B签名完成了,我们如何验证某些签名后的数据是哪个账户签名的呢?! T8 Q4 }' c5 G  F5 y) U
本例中,验证签名通过智能合约的ecrecover函数来实现。5 V/ m0 v" f! s' a3 }
ecrecover接收原始数据的哈希值(就是上面输出的sha3Msg)以及r/s/v等参数作为输入,返回实施该签名的账户地址。5 _8 I' p3 ]$ I& Q) R
因此我们只需要通过合约拿到实施签名的地址,和真正的地址进行对比,如果地址一致,就说明验证通过了。
" N9 X: A. B/ A1 V智能合约代码如下:9 Q: v1 W$ ?" g2 t+ D5 p4 o
pragma solidity ^0.4.15;
. M) |; s" b, k' M; M* fcontract Auth {- \6 ~# b2 U1 y* [4 G
function verify( bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(address retAddr) {6 D* z2 [0 T( X
bytes memory prefix = “\x19MoacNode Signed Message:\n32”;
7 u5 ~% c: g$ Hbytes32 prefixedHash = sha3(prefix, hash);  p  S; V; D9 x6 Y1 J( p0 w
return ecrecover(prefixedHash, v, r, s);2 j7 n! ^$ d7 B/ h: t+ R0 P4 g: p
}
' P5 f9 `' n9 Q  U) O. d}! j1 j5 T. B1 P! J
合约会为要签名的消息加上"\x19MoacNode Signed Message:\n32"前缀,所以实际上真正的被签名的数据并不是message的哈希值,而是message的哈希值加上前缀后再次哈希的值。要非常注意这一点,否则后期验证签名会不成功。
) R. P/ m* a# L! F7 f- f8 W将该智能合约部署到墨客区块链mainnet,拿到合约的地址以及abiString,用于接下来和合约进行交互的代码。
' e  z; ~* X3 `* I1 |const contract=chain3.mc.contract(abiString).at(contractAddr);
8 a6 T" U' k$ Y+ Xconsole.log(contract.verify(sha3Msg, v, r, s));3 h3 `2 _; X6 ?# r
console.log(account);" q- ~% e4 J. d* Q# H
运行的结果:/ ?0 n, i3 C% U3 M' r
可以看到实施签名的地址和验证后返回的地址一致,签名通过验证。
1 @# s  x- \) ~3 X将合约的abiString保存为文件./Auth-abi.json;
2 E: ]. U7 ?9 `! u本部分完整代码 moacAuth.js:
/ v2 \& @. e# O1 n6 O! |var Chain3 = require(‘chain3’);9 z4 Y+ C+ X% Y! y
var chain3 = new Chain3(new Chain3.providers.HttpProvider(‘http://localhost:8545’));, ]0 E  B/ Y- @* A( r+ s
var contractAddr = “0x273C467c9404e6867D873203805aaFFAd20aAc93”;
' p  u1 a. ?6 @- f' U  l  A  Wconst account = chain3.mc.accounts[0];
/ @0 k3 l, t0 `5 r- econst abiString = require(’./Auth-abi.json’);
; e1 Q0 f; i$ `( [9 u, {chain3.personal.unlockAccount(chain3.mc.accounts[0], “XXXXXXXXXXX”, 1000);- {5 P2 ?9 J+ b
let sha3Msg = chain3.sha3(“HELLO MOAC!”);
5 n. e( t, s6 t3 i  G: }4 {$ |let signedData = chain3.mc.sign(account, sha3Msg);
/ |; v1 {) Y/ f* f- a! [+ Elet r = signedData.slice(0, 66);, ~, R8 V' z; Q+ m" a
let s = ‘0x’ + signedData.slice(66, 130);+ y) z2 h7 G5 k: ]
let v = ‘0x’ + signedData.slice(130, 132);7 k0 u/ ~& [$ j* K* d0 J
v = chain3.toDecimal(v);/ S% j. g- u- R6 w! b5 ?+ U
const contract=chain3.mc.contract(abiString).at(contractAddr);
# R  \8 N5 i& n& }0 C+ cconsole.log(contract.verify(sha3Msg, v, r, s));
: P4 M; T/ [2 v) b8 Kconsole.log(account);
3 g; D3 ]/ ^' t- G! {可以看到,在区块链中使用智能合约完成对数据的签名和验证还是比较简单的。逻辑图如下:
/ r% T2 q; V1 x* H( X/ n' w; h4 R
7 M7 j. F! O2 J; o" w! [, K, d3
  S2 w, b, i7 v4 \5 W+ T直接签名与验证
% v, N6 ^, w0 a1 @6 b4 \1 M步骤1和步骤2,通过部署智能合约进行验证。
' [# f' n  r5 W/ r也可以直接使用私钥签名,然后通过解析签名信息得到公钥或者地址,这样就不需要部署智能合约。
1 Q3 ?5 @: Y+ {1 {  r! i3 d% k  [8 ~//run ‘npm install eth-crypto --save’8 D* C2 f, O! k- t5 z+ U, U' E
const EthCrypto = require(‘eth-crypto’);
* ?& r5 e1 g9 S$ \const identity = EthCrypto.createIdentity();5 U8 ]+ ?1 Q+ \9 ^$ d  Q' i) ^5 \. `
//显示私钥、公钥和地址
/ N- y3 b' m$ h$ w2 gconsole.log(‘privateKey:’+identity.privateKey);: b# C2 z) c) Q
console.log('publicKey: '+identity.publicKey);
' P0 ~5 b6 U' L7 s% ^  v3 Lconsole.log('address:   '+identity.address);+ g* g% l$ [; V4 a( T2 M5 k
//用私钥给明文签名4 J9 c9 e, s! E) E3 P: B
const message = ‘Hello World’;( v# m% f) `$ s- Y: F
const messageHash = EthCrypto.hash.keccak256(message);5 F5 r; G% J& S& w
const signature = EthCrypto.sign(
! m+ g* W; u; g9 xidentity.privateKey, // privateKey- S7 _) d4 p2 u# C/ `0 q% d+ X; d
messageHash // hash of message
& D- r6 e4 }$ [0 n7 t8 u  _0 y);/ z$ I& g3 k4 _1 n2 K
console.log();! X  N2 O$ O8 f% [  V8 ?( u
console.log('signature: '+signature);
' b' ^* Z0 c, j4 D0 O4 k: H6 P1 r//使用签名信息+明文9 b1 e9 K3 r7 l8 q& D4 L1 f
//解析出签名者地址
2 a& q$ {0 q4 @const signerAddress = EthCrypto.recover(
$ K% S$ {4 U' k4 fsignature,% m9 O' d( {& _5 \( o
EthCrypto.hash.keccak256(message) // signed message hash
3 A; E5 ~! R# L% Y) C9 b);
: }/ L0 N" I# D8 \; h' uconsole.log();" z: k( f* E" Q9 Q4 C7 ~# N2 ?
console.log('signerAddress: '+signerAddress);
1 v" {" k2 l* N0 j; y//使用签名信息+明文: I- w& {5 a& K: W7 x6 z8 X
//解析出签名者公钥0 f: h& R9 H3 t: r! s0 n/ ^; b
const signerPublic = EthCrypto.recoverPublicKey(. N0 c" Q$ l8 ^/ c# J. S
signature, // signature
4 o& R, g! a, f6 j; x) H% h' k3 m! F1 GEthCrypto.hash.keccak256(message) // message hash/ G# M' x. J- c9 R5 O. M0 w6 v
);
! W  ]. U9 B$ B9 `6 R9 [. e, hconsole.log();  u7 P/ u9 U7 f7 h" h% `5 E
console.log('signerPublic: '+signerPublic);
6 U* u1 P9 H( Z# s运行结果:
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

飞儿506 初中生
  • 粉丝

    0

  • 关注

    0

  • 主题

    11