eos中签名验签流程和eosjs中的加解密原理

关键词:eos 签名 验签 ecc dsa 加密 解密 eosjs aes

本文主要探讨两方面

1.eosjs中用密钥对进行加解密功能
2.eos中密钥对生成,签名和验签过程(私钥签名 公钥验签)

常用的加密算法

对称性加密算法

对称式加密就是加密和解密使用同一个密钥,信息接收双方都需事先知道密匙和加解密算法,之后便是对数据进行加解密了.对称加密算法用来对敏感数据等信息进行加密。

对称性加密算法有:AES、DES、3DES
DES(Data EncryptionStandard):数据加密标准,速度较快,适用于加密大量数据的场合.

3DES(Triple DES):是基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高.

AES(Advanced EncryptionStandard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高;AES是一个使用128为分组块的分组加密算法,分组块和128 192或256位的密钥一起作为输入,对4×4的字节数组上进行操作.众所周之AES是种十分高效的算法,尤其在8位架构中,这源于它面向字节的设计.AES 适用于8位的小型单片机或者普通的32位微处理器,并且适合用专门的硬件实现,硬件实现能够使其吞吐量(每秒可以到达的加密/解密bit数)达到十亿量级.同样,其也适用于RFID系统

非对称算法

非对称式加密就是加密和解密所使用的不是同一个密钥,通常有两个密钥,称为"公钥"和"私钥",它们两个必需配对使用,否则不能打开加密文件.发送双方A,B事先均生成一堆密匙,然后A将自己的公有密匙发送给B,B将自己的公有密匙发送给A,如果A要给B发送消息,则先需要用B的公有密匙进行消息加密,然后发送给B端,此时B端再用自己的私有密匙进行消息解密,B向A发送消息时为同样的道理

非对称性算法有:RSA、DSA、ECC

RSA:由 RSA 公司发明,是一个支持变长密钥的公共密钥算法,需要加密的文件块的长度也是可变的.RSA在国外早已进入实用阶段,已研制出多种高速的RSA的专用芯片.

DSA(Digital SignatureAlgorithm):数字签名算法,是一种标准的DSS(数字签名标准),严格来说不算加密算法.

ECC(Elliptic CurvesCryptography):椭圆曲线密码编码学.ECC和RSA相比具有多方面的绝对优势,主要有:抗攻击性强.相同的密钥长度,其抗攻击性要强很多倍.计算量小,处理速度快.ECC总的速度比RSA、DSA要快得多.存储空间占用小.ECC的密钥尺寸和系统参数与RSA、DSA相比要小得多,意味着它所占的存贮空间要小得多.这对于加密算法在IC卡上的应用具有特别重要的意义.带宽要求低.当对长消息进行加解密时,三类密码系统有相同的带宽要求,但应用于短消息时ECC带宽要求却低得多.带宽要求低使ECC在无线网络领域具有广泛的应用前景

散列算法

散列算法,是一种单向的不可逆的加密算法.它对不同长度的输入消息,产生固定长度的输出.多用于网络传输过程验证数据的完整性

散列算法(签名算法)有:MD5、SHA1
用途:主要用于验证,防止信息被修.具体用途如:文件校验、数字签名、鉴权协议

总结

对称加密算法,速度快,安全性低,目前大量数据加密建议采用对称加密算法,提高加解密速度
非对称加密算法,速度较慢,安全性高,小量的机密数据,可以采用非对称加密算法。

实际工作中常用的方式是采用非对称加密算法管理对称算法的密钥,然后用对称加密算法加密数据,这样我们就集成了两类加密算法的优点,既实现了加密速度快的优点,又实现了安全方便管理密钥的优点。

eosjs中用密钥对进行加解密功能

场景一
AES的Key经过接收方公钥加密和AES加密的内容 一起发送给接收方,接收方通过自己私钥先将加密后的AES_KEY解密,再通过解密得到的原始AES_KEY,并用该key解密发送方发送的内容,得到明文

结论 这种算法是目前市面上常用的,既增加了安全性又提升了加密速度

场景二
先来看一下ECC 数学函数 Q=dG; (Q是公钥 d是私钥 G是他们之间的关系);Q1 = d1G1; Q2=d2G2;那么能推出 key=Q1d2G2 = Q2d1G1;

结论 1的公钥和2的私钥 2的公钥和1的私钥 他们能得到一个相同的值 key,这个相同的key 作为他们之间AES加减密的key eosjs就是通过这种方式实现的    

通过对eosjs中ecc库的测试 结果跟上边的场景二是一致的

我们通过分析源码来看一下

someonesPrivateKey = ecc.seedPrivate("someone");
someonesPublicKey = ecc.privateToPublic(someonesPrivateKey);

console.log(‘someonesPrivateKey:\t‘, someonesPrivateKey.toString())
console.log(‘someonesPublicKey:\t‘, someonesPublicKey.toString())

myPrivate = ecc.seedPrivate("my");
myPublic = ecc.privateToPublic(myPrivate);

console.log(‘myPrivate:\t‘, myPrivate.toString())
console.log(‘myPublic:\t‘, myPublic.toString())

encryptedMessage = ecc.Aes.encrypt(myPrivate, someonesPublicKey, message)//MY用自己的私钥和someone的公钥进行加密
decryptedMessage = ecc.Aes.decrypt(someonesPrivateKey, myPublic, encryptedMessage.nonce, encryptedMessage.message, encryptedMessage.checksum)//someone用自己的私钥和my的公钥进行解密

随机生成两对密钥对

最终要的是加密encrypt和解密decrypt这两个函数

function encrypt(private_key, public_key, message) {
   var nonce = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : uniqueNonce();

   return crypt(private_key, public_key, nonce, message);
}

function decrypt(private_key, public_key, nonce, message, checksum) {
   return crypt(private_key, public_key, nonce, message, checksum).message;
}

他们两个其实都是通过一个crypt函数实现的,我们看一下crypt的具体实现流程

function crypt(private_key, public_key, nonce, message, checksum) {

   private_key = PrivateKey(private_key);//检查私钥的合理性,是否符合sha256x2, K1等算法要求,这一部分稍后会详细的跟踪一下
   if (!private_key) throw new TypeError(‘private_key is required‘);

   public_key = PublicKey(public_key);//验证公钥的合法性,这里对公钥进行了去除EOS头的处理
   if (!public_key) throw new TypeError(‘public_key is required‘);

   nonce = toLongObj(nonce);//随机或唯一uint64在重新使用相同的私钥/公钥时提供熵,这个nonce是通过时间生成的,我测试了如果 不加这个逻辑加解密也没问题,应该是增加安全性的吧,后续会再继续跟踪
   if (!nonce) throw new TypeError(‘nonce is required‘);
   console.log(‘nonce:\t‘, nonce.toString())

   //这一部分是将要加密的格式转换成封装的Buffer格式,Buffer支持"ascii" | "utf8" | "utf16le" | "ucs2" | "base64" | "latin1" | "binary" | "hex"编码格式
   if (!Buffer.isBuffer(message)) {
       if (typeof message !== ‘string‘) throw new TypeError(‘message should be buffer or string‘);
       message = new Buffer(message, ‘binary‘);
   }
   if (checksum && typeof checksum !== ‘number‘) throw new TypeError(‘checksum should be a number‘);

   var S = private_key.getSharedSecret(public_key);//获取共享密钥key,这个key在函数中调用了一次hash.sha512
   var ebuf = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);//
   ebuf.writeUint64(nonce);
   ebuf.append(S.toString(‘binary‘), ‘binary‘);
   ebuf = new Buffer(ebuf.copy(0, ebuf.offset).toBinary(), ‘binary‘);
   var encryption_key = hash.sha512(ebuf);//对数据有进行了一次hash

   // D E B U G
   console.log(‘crypt‘, {
       priv_to_pub: private_key.toPublic().toString(),
       pub: public_key.toString(),
       nonce: nonce.toString(),
       message: message.length,
       checksum,
       S: S.toString(‘hex‘),
       encryption_key: encryption_key.toString(‘hex‘),
   })

   var iv = encryption_key.slice(32, 48);//获取AES需要的IV初始向量
   var key = encryption_key.slice(0, 32);//获取AES的密钥

   //获取共享秘密校验和,这个用来校验两端获取的key是否一致
   var check = hash.sha256(encryption_key);
   check = check.slice(0, 4);
   var cbuf = ByteBuffer.fromBinary(check.toString(‘binary‘), ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
   check = cbuf.readUint32();

   if (checksum) {
       if (check !== checksum) throw new Error(‘Invalid key‘);
       message = cryptoJsDecrypt(message, key, iv);//aes解密
   } else {
       message = cryptoJsEncrypt(message, key, iv);//aes加密
   }
   return { nonce: nonce, message: message, checksum: check };
}

这里边最终要的一个函数 就是getSharedSecret,通过这个接口 就实现了场景二中提到的 1的公钥和2的私钥 2的公钥和1的私钥 他们能得到一个相同的key

看一下他内部做了一些什么操作

function getSharedSecret(public_key) {
       public_key = PublicKey(public_key);//验证公钥的合法性,这里对公钥进行了去除EOS头的处理
       var KB = public_key.toUncompressed().toBuffer();//获取公开密钥K
       var KBP = Point.fromAffine(secp256k1, BigInteger.fromBuffer(KB.slice(1, 33)), // x
       BigInteger.fromBuffer(KB.slice(33, 65)) // y
       );//获取K的椭圆曲线上的映射点(x,y)
       var r = toBuffer();
       var P = KBP.multiply(BigInteger.fromBuffer(r));//KBP对应的(x,y)分别和r进行相乘获取新的点point
       var S = P.affineX.toBuffer({ size: 32 });//新生成的point的X坐标
       // SHA512 used in ECIES
       return hash.sha512(S);
   }

具体的aes加密和解密函数

function cryptoJsDecrypt(message, key, iv) {
   message.toString(),key.toString(‘hex‘), iv.toString(‘hex‘))
   assert(message, "Missing cipher text");
   message = toBinaryBuffer(message);
   var decipher = crypto.createDecipheriv(‘aes-256-cbc‘, key, iv);
   // decipher.setAutoPadding(true)
   message = Buffer.concat([decipher.update(message), decipher.final()]);
   return message;
}

