智能合约验证签名

原创 2018年04月15日 20:22:41

以太坊(ethereum)中使用了ECDSA签名算法,该算法基于椭圆曲线实现。
同时,智能合约编程语言solidity 也提供了签名和验证签名的操作:

1、签名

签名使用web3.eth.sign(),比如利用web3.js:

var account = web3.eth.accounts[0];
var sha3Msg = web3.sha3("blockchain");
var signedData = web3.eth.sign(account, sha3Msg);

但是需要注意的是geth 客户端签名比较特殊,下面是以太坊签名函数

sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)))

从上述代码可以看出,geth会为要签名的消息加上"\x19Ethereum Signed Message:\n" + len(message)前缀,所以实际上真正的被签名的数据并不是message 的哈希值,而是message的哈希值加上前缀后再次哈希的值。我们要非常注意这一点,否则后期验证签名会不成功。

2、验签

验证签名可以在智能合约中通过ecrecover()函数实现,ecrecover()函数如下:

ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)

从代码中可以看出,ecrecover()函数有四个参数,被签名数据的哈希值hash,以及签名结果划分的rsv三个值,即rsv是分别来自签名结果串signature,比如:

signature = 0x1a0744724beca14bfd7b5f838394a3e5c49c4872dbd1fbc29661ffd88bed14ad53e045268098d7934530e81c058d40b763381f8d5c75fd29b4300e64be75867401

r = signature[0:64] = 0x1a0744724beca14bfd7b5f838394a3e5c49c4872dbd1fbc29661ffd88bed14ad
s = signature[64:128] = 0x53e045268098d7934530e81c058d40b763381f8d5c75fd29b4300e64be758674
v = signature[128:130] = 0x01

这里需要特别注意另一参数被签名数据的哈希值hash,比如要签名的数据是blockchain,哈希值为:

sha3Msg = web3.sha3("blockchain") = 0x1a0744724beca14bfd7b5f838394a3e5c49c4872dbd1fbc29661ffd88bed14ad53e045268098d7934530e81c058d40b763381f8d5c75fd29b4300e64be75867401

但是如果把sha3Msg作为参数的话,恢复出的公钥地址并不是原来的进行签名的公钥地址,这就是前面提到的签名函数参数前缀问题,所以ecrecover()函数中的hash参数应该为:

sha3Msg = web3.sha3("blockchain");
hash = web3.sha3(sha3Msg);

附录

VerifySignature.sol文件

pragma solidity ^0.4.10;

contract VerifySignature{

  //数据验签入口函数
  function verifyByHashAndSig(bytes32 hash, bytes signature) returns (address){
    bytes memory signedString = signature;

    bytes32  r = bytesToBytes32(slice(signedString, 0, 32));
    bytes32  s = bytesToBytes32(slice(signedString, 32, 32));
    byte  v1 = slice(signedString, 64, 1)[0];
    uint8 v = uint8(v1) + 27;
    return ecrecoverDirect(hash, r, s, v);
  }

  //将原始数据按段切割出来指定长度
  function slice(bytes memory data, uint start, uint len) returns (bytes){
    bytes memory b = new bytes(len);

    for(uint i = 0; i < len; i++){
      b[i] = data[i + start];
    }
    return b;
  }

  //bytes转换为bytes32
  function bytesToBytes32(bytes memory source) returns (bytes32 result) {
    assembly {
        result := mload(add(source, 32))
    }
  }

  //使用ecrecover恢复公匙
  function ecrecoverDirect(bytes32 hash, bytes32 r, bytes32 s, uint8 v) returns (address addr){
     /* prefix might be needed for geth only
     * https://github.com/ethereum/go-ethereum/issues/3731
     */
     bytes memory prefix = "\x19Ethereum Signed Message:\n32";
     hash = sha3(prefix, hash);

     addr = ecrecover(hash, v, r, s);
  }
}

或者直接写为一个函数

function ecrecovery(bytes32 hash, bytes sig) public returns (address) {
    bytes32 r;
    bytes32 s;
    uint8 v;

    if (sig.length != 65) {
      return 0;
    }

    assembly {
      r := mload(add(sig, 32))
      s := mload(add(sig, 64))
      v := and(mload(add(sig, 65)), 255)
    }

    // https://github.com/ethereum/go-ethereum/issues/2053
    if (v < 27) {
      v += 27;
    }

    if (v != 27 && v != 28) {
      return 0;
    }

    /* prefix might be needed for geth only
     * https://github.com/ethereum/go-ethereum/issues/3731
     */
    // bytes memory prefix = "\x19Ethereum Signed Message:\n32";
    // hash = sha3(prefix, hash);

    return ecrecover(hash, v, r, s);
  }

VerifySignature.js测试文件

var VerifySignature = artifacts.require("./VerifySignature.sol");

