• EOS 数据签名与公匙验证代码用例


      本文编写了一个小例子诠释了EOS是如何对数据签名与校验的,通过本文可以理解了签名的重要性和数据的不可篡改性。

      系统: ubuntu  版本为EOS1.1.1

          注:因为本文的程序是把EOS里面的钱包和fc工具的代码全部提取出来编译的,这个过程相对复杂本文不做解释,这里只注重本文的内容,但我的示例代码是来自于此用例的片段。

    一.测试代码

    std::string dig_str("test");

    public_key_type pubk(std::string("EOS62M5kVouCEU31xP736Txb4pe82FoncprqevPuagE6boCLxwsC8")); import_key("5KT27tqC9YgfEyqD61EjCFRz7QXTz8XrYadzZ8LAGt7HVovUHnT");
    fc::crypto::signature sig_stru
    = sign_digest(dig, pubk);
    unsigned
    char arr_sign[200]; unsigned char arr_pubkey[100]; memset(arr_sign, 0, sizeof(arr_sign)); memset(arr_pubkey, 0, sizeof(arr_pubkey)); fc::datastream<const unsigned char*> ds_sign( arr_sign, 200 ); fc::raw::pack( ds_sign, sig_stru); int siglen = ds_sign.tellp();

      std::cout << std::string(dig) << std::endl;
      std::cout << std::endl;

    
    

      std::cout << std::string(sig_stru) << std::endl;
      std::cout << std::endl;


    fc::datastream<const unsigned char*> ds_pub( arr_pubkey, 100 );
    for(int i = 0; i < siglen; ++i) { printf("%02x", arr_sign[i]); } std::cout << std::endl; fc::raw::pack( ds_pub, pubk); int publen=ds_pub.tellp(); for(int i = 0; i < publen; ++i) { printf("%02x", arr_pubkey[i]); } std::cout << std::endl; assert_recover_key( dig, arr_sign , siglen,arr_pubkey, publen );

    代码运行的结果如下:

    9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08

    SIG_K1_KkSbKuDSV7x87FeexJ3goinHsd3MhPBCH91MRqhyS3Z7H1v4HtUZoJc6AkgYWW5mEan7UbdmDAzDpCzUwheDPxRxtzuD8s

    002077f62e26587bd9345c61cf1904f048b1e2cd252acae79d3c1e6f48a4aef9bd03778ae7498014e9207e35117cf79920b2e31becb7e24d6c09ebeaf184c956c81c

    000295898b10fe9f5f056ed09e334e8e768e42cc16c1a2e02565de6d57b5e25cfd55

    二.代码分析

    1.数据发送端

    首先,我们对字符串"test"形成摘要,摘要转换成字符串为:

    9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08

    然后再对摘要使用私匙签名,转换成字符串形式输出如下:

    SIG_K1_KkSbKuDSV7x87FeexJ3goinHsd3MhPBCH91MRqhyS3Z7H1v4HtUZoJc6AkgYWW5mEan7UbdmDAzDpCzUwheDPxRxtzuD8s

    但实际上上面的数据存在于arr_sign中的数据(对应的datastream也可),我这里按字节打印成十六进制是如下格式的:

    002077f62e26587bd9345c61cf1904f048b1e2cd252acae79d3c1e6f48a4aef9bd03778ae7498014e9207e35117cf79920b2e31becb7e24d6c09ebeaf184c956c81c

    到这里我们得到了数据test内容的签名,那么发送数据方就可以使用明文test再加上签名进行发送给对端,签名一般使用十六进制。

    2.数据接收端

    在上面的用例中,我们直接调用EOS即assert_recover_key就可得到验证,那么如何是在合约中呢?

    如果数据在合约中验证,我们需要三个字段:发送的明文+签名+用户公匙

    明文就是test,签名就是 002077f62e26587bd9345c61cf1904f048b1e2cd252acae79d3c1e6f48a4aef9bd03778ae7498014e9207e35117cf79920b2e31becb7e24d6c09ebeaf184c956c81c

    公匙就是000295898b10fe9f5f056ed09e334e8e768e42cc16c1a2e02565de6d57b5e25cfd55,而不是EOS62M5kVouCEU31xP736Txb4pe82FoncprqevPuagE6boCLxwsC8,其实这么说不准确,只是前者是以十六进制体现出来的。

    现在接收端收到的内容是:test + 002077f62e26587bd9345c61cf1904f048b1e2cd252acae79d3c1e6f48a4aef9bd03778ae7498014e9207e35117cf79920b2e31becb7e24d6c09ebeaf184c956c81c

    (用户的公匙理论上是接收方本来就知道的)

    那么现在合约中代码就只需要转换一下格式即可:

     1 struct sig_hash_key {
     2          transaction_id_type dig;
     3          public_key pk;
     4          signature sig;
     5  };
     6 
     7 sig_hash_key sh;
     8 public_key pk;
     9 
    10 sha256(const_cast<char*>(userdata.data()),userdata.length(), &sh.dig);
    11 
    12 from_hex( sig, (char*)sh.sig.data, sig.length() );
    13 from_hex( pubk, sh.pk.data, pubk.length());
    14 
    15 assert_recover_key( &sh.dig,  (const char*)&sh.sig, sizeof(sh.sig), sh.pk.data, sizeof(sh.pk) );

    下面是一个将十六进制字符串转换成字符数组的函数,来自于fc工具中,加上去即可。

     1 size_t from_hex( const std::string& hex_str, char* out_data, size_t out_data_len ) {
     2         std::string::const_iterator i = hex_str.begin();
     3         uint8_t* out_pos = (uint8_t*)out_data;
     4         uint8_t* out_end = out_pos + out_data_len;
     5         while( i != hex_str.end() && out_end != out_pos ) {
     6           *out_pos = from_hex( *i ) << 4;
     7           ++i;
     8           if( i != hex_str.end() )  {
     9               *out_pos |= from_hex( *i );
    10               ++i;
    11           }
    12           ++out_pos;
    13         }
    14         return out_pos - (uint8_t*)out_data;
    15     }
    16 
    17 
    18 uint8_t from_hex( char c ) {
    19       if( c >= '0' && c <= '9' )
    20         return c - '0';
    21       if( c >= 'a' && c <= 'f' )
    22           return c - 'a' + 10;
    23       if( c >= 'A' && c <= 'F' )
    24           return c - 'A' + 10;
    25 
    26         eosio_assert( false, " invalid char 
    ");
    27         return 0;
    28     }

    到此为即,我们实现了数据的签名验证,也可以将此功能加在其它项目中以增加数据的安全性。

    
    
    
  • 相关阅读:
    函数式编程之Functional.js源码解析(一)
    libevent的bufferevent
    Luna
    线程池的实现
    如何排查字节对齐问题引起的程序诡异崩溃
    GCC编译之如何控制共享文件导出符号
    ubuntu禁用独显的问题
    protobuf的一些细节
    GCC编译之新老版本共存
    libevent的evbuffer之减少内存拷贝
  • 原文地址:https://www.cnblogs.com/hbright/p/9408857.html
Copyright © 2020-2023  润新知