function cryptoJsEncrypt(message, key, iv) {
   message.toString(),key.toString(‘hex‘), iv.toString(‘hex‘))
   assert(message, "Missing plain text");
   message = toBinaryBuffer(message);
   var cipher = crypto.createCipheriv(‘aes-256-cbc‘, key, iv);
   // cipher.setAutoPadding(true)
   message = Buffer.concat([cipher.update(message), cipher.final()]);
   return message;
}

对应java也有封装的库可以调用

对应的aes-256-cbc为aes的256位的cbc模式 这个后续会继续介绍

eos中密钥对生成,签名和验签过程

1.密钥对生成

当我们在终端输入下面命令时 可以获取一对密钥对

```

[email protected]:~/9f-git/eosjs/api/local$ cleos create key --to-console

Private key: 5HptWorg6Q8ao3i7i1AjWnEoou1AZwoaTEkfpo1LeXrT9afBawS

Public key: FZS7BoDwkm4oZiWobX3HH9wtJ27a42RQuJm6en2ZnJfX1K4yDyKTV


让我们看一下内部是如何实现的
cleos对应main.cpp中如下代码

bool r1 = false;

string key_file;

bool print_console = false;

// create key

auto create_key = create->add_subcommand("key", localized("Create a new keypair and print the public and private keys"))->set_callback( &r1, &key_file, &print_console{

if (key_file.empty() && !print_console) {

std::cerr << "ERROR: Either indicate a file using "--file" or pass "--to-console"" << std::endl;//当--file和--to-console都没有指定的时候 退出

return;

}

 auto pk    = r1 ? private_key_type::generate_r1() : private_key_type::generate();//默认K1方式 当参数制定--ri时 采用r1方式
  auto privs = string(pk);
  auto pubs  = string(pk.get_public_key());
  if (print_console) {
     std::cout << localized("Private key: ${key}", ("key",  privs) ) << std::endl;
     std::cout << localized("Public key: ${key}", ("key", pubs ) ) << std::endl;
  } else {
     std::cerr << localized("saving keys to ${filename}", ("filename", key_file)) << std::endl;
     std::ofstream out( key_file.c_str() );
     out << localized("Private key: ${key}", ("key",  privs) ) << std::endl;
     out << localized("Public key: ${key}", ("key", pubs ) ) << std::endl;
  }

});

ecc的ri和k1方式区别在哪儿 我们继续跟踪

template< typename KeyType = ecc::private_key_shim >//这里是定义在ecc命令空间中

static private_key generate() {

return private_key(storage_type(KeyType::generate()));

}

     template< typename KeyType = r1::private_key_shim >//这里定义在fc::ri的命名空间中
     static private_key generate_r1() {
        return private_key(storage_type(KeyType::generate()));
     }
可以两个定义在不同的命名空间中
下面我们具体分析这两种命名空间中具体代码的实现区别

ecc命名空间中的调用代码

private_key private_key::generate()

{

EC_KEY* k = EC_KEY_new_by_curve_name( NID_secp256k1 );//首先通过椭圆曲线的标识符NID_secp256k1生成一个EC_KEY,通过这种方式生成的EC_KEY里已经包含了椭圆曲线的参数。否则,需要手动设置EC_GROUP

if( !k ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" );

if( !EC_KEY_generate_key( k ) )//生成私钥和公钥

{

FC_THROW_EXCEPTION( exception, "ecc key generation error" );

   }

   return private_key( k );
}  
ri命名空间中的实现代码

private_key private_key::generate()

{

private_key self;

EC_KEY* k = EC_KEY_new_by_curve_name( NID_X9_62_prime256v1 );//首先通过椭圆曲线的标识符NID_X9_62_prime256v1生成一个EC_KEY,通过这种方式生成的EC_KEY里已经包含了椭圆曲线的参数。否则,需要手动设置EC_GROUP

if( !k ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" );

self.my->_key = k;

if( !EC_KEY_generate_key( self.my->_key ) )//生成私钥和公钥

{

FC_THROW_EXCEPTION( exception, "ecc key generation error" );

   }
通过跟踪标识符的定义发现还有好多种不同的曲线算法,不同的椭圆曲线只有参数上的不同。所以,算出正确签名的前提是设置正确的参数

fc::sha256 private_key::get_secret( const EC_KEY * const k )

{

if( !k )

{

return fc::sha256();

}

   fc::sha256 sec;
   const BIGNUM* bn = EC_KEY_get0_private_key(k);
   if( bn == NULL )
   {
     FC_THROW_EXCEPTION( exception, "get private key failed" );
   }
   int nbytes = BN_num_bytes(bn);
   BN_bn2bin(bn, &((unsigned char*)&sec)[32-nbytes] );
   return sec;
}

```