contract('VerifySignature', function(accounts) {
    console.log(accounts);
    it("verify signature 成功!", function(done) {
        var account = accounts[0];
        var sha3Msg = web3.sha3("blockchain");
        /*
        console.log('1 '+ web3.sha3(web3.toHex('blockchain'))); 
        //0x948100b2466113dfb2b67ab686395aaf8935c612cc5316c01e30b5b354b646c9
        console.log('2 '+ web3.sha3(web3.toHex('blockchain'),{encoding:'hex'}));
        //0x7ee156df5091fbef71b96557542210a9c9ca851cc85aaf60026519b4aaccf491
        console.log('3 '+ web3.sha3('blockchain'));
        //0x7ee156df5091fbef71b96557542210a9c9ca851cc85aaf60026519b4aaccf491
        console.log('4 '+  web3.sha3(  unescape(encodeURIComponent('blockchain'))  ) ); 
        //to UTF8 //0x7ee156df5091fbef71b96557542210a9c9ca851cc85aaf60026519b4aaccf491
        console.log('5 '+  web3.sha3(  unescape(encodeURIComponent('blockchain')), {encoding:'hex'} ) ); 
        //to UTF8 //0x2f1437634f08e2b6324e0701d49075e39cfc34c1fca7c5111dc07b8150399176
        */
        var signedData = web3.eth.sign(account, sha3Msg);

        console.log("account: " + account);
        console.log("sha3(message): " + sha3Msg);
        console.log("Signed data: " + signedData);

        VerifySignature.new({ from: accounts[1] }).then(function(verifySignature) {
            verifySignature.verifyByHashAndSig.call(sha3Msg, signedData).then(function (addr) {
                console.log("    addr = %s", addr);
                assert.equal(addr, account, "Address doesn't match!")
                return  verifySignature.verifyByHashAndSig(sha3Msg, signedData);
                //return VerifySignature.verifyConsumerSignature.call();              
            }).then( function (txid) {
                console.log("    verifySignature, txid = %s, mined at #%s block", txid.tx, txid.receipt.blockNumber);//txid is a object contains tx, receipt, logs. Can print ("%x", txid) to check.

                var sig = signedData.slice(2)
                var r = `0x${sig.slice(0, 64)}`
                var s = `0x${sig.slice(64, 128)}`
                var v = web3.toDecimal(sig.slice(128, 130)) + 27

                return verifySignature.ecrecoverDirect.call(sha3Msg, r, s, v);
            }).then(function (addr) {
                console.log("    DirectAddr = %s", addr);
                assert.equal(addr, account, "Address doesn't match!")
                done();  // to stop these tests earlier, move this up
            }).catch(done); 
        }).catch(done);
    });
});

参考文献

1、区块链语言Solidity校验椭圆曲线加密数字签名(附实例)
2、workflow on signing a string with private key, followed by signature verification with public key
3、Signing in geth and verifying in solidity do not produce correct results

在32位程序中如何实现进程间通讯

1、引言 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯。WIN32 API提供了许多函数使我们能够方便高效的进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换,就如同...
  • saliven
  • saliven
  • 2001-03-09 13:12:00
  • 970

智能合约

智能合约是 1990s 年代由尼克萨博提出的理念,几乎与互联网同龄。由于缺少可信的执行环境,智能合约并没有被应用到实际产业中,自比特币诞生后,人们认识到比特币的底层技术区块链天生可以为智能合约提供可信...
  • u014345860
  • u014345860
  • 2017-08-23 15:59:15
  • 986

AntShares 区块链的智能合约(FunctionCode)外传数据

上篇说到了部署智能合约,可以部署可以调用,但是现阶段官方没有提供查询FunctionCode返回值的方法,不过不要紧,AntShares是个不错的开源区块链项目,要自己加入获取FunctionCode...
  • hellogv
  • hellogv
  • 2017-06-18 16:43:09
  • 4408

<重发> 如何打造安全的以太坊智能合约

智能合约语言 Solidity 自身与合约设计都可能存在漏洞。如果智能合约开发者疏忽或者测试不充分,而造成智能合约的代码有漏洞的话,就非常容易被黑客利用并攻击。-- 众享比特致谢转载自 | https...
  • F8qG7f9YD02Pe
  • F8qG7f9YD02Pe
  • 2018-03-16 00:00:00
  • 140

智能合约编写实例

前言本文主要介绍智能合约的工作原理及其部署过程。合约部署流程一般来说,部署智能合约的步骤为1: 启动一个以太坊节点 (例如geth或者testrpc)。 使用solc编译智能合约。 => 获得二进制代...
  • u013137970
  • u013137970
  • 2016-11-03 09:44:27
  • 17788

区块链2.0:智能合约

区块链2.0:智能合约 区块链2.0是对整个市场的去中心化,利用区块链技术来转换许多不同的资产而不仅仅是比特币,通过转让来创建不同资产单元的价值。   区块链技术的去中心化账本功能可以被用来注册...
  • wangdd_199326
  • wangdd_199326
  • 2017-06-15 21:03:57
  • 2187

智能合约的安全问题

前言本文主要总结以太坊智能合约的安全漏洞。新加坡国立大学的Loi Luu提出了现在的智能合约存在的几种安全漏洞1。然而,由于智能合约目前还只是初级阶段,相信各种安全问题会不断的发现。智能合约中的安全漏...
  • u013137970
  • u013137970
  • 2016-11-03 16:52:26
  • 3609

编写调试以太坊智能合约/blockchain

一、        智能合约IDE简介     目前以太坊上支持三种语言编写智能合约,     Solidity:类似JavaScript,这是以太坊官方推荐语言,也是最流行的智能合约...
  • cnsd_liuliu
  • cnsd_liuliu
  • 2016-11-18 17:16:48
  • 2389

智能合约中存在的3种最常见的误解

作为一名受欢迎的区块链平台的开发者,我们有时被问到类似以太坊的智能合约是否走多链路线。我总是回答说:没有,至少目前还没有。 但智能合约在区块链充满炒作的世界里都可以风靡一时,为什么以前不行呢?那么问...
  • wo541075754
  • wo541075754
  • 2018-01-15 09:28:25
  • 1365

ETH 基础篇 JAVA Web3j 智能合约

架构springboot   这里使用web3j当前eth官方推荐的集成jdk来做说明!当然你也可以使用它最底层的rpc方案来编写(官网也有说明)! 这里做个总结 第一步:先引入jdk [mav...
  • xf191
  • xf191
  • 2018-01-30 14:30:40
  • 1849
收藏助手
不良信息举报
您举报文章:智能合约验证签名
举报原因:
原因补充:

(最多只允许输入30个字)