AES-ECC数字信封协议与接口文档



  • AES-ECC数字信封协议文档

    概述

    AES-ECC数字信封加密算法库,是基于AES256,以及椭圆曲线secp256k1实现的对任意字节序列实现数字信封加密的算法库。

    对数据使用AES对称密钥$key$进行加密,再通过椭圆曲线算法,使用公钥$pubKey$加密$key$,实现数据加密。解密过程与之相反,通过公钥对应的私钥$priKey$解密,得到对称密钥$key$,再通过$key$解密得到原始数据。

    AES算法说明

    AES(Advanced Encryption Standard,高级加密标准),是一种常见的加密算法,采用对称分组密码体制。本算法库采用的是AES-256-CBC-PKCS7Padding加密方式,此种方式下加解密需要一个16字节的初始化向量(Initialzation Vector, IV),同时密钥的长度为256位,即32字节;并采用CBC模式加密,即先将明文切分成若干个小段,每一个小段与初始块或上一段的密文进行异或运算后,再与密钥进行加密;采用PKCS7Padding填充模式,保证明文分段时,对于数据不齐时,对数据按字节进行补齐,若数据本身已经对齐,则会填充一块长度为块大小的数据,每个字节都是块大小。

    算法具体描述如下:

    明文分段

    对于任意长度的明文$Plaintext$,首先根据PKCS7Padding进行补齐,补齐后将数据分段,每块大小为16字节。分段后得到n个明文段,即$Plaintext_1,...,Plaintext_n$。

    加密数据

    1. $Plaintext_1$与IV进行异或运算,得到$Input_1$做为AES加密算法的输入,与密钥$key$进行计算,得到密文$Ciphertext_1$。
    2. $Plaintext_2$与$Ciphertext_1$进行异或运算,得到$Input_2$做为AES加密算法的输入,与密钥$key$进行计算,得到密文$Ciphertext_2$。
    3. 循环往复,直至将所有的明文段加密完,最后一次得到密文$Ciphertext_n$。
    4. 将$Ciphertext_1,...,Ciphertext_n$拼接在一起,即为最终的密文$Ciphertext$。

    加密AES-256-CBC-PKCS7Pandding

    解密数据

    1. 将$Ciphertext$按16字节,划分成n个密文段$Ciphertext_1,...,Ciphertext_n$。
    2. $Ciphertext_1$与密钥$key$进行解密运算,得$Output_1$,$Output_1$与IV进行异或,得到第一段明文$Plaintext_1$。
    3. $Ciphertext_2$与密钥$key$进行解密运算,得到$Output_2$,$Output_2$与$Ciphertext_1$进行异或,得到第二段明文$Plaintext_2$。
    4. 循环往复,即可得到左右的明文段,最后将$Plaintext_1,...,Plaintext_n$拼接在一起,即可得到最终的明文$Plaintext$。

    解密AES-256-CBC-PKCS7Pandding

    ECC算法说明

    ECC(Elliptic Curve Cryptography,椭圆曲线密码学),基于有限域的椭圆曲线和复杂的椭圆曲线离散对数,实现的一种非对称加密系统。

    在本算法库中的椭圆曲线加密算法是基于secp256k1实现的。 大多数常用的曲线都具有随机结构,但 secp256k1 是以特殊的非随机方式构建的,因此可以实现特别高效的计算。因此,如果实现充分优化,它通常比其他曲线快 30% 以上。

    而本算法库所实现的是基于椭圆曲线的一种集成加密方案,ECIES(elliptic curve integrate encrypt scheme)。其中密钥派生函数、校验码的生成算法和对称加密方案是根据需要设计的,本算法库选择的密钥派生函数为SHA512哈希函数,校验码生成算法为SHA256哈希函数,对称加密方案选择的是AES/CBC/PKCS7Padding。

    密钥生成

    在椭圆曲线$Ep(a,b)$中选择两个点$G,K$,满足$K=kG$,$n$为$G$的阶$(nG=O_{\infty})$,$k$为小于$n$的整数。则给定$k$和$G$,计算$K$是很容易的,而给定$K$和$G$计算$k$是非常困难的,同时由于$n,p$的取值是非常大的,因此想要把各个点逐一算出是不可能的,基于此构建非对称密码算法。

    1. 在椭圆曲线上随机选取一点$G$,称为基点。
    2. $k(k<n)$为私钥。
    3. $K$为公钥。

    在使用椭圆曲线进行加密时,用户需要公开自己的公钥$K$,其他人可以通过公钥加密明文数据得到密文发送给用户,而用户可以通过自己的私钥$k$进行解密。而其他人在不知道私钥的情况下是无法解密的。

    加密算法

    为加密信息$m$,需要以下步骤:

    1. 生成一个随机数$r\in[1,n-1]$,并计算$R=rG$。
    2. 将秘密$S$映射到椭圆曲线上,令$S=P_x$,其中$P=(P_x, P_y)=rK(P\neq O_{\infty})$。
    3. 使用哈希算法SHA512,对秘密$S$进行哈希运算,得到的值分别做为对称加密的密钥以及校验密文是否正确的密钥,$k_E||k_M=\mathsf{SHA512}(S)$。
    4. 对秘密进行加密$c=E(k_E;m)$
    5. 计算加密信息的校验码$d=\mathsf{SHA256}(k_M;c)$
    6. 输出$R||c||d$。

    解密算法

    当获得$R||c||d$后执行解密:

    1. 求出$P=(P_x,P_y)=kR$,因为$P=kR=krG=rkG=rK$,其中$S=P_x$。
    2. 生成加密密钥和校验密钥$k_E||k_M=\mathsf{SHA512}(S)$。
    3. 计算校验码是否正确,即$d$与$\mathsf{SHA256}(k_M;c)$。
    4. 使用对称密钥进行解密$m=D(k_E;c)$。

    AES-ECC SDK 接口文档

    通过java和JS两种语言,实现了数字信封的SDK,并且两种语言的SDK可以实现互相加解密。

    对于明文采用AES进行加密,对加密中使用的对称密钥,利用椭圆曲线中的ECIES进行加密(公钥加密),加密后得到关于明文的AES密文,以及关于对称密钥的ECIES密文。解密的过程与此相反,通过ECIES进行解密(私钥解密),得到AES的对称密钥,利用此密钥对AES的密文进行解密,得到明文。

    JAVA SDK

    接口说明

    public class envelop{}
    

    将数字信封封装在envelop中。通过实例化envelop对象,完成数字信封的相关功能。

    public void setSymKey(String key)
    

    传入AES的对称密钥。

    public void setEciesPubKey(String pk)
    public void setEciesSecKey(String sk)
    

    传入ECC的公钥/私钥。

    public String getSymKey()
    

    获取当前AES的密钥。

    public String getEciesPubKey()
    public String getEciesSecKey()
    

    获取当前ECC的公钥/私钥。

    public String encryptMsg(String msg) throws Exception
    public String decryptMsg(String encMsg) throws Exception
    

    使用AES对明文进行加密,返回密文;以及使用AES对密文进行解密,返回解密后的明文。

    public String encryptKey(String key) throws Exception
    public String decryptKey(String encKey) throws Exception
    

    使用ECC中的ECIES加密算法,对明文(在数字信封中为AES的对称密钥)进行加密,返回密文(在数字信封中为AES对称密钥的密文);以及使用ECIES解密算法对密文进行解密,返回明文。

    具体实例

    实例化对象

    envelop env = new envelop();
    

    实例化一个数字信封对象,此对象会通过构造函数,分配AES加密的密钥以及ECC的公私钥对。

    数字信封加密

    String encMsg = env.encryptMsg(msg);
    String symKey = env.getSymKey();
    String encKey = env.encryptKey(symKey);
    

    AES加密明文,通过调用getSymKey()方法,获得AES的密钥,对于对称密钥使用椭圆曲线进行加密。

    数字信封解密

    String decKey = env.decryptKey(encKey);
    env.setSymKey(decKey);
    String decMsg = env.decryptMsg(encMsg);
    

    使用椭圆曲线进行解密,得到AES的对称密钥,调用setSymKey()方法,将AES对称密钥传入对象中,然后使用AES进行解密,得到的decMsg即为解密后的密文。

    JavaScript SDK

    实现JS的SDK,分别用到了aes.js、ecc-ecies.js以及secp256k1.js,其中secp256k1.js提供了椭圆曲线中secp256k1这条曲线的相关参数和接口。

    接口说明(aes.js)

    生成密钥

    function generateKey()
    function StringCodeParse()
    

    generateKey() 会随机生成16字节(256位)的十六进制的字符串(小写字母)。

    StringCodeParse() 将以上生成的字符串(大写字母,对于字符串可以用toUpperCase()函数进行转换)转换成适用于aes的密钥。

    加密

    function encrypt(plaintext, key, iv)
    

    加密函数,其中plaintext接收的是明文数据的字符串,key是由StringCodeParse()转换的密钥,iv是填充向量。由于java版本和JS版本采用的是不同的填充格式,因此为保证二者互通,指定iv向量的值iv = "30313233343536373839414243444546"

    解密

    function decrypt(ciphertext, key, iv)
    

    解密函数,其中ciphertext是密文字符串,key是由StringCodeParse()转换的密钥,iv是填充向量。由于java版本和JS版本采用的是不同的填充格式,因此为保证二者互通,指定iv向量的值iv = "30313233343536373839414243444546"

    接口说明(ecc-ecies.js)

    生成公私钥对

    function generateSK()
    function generatePK(sk)
    

    generateSK() 生成私钥,generatePK(sk)根据私钥生成公钥,其中sk是由generatePK(sk)直接生成的。

    function SK2Bytes(sk)
    function PK2Bytes(pk)
    function Bytes2Key(bytes)
    

    以上三个函数中,前两个分别是将私钥、公钥转成byte数组,最后一个是将byte数组转成用私钥/公钥的格式。

    加密

    function encrypt(pub, msg)
    

    加密函数,pub为上文生成的公钥,msg为需要加密的明文字符串,函数的输出为Array数组。

    解密

    function decrypt(prv, cyph)
    

    解密函数,prv为上文生成的私钥,cyph为加密的Array数组,函数输出为解密结果的字符串。

    function StringToHex(bytes)
    

    将字符串形式的密文,转换成适合js解密的Array数组。

    具体实例

    var aes = require("./aes")
    var CryptoJS = require("crypto-js")
    var ecc = require("./ecc-ecies")
    

    引入对应的包。

    生成密钥

    var key = aes.generateKey()
    var iv = "30313233343536373839414243444546"
    var key1 = aes.StringCodeParse(key.toUpperCase())
    
    var secKey = ecc.generateSK()
    var pubKey = ecc.generatePK(secKey)
    

    其中key是生成的256位的随机序列,key1是转换后的AES密钥。secKey是椭圆曲线的私钥,pubKey是椭圆曲线的公钥。

    加密

    var msg = "This is a plaintext message from alice to bob"
    var encMsg = aes.encrypt(msg, key1, iv)
    var encKey = ecc.encrypt(pubKey, key)
    

    对明文msg使用AES进行加密,得到encMsg,对key使用椭圆曲线进行加密,得到encKey

    解密

    var decKey = ecc.decrypt(secKey, encKey)
    decKey =  aes.StringCodeParse(decKey)
    var decMsg = aes.decrypt(encMsg, decKey, iv)
    console.log(decMsg)
    

    encKey进行解密,得到AES对称密钥,解密的密钥使用StringCodeParse()函数进行转换,以适应在JS的AES中进行解密。得到的decKey,使用椭圆曲线进行解密,最后打印到控制台。


    JAVA和JS互通调用实例

    JAVA加密,JS解密

    java部分:

    envelop env = new envelop();
    String msg = "hello world!";
    String encMsg = env.encryptMsg(msg);
    String symKey = env.getSymKey();
    String encKey = env.encryptKey(symKey);
    
    System.out.println("##encMsg:");
    System.out.println(encMsg);
    System.out.println("##encKey:");
    System.out.println(encKey);
    System.out.println("##secKey:");
    System.out.println(env.getEciesSecKey());
    

    输出结果:

    ##encMsg:
    8cf8d9b71da857a75ee5ad683f6a2ba5
    ##encKey:
    eaed4c48657ec38ca12eafbdf674c7653df801e8f115cd7e895f8a0a8034eb54e3b18ba30abf2bfc61c923b75288444844ec5b926e4d508760b7d2b41672525addb691cfb39c40c2e92bf0ef9c62e21acd4cd44ede52ffa425d4a4b6c96ce521996fe2d69b21537defc668269faadd981290e7d8d631d5b7ed73fb0ff436b2762e7a3976ea358b46a653a85d4f57eb44
    ##secKey:
    30dba07390235aa9da79fdc7778fe256c0058bc0cb12a64e943510735a2c8aaf
    

    js解密:

    var aes = require("./aes")
    var CryptoJS = require("crypto-js")
    var ecc = require("./ecc-ecies")
    var secp256k1 = require("./secp256k1")
    
    var iv = aes.iv
    var encMsg = "8cf8d9b71da857a75ee5ad683f6a2ba5"
    var encKey = "eaed4c48657ec38ca12eafbdf674c7653df801e8f115cd7e895f8a0a8034eb54e3b18ba30abf2bfc61c923b75288444844ec5b926e4d508760b7d2b41672525addb691cfb39c40c2e92bf0ef9c62e21acd4cd44ede52ffa425d4a4b6c96ce521996fe2d69b21537defc668269faadd981290e7d8d631d5b7ed73fb0ff436b2762e7a3976ea358b46a653a85d4f57eb44"
    var secKey = "30dba07390235aa9da79fdc7778fe256c0058bc0cb12a64e943510735a2c8aaf"
    secKey = secp256k1.uint256(secKey, 16)
    var encKey = ecc.StringToHex(encKey.toUpperCase())
    
    var decKey = ecc.decrypt(secKey, encKey)
    decKey =  aes.StringCodeParse(decKey)
    var decMsg = aes.decrypt(encMsg, decKey, iv)
    console.log(decMsg)
    

    输出结果:

    hello world!
    

    值得注意的是,在secKey做为字符串不能直接做为私钥进行解密,需要使用secp256k1.js中的函数进行映射,将其映射到椭圆曲线上,以适应解密。

    JS加密,JAVA解密

    JS加密部分:

    var aes = require("./aes")
    var CryptoJS = require("crypto-js")
    var ecc = require("./ecc-ecies")
    var secp256k1 = require("./secp256k1")
    
    var key = aes.generateKey()
    var iv = aes.iv
    var key1 = aes.StringCodeParse(key.toUpperCase())
    var secKey = ecc.generateSK()
    var pubKey = ecc.generatePK(secKey)
    
    var msg = "This is a plaintext message from alice to bob"
    var encMsg = aes.encrypt(msg, key1, iv)
    var encKey = ecc.encrypt(pubKey, key)
    
    console.log("sk:")
    console.log(secKey)
    console.log("encMsg:")
    console.log(encMsg)
    console.log("encKey:")
    console.log(secp256k1.Bytes2Hex(encKey))
    

    加密后的输出结果

    sk:
    <BN: 5965f0ac0cbc8c1de77d788df14586b494485816ede997ca3ac1365b52076eb9>
    encMsg:
    45f15773c2d848e6529f7982729c01fb0c1409de130146977b393851f13afe630093e4ff0f0943ecd235edf2a2745475
    encKey:
    53225863dbddfb98ae018c9790e57057cddd8a1b0ecf48bf12238da1c90be013b0397cc9ea8f0a0630a8dc903a9c7d3f93e8fa8a150e720eab4cf9af00ce1d12229ba87a99f8440e22d6ed3aec85d4fa964b6b508fee05ea18f5ab152b8f6543d68b0cedbb4ad5e97ede7c38089b34ad76bb47581aa89b0e0cb261e929edba7d55dd671cad8c56004fe16068ea540aad
    

    java解密:

    public void testDecrypt() throws Exception {
         envelop env = new envelop();
         String sk = "5965f0ac0cbc8c1de77d788df14586b494485816ede997ca3ac1365b52076eb9";
         env.setEciesSecKey(sk);
         String encMsg = "45f15773c2d848e6529f7982729c01fb0c1409de130146977b393851f13afe630093e4ff0f0943ecd235edf2a2745475";
         String encKey = "53225863dbddfb98ae018c9790e57057cddd8a1b0ecf48bf12238da1c90be013b0397cc9ea8f0a0630a8dc903a9c7d3f93e8fa8a150e720eab4cf9af00ce1d12229ba87a99f8440e22d6ed3aec85d4fa964b6b508fee05ea18f5ab152b8f6543d68b0cedbb4ad5e97ede7c38089b34ad76bb47581aa89b0e0cb261e929edba7d55dd671cad8c56004fe16068ea540aad";
        
         String decKey = env.decryptKey(encKey);
         System.out.println("##decKey:");       
         System.out.println(decKey);
         env.setSymKey(decKey);
            
         String decMsg = env.decryptMsg(encMsg);
         System.out.println("##decMsg:");
         System.out.println(decMsg);
    }
    

    解密后的输出结果:

    ##decKey:
    E59C191F3EB84A6B5E7C144F3DAFEA1E
    ##decMsg:
    This is a plaintext message from alice to bob
    


  • @昌用 可喜可贺!


Log in to reply