签约和验签流程后续会补充

原文地址:https://www.cnblogs.com/wanglishuai/p/10168117.html

时间: 2024-10-10 18:07:43

eos中签名验签流程和eosjs中的加解密原理的相关文章

非对称加密解密与签名验签的关系

首先看一下各自的定义: 加密:发送方利用接收方的公钥对要发送的明文进行加密. 解密:接受方利用自己的私钥进行解密. 签名:发送方用一个哈希函数从报文文本中生成报文摘要,然后用自己的私人密钥对这个摘要进行加密,得到的就是这个报文对应的数字签名.通常来说,发送方会把数字签名和报文原文一并发送给接受者. 验签:接收方得到原始报文和数字签名后,用同一个哈希函数从报文中生成摘要A,另外,用发送方提供的公钥对数字签名进行解密,得到摘要B,对比A和B是否相同,就可以得知报文有没有被篡改过. 两者的目的不同,加

RSACryptoServiceProvider加密解密签名验签和DESCryptoServiceProvider加解密

原文:RSACryptoServiceProvider加密解密签名验签和DESCryptoServiceProvider加解密 C#在using System.Security.Cryptography下有 DESCryptoServiceProvider RSACryptoServiceProvider  DESCryptoServiceProvider 是用于对称加密 RSACryptoServiceProvider是用于非对称加密  对称加密的意思:有一个密钥 相当于加密算法,加密用它来加

