概述
所谓非对称(Asymmetric)加密,就是一种使用两个密钥,来对信息进行加密和解密计算的算法体系。从其名称我们就可以理解,和对称加密体系中,加密和解密过程使用同一个密钥不同,非对称加密在加密和解密过程使用不同的密钥。这个特性,可以很好的解决加解密过程中,密钥共享和交换的问题,从而大大提高信息计算和处理的安全性。
非对称加密的密钥都是成对出现的,称为公钥(Public Key)和私钥(Private Key),公钥和私钥之间,其实是具有某种数学的关联关系,让其可以用于加解密算法当中。其中公钥是公开的,任何人都可以获取,通常使用公钥用于加密数据或验证数字签名;而私钥是保密的,只有密钥的拥有者知道,私钥用于解密数据或生成数字签名。
基于非对称加密的基本概念,在具体的实现路径上,还有不同的技术体系,如现在主流的非对称加密技术大体包括RSA和ECC两个大类。RSA算法的理论基础主要基于大数分解的数学难题;而ECC则是椭圆曲线上离散对数和点计算。它们的基本概念都是一种计算,在一个方向上可以非常简单和快速,而却很难从计算结果来推导输入的参数(最容易理解的就是两个非常大的素数相乘)。在早期由于RSA的概念清晰实现简单,相对应用比较广泛,但后来ECC具有安全性更强,相对密钥更小,计算性能更好等特性,已经逐渐成为主流的非对称加密体系。在基础技术框架和要素来看,其实这两大体系的实现和应用并没有太大的区别,比如都有公钥和私钥,有密钥对生成过程,都可以支持加密解密、签名验证等操作,都支持密钥协商等等,只是相关的参数和选项略有差异。
本文的主要目的并不是讨论一般的非对称加密的设计和实现,那样的材料和信息已经非常丰富了。而是笔者想要结合一些应用的经验,深入研究笔者在应用非对称加密过程中的一些问题,并提出一些想法和进行实践,能够更好的提升加密应用以至于信息安全方面的认知,从而有能力设计和改进现有的信息安全体系。
非对称加密的一般过程
笔者想要从非对称加密计算的一般过程入手。非对称加密一般应用过程和操作包括:
- 密钥对生成
在进行非对称加密之前,需要先生成密钥对。然后使用密钥对对信息进行加密和解密的操作。密钥对由公钥和私钥构成,它们由数学关系保证其是严格对应的,其中私钥应当由生成方进行保密存储和使用;而公钥可以对外公布和分发。
在大多数应用场景中,私钥的生成是相对固定的,它在生成之后,就以机密信息的形式,保存在持久化存储(比如一个文件或者配置信息中)中,然后在应用系统中加载使用。一般没有特别的原因,不需要保存公钥,因为可以很容易从私钥计算得到。
现代计算机技术的发展,使这个密钥对生成的操作已经变得非常简单和廉价,一般情况下是完全可以实时操作的,受到的限制主要是管理和应用方面的,作为开发者不用太担心这个计算性能的问题。而且,随机生成和应用密钥对,也可以大幅度提升信息处理过程的安全性。
- 公钥加密
公钥加密,是最简单直观的应用场景,用于对传输的信息进行加密。如果A要向B传输数据,它需要先获取B的公钥,然后使用B的公钥对信息进行加密。加密后向B传输加密数据。B收到数据后,使用自己的私钥(和公钥配对的)就可以对加密后的信息进行解密了。由于这个密文,只能通过私钥进行解密,没有共享密钥的交换过程,就可以保证过程和操作的安全性。
公钥加密的主要问题是如果仅使用公钥进行加密,由于加密机制的限制,和对称加密相比,公钥加密和私钥解密的效率比较低,不适合处理比较大的信息。
- 私钥加密
刚开始接触私钥加密的开发者可能会感觉到很奇怪。私钥加密,就是可以使用公钥进行解密,那为什么要给一个人人都可以解密的信息进行加密呢。
这是因为,非对称加密,除了信息的加解密之外,还有一个常用的应用方式,就是签名和验证,这恰恰是通过私钥加密来实现的。比如,A需要向B传输一个信息,A就可以使用私钥对这个信息进行加密;B收到信息后,可以使用A的公钥进行正确的解密,由于这个信息只能用A的公钥解密,就可以确定肯定是由A的私钥进行的加密,从而确定这个信息只能来源于A,就是所谓的签名和验证。
签名的目的在于实现信息的完整性和不可否认性。对信息进行签名,任何人都可以通过签名来验证这个原始信息是完整的,并且由于只能使用公钥来进行验证,就可以确保这个信息确实是使用签名者的私钥进行的签名,在逻辑上确保这个信息来自签名者。而且签名者还无法否认,因为如果验证成功,说明别人无法使用私钥生成原文对应的签名信息。
新的思考
上面就是非对称加密的一般应用方式和基础的认知。但原来笔者和普通的开发者一样,知道如何使用这个过程,但其实并没有进一步深入思考,它是如何实现的,以及如何能够进行改进,来适应更高的安全需求。在这方面笔者原来有两个疑惑:
- 非对称加密的效率
前面已经讨论到,由于相对的数学原理和操作比较复杂。如果使用公钥加密,它的执行效率相对是比较低的,加密信息的规模也受到限制。这样其实对在实际业务应用中的限制是比较大的。
- 签名验证的过程黑箱
一般情况下,所谓的验证,都是通过对于信息的对比来实现的。但私钥加密(签名验证)的一般实现却不是如此,而是直接通过输入相关的参数,由相关的算法函数给出验证通过或者不通过的结果,开发者基本无法干预这个过程,而且作为一个"黑箱"的功能,也不知道其具体的实现方式和原理,就更谈不上对这个过程进行干预或者改进了。
针对上面的疑问,笔者以Nodejs的Crypto模块为基础,通过分析和整理非对称加密的算法体系,笔者认为,除了底层的非对称加密密钥体系之外,其实在应用方面,如果要对非对称加密进行扩展的应用,其核心应该是密钥协商技术。
关于密钥协商(Key Exchange)
在密码学系统中,密钥交换(协商)并不是它的学术名词Key Exchange,而是Diffie-Hellman(DH)。这个命名充分展示了行业对于这两位算法发明人的尊敬,事实上,他们也由于在密码学方面的杰出贡献,获得了2015年的图灵奖,完全配得上以名字来命名算法的荣誉。
所谓的密钥协商,就是两对非对称加密的密钥,在不进行密钥信息的加密情况下,基于自己的私钥,和对方的公钥,可以在不交换其他信息的情况下,使用数学方式,计算出来一个新的密钥,这个密钥可以由数学算法保证,在双方是一致的,如下面的公式所显示的那样:(VK是私钥,PK是公钥,DH是密钥协商算法函数)
DH(VKb,PKa) = DH(VKa,Pkb)
和各种对称和非对称的加密算法一样,基于相同的理念,具体的DH算法实现也有很多种类型,开发者可以根据喜好和熟悉程度,选择适合自己和应用场景的实现算法和函数库。
公钥加密改进
基于以上思考,我们其实可以提出一种更高效的"公钥加密"的方案。实际上不是直接使用公钥进行加密,而是先使用双方的密钥对协商出一个密钥,然后使用这个密钥对原始信息进行对称的加解密操作。
其基本方法和过程是(假设由A向B发送信息):
- A、B分别生成自己的密钥对
- A获得B的公钥PKb
- A使用DH,使用VKa和PKb,计算协商密钥NK
- A使用pkdf2等衍生算法,计算临时密钥NKt
- A使用NKt对信息进行加密(对称加密)
- A向B传输公钥PKa、密文E和NKt的衍生参数
- B收到A的公钥PKa、密文E和衍生参数
- B使用DH,使用VKb和PKa,计算协商密钥NK
- B使用pkdf2和衍生参数,计算临时密钥NKt
- B使用NKt,对密文进行解密,获得原文
这个过程的优势在于:
- 可以处理较大规模的信息,因为在一次信息传输过程中,结合使用非对称加密和对称加密,不受非对称加密性能较差的限制
- 每次加密没有使用固定的密钥,而是衍生的随机密钥,提供更高的安全性
- 双方可以随时更新密钥对,实现完全动态加解密
- 真正的加密强度可以随时调整(通过增加对称加密密钥长度和实现方式)
- 对于原始密钥的保护更安全(只使用衍生密钥)
私钥加密(签名和验证)的改进
在传统的非对称加密签名和验证的应用过程中,一般的密码学程序库完全隐藏了其实现的技术细节,这样也会影响到对其算法的改进和升级。比如做签名验证的时候,传入原始数据和签名信息,只能得到一个验证结果,内部的处理完全是封闭的。
笔者构想,如果是想要对这一过程进行改进或者定制,其实可以按照非对称加密的原理,来进行处理。其核心是引入一个"临时非对称密钥"。基本过程如下(预设的场景是A对B传输信息,A签名,B验证):
- A生成或者加载自己的密钥对(PKa/VKa)
- A生成临时密钥对(PKt/VKt)
- A使用自己的私钥VKa和临时公钥PKt,生成协商密钥NK
- A使用NK,对原始信息I,使用加密摘要算法(HMAC),计算加密摘要IM,作为信息签名的签名部分
- A对临时私钥VKt进行编码,附加到签名后面,作为其密钥部分
- A将签名信息传输给B
- B收到原始信息I(也可能是解密得到)和签名,并获得A的公钥PKa
- B从签名信息中,分离出IM和Vkt
- B使用VKt和Pka计算协商密钥NK
- B使用NK,对原始信息I,使用加密摘要算法(HMAC),计算摘要结果IM2
- B比较IM和IM2,确定验证结果
这种过程和算法的优点在于:
- 每次签名和验证,都使用一次性的临时密钥,生成的动态签名信息,提供更高的安全性
- 临时衍生密钥,可以很好的保护原始密钥
- 可选摘要算法的实现,可以动态和持续改进安全性
- 可选加入自定义信息和验证过程,提供更高的业务灵活性
- 临时密钥对和HMAC都可以快速计算,保证签名和验证的处理效率
- 每个组成阶段,都可以改进和升级
所以,在这个实现中,真正的签名信息,包括算法、临时私钥和摘要计算结果。
参考实现和示例代码
根据以上的实现构想,基于Nodejs的crypto模块,笔者编写了下列示例和实验代码:
这个实现和代码的要点如下:
- 密码学套件使用Nodejs标准的crypto模块
- 相关配置信息详见配置常量
- 摘要函数使用SHA256,长度为32字节
- 对称加密方式为 chacha20-poly1305
- IV长度为12(随机设置),TAG长度为16(加密计算得到),都由算法确定
- AAD暂时使用随机信息,规格同IV,理论上可以使用任意附加数据
- ECDH使用crypto.ecdh类,曲线类型secp521r1
- 使用预制密钥作为私钥,可推导出对应公钥,生产环境可以考虑随机生成,或者使用更复杂的密钥
- 签名使用临时随机密钥对,由ECDH对象生成
- 在标准情况下,ECDH的私钥长度为32,公钥长度为133
- 有趣的是,computeSecret的密钥长度是不定的(65或者66),所以需要trim到32字节,作为标准对称加密密钥
小结
本文从笔者对非对称加密应用的两个问题出发,提出了相关思考和改进方式,并编写了示例代码进行了测试和验证。