標籤:

基於以太坊(Ethereum)完成對數據的簽名及驗證

數據的簽名及驗證過程是密碼學在區塊鏈項目里一個非常重要的應用。本篇文章探索了以太坊(Ethereum)里簽名及驗證簽名機制的實現。

以下內容均基於本地私有鏈進行測試(不會搭建的可以看上一篇專欄文章), 使用智能合約完成對簽名的驗證,使用web3.js完成對數據的簽名以及和智能合約的交互。

一、簽名

實施簽名需要兩個部分:待簽名的數據+實施簽名的賬戶。簽名過程可以使用web3.eth.sign()來實現,具體代碼為:

let msg = web3.sha3(today is 20171026)nlet signature = web3.eth.sign(address, msg)n

這裡我們使用address賬戶對信息today is 20171026進行了簽名。返回值signature為

0x125a275046b65a96f11fdb7cd1072054e67526a76f54b1622fde4e4592d6fe2d5bf664ace77da52c6f94f08a56077e5d7a80048f70c38a92169205df3c9c43ea1bn

該返回值總共132位元組(去掉前面的0x的話是130位元組)。因為以太坊採用的ECDSA簽名演算法,根據ECDSA: (v, r, s), what is v?的介紹, 返回值可以分為三個部分:r, s, v。其中前0~66個位元組為r, 66~130之間的位元組為s, 130~132的位元組為v。代碼實現如下:

let r = signature.slice(0, 66)nlet s = 0x + signature.slice(66, 130)nlet v = 0x + signature.slice(130, 132)nv = web3.toDecimal(v)n

接下來我們可以將它列印出來,在接下來驗證簽名的部分會用到。該部分完整的代碼如下:

const Web3 = require(web3);nnconst ethereumUri = http://localhost:8545;nconst web3 = new Web3(new Web3.providers.HttpProvider(ethereumUri));nnconst address = web3.eth.accounts[0];nnweb3.personal.unlockAccount(address, password) // 實施簽名要求賬戶已解鎖nnlet msg = web3.sha3(today is 20171026)nlet signature = web3.eth.sign(address, msg)nnlet r = signature.slice(0, 66)nlet s = 0x + signature.slice(66, 130)nlet v = 0x + signature.slice(130, 132)nv = web3.toDecimal(v)nnconsole.log(r, r)nconsole.log(s, s)nconsole.log(v, v)nconsole.log(msg)n

二、驗證

簽名完成了,我們如何驗證某些簽名後的數據是哪個賬戶簽名的呢?在web3.js 發布1.0版本以前,驗證簽名只能通過智能合約的ecrecover函數來實現。新版的web3.js提供了web3.eth.accounts.recover函數用於驗證簽名。這裡我們仍然使用傳統的智能合約ecrecover方式。

ecrecover接收數據的哈希值以及r/s/v等參數作為輸入,返回實施該簽名的賬戶地址。因此我們只需要通過合約拿到實施簽名的地址,和我們真正的地址進行對比,如果地址一致,就說明驗證通過了。

智能合約代碼如下:

pragma solidity ^0.4.15;nncontract Auth { n function verify( bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(address retAddr) {n bytes memory prefix = "x19Ethereum Signed Message:n32";n bytes32 prefixedHash = sha3(prefix, hash);n return ecrecover(prefixedHash, v, r, s);n }n}n

將該智能合約部署到私有鏈上,拿到合約的地址以及abi,用於接下來和合約進行交互的代碼。

const contract=web3.eth.contract(abi).at(0x2e2A4cD2869862492C744307310847466c008257);nconsole.log(contract.verify(msg, v, r, s));nconsole.log(address)n

本地執行結果為:

liangpeili@liang-desktop$ node sign.jsn0xe0803904cbfce8e07745e1b404de43ce6f1e43bcn0xe0803904cbfce8e07745e1b404de43ce6f1e43bcn

可以看到實施簽名的地址和驗證後返回的地址一致,簽名通過驗證。

本部分完整代碼如下:

const Web3 = require(web3);nconst abi = require(./Auth-abi.json);nnconst ethereumUri = http://localhost:8545;nconst web3 = new Web3(new Web3.providers.HttpProvider(ethereumUri));nnconst address = web3.eth.accounts[0];nnweb3.personal.unlockAccount(address, password)nnlet msg = web3.sha3(today is 20171026)nlet signature = web3.eth.sign(address, msg)nnlet r = signature.slice(0, 66)nlet s = 0x + signature.slice(66, 130)nlet v = 0x + signature.slice(130, 132)nv = web3.toDecimal(v)nn// console.log(r, r)n// console.log(s, s)n// console.log(v, v)n// console.log(msg)nnconst contract=web3.eth.contract(abi).at(0x2e2A4cD2869862492C744307310847466c008257);nnconsole.log(contract.verify(msg, v, r, s));nconsole.log(address)n

可以看到,在以太坊中完成對數據的簽名和驗證還是比較簡單的。並且賬戶不僅可以對交易進行簽名,還可以對任意數據進行簽名並驗證。

參考資料:

Signing in geth and verifying in solidity do not produce correct results · Issue #3731 · ethereum/go-ethereum

Simpler signing and verifying procedure · Issue #79 · ethereum/EIPs

workflow on signing a string with private key, followed by signature verification with public key

Ethereum ecrecover signature verification and encryption

Simple replay attack protection · Issue #155 · ethereum/EIPs


推薦閱讀:

ICO系列補遺:關於ERC20代幣
ETC價格突破20美元,市值已超越辣條
SelfSell:區塊鏈時代的個人版「納斯達克」?
比特幣,萊特幣,以太坊,以太經典,BCC,Dash價格分析(46)

TAG:以太坊 |