.net core RSA 分段加密解密,签名验签(对接java)

参考地址: https://www.cnblogs.com/stulzq/p/7757915.html https://www.cnblogs.com/stulzq/p/8260873.html https://github.com/stulzq/RSAExtensions(XC.RSAUtil) https://www.cnblogs.com/stulzq/p/12053976.html https://github.com/stulzq/RSAExtensions (RSAExtension

JAVA 实现 基于RSA算法的签名验签

基本步骤 签名方: 1用sha1算出原文的摘要 2用私钥对摘要进行加密 3对密文进行BASE64编码 验证方: 1对密文进行BASE64解码 2用公钥对解码后的密文解密 3用sha1对原文计算摘要并和解密后的明文比对 上干货 //参数字符串         String userId="2312sd";         String orderId="232djfj";         String price="12312";         

RSA签名验签

import android.util.Base64; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; /** * Author:JsonLu * DateTime:

UL例行检验(Regular Inspection)验厂流程和次数,IMP检验(加严检查)

UL例行检验(Regular Inspection)验厂流程和次数,IMP检验(加严检查) R类事先不通知检查次数原则上是4次/年,UL认为必要时,就不只是4次/年检查员在工作时间内,有权利进入工厂的相关区域:材料仓库/半成品仓库/生产车间/实验室/成品仓库等检验场所 L类事先不通知检查次数根据UL标签的使用量来检查,不同的产品差距特别大.检查员在工作时间内,有权利进入工厂的相关区域:材料仓库/半成品仓库/生产车间/实验室/成品仓库等检验场所 IMP检验:当严重或明显的违反UL的要求的情况时,工

openresty中使用私钥/公钥进行加密/解密/签名/验签。

对于公钥私钥的提取,详细请看http://www.cnblogs.com/dreamer-One/p/5621134.html另外付在线加解密工具链接:http://tool.chacuo.net/cryptrsaprikey--公钥local RSA_PUBLIC_KEY = [[ -----BEGIN RSA PUBLIC KEY----- MIGJAoGBAJ9YqFCTlhnmTYNCezMfy7yb7xwAzRinXup1Zl51517rhJq8W0wVwNt+ mcKwRzisA1S

加解密/签名验签

一.我加密.签名的过程 1,生成18位随机密钥:rand 067870-544583-448433: 2,使用对方的公钥证书(cer文件),并使用“RSA”算法,对随机密钥的字节数组加密,得到一个字节数组,将其转化为小写的十六进制字符串:secretKey 07ed7542951980385e32c149039255aabf8009e1e98a9432db19755e45e07361d318b98393d431cad4f656df7bc254548bacc92c53aa9566fd9ca1105

RSA签名验签学习笔记

RSA私钥签名时要基于某个HASH算法,比如MD5或者SHA1等.之前我一直认为签名的过程是:先对明文做HASH计算,然后用私钥直接对HASH值加密.最近才发现不是那么简单,需要对HASH后的数据进行BER编码再加密. 先看一个例子. 公钥模:89 54 E6 61 C1 52 DB ED 07 57 50 04 AD B3 D2 A7 A9 8F E8 D8 20 5B 01 B2 E5 E4 7A 7B EE 80 E3 C0 13 11 D2 F9 AD C3 CC 5F 1D 96 AC