加密算法是SSL/TLS最重要的组成,没有它们就不会有SSL/TLS协议的诞生,简单可以划分为两类,分别为对称加密和非对称加密。
对称加密
对称加密指的是可以使用同一个密钥对内容进行加密和解密。相比非对称加密,它的特点是加、解密速度快,并且加密的内容长度几乎没有限制,简单的表达式就是
发送者加密信息:encrypted_message = func1(message, key),
接收者解密信息:message = func2(encrypted_message, key)
其中根据不同的算法,func1有可能会等于func2,也有可能不同。
现在常用的对称密钥主要分成两种,块式加密和流式加密,他们的基本思想都是对信息进行XOR、移位等操作来进行加密的。在操作方式上块式加密是把message分成多个固定长度的组,每组包含多个字节,每次操作的时候会针对一组字节进行操作,鼎鼎大名的DES和AES就是采用这类方式。而流式加密是每次只针对一个字节进行操作,鼎鼎大名的RC4就是采用这种方式。
DES的原理
我们将需要加密的消息分成长度为64bits的一堆block,对每个block通过XOR、移位等等方式进行加密,DES的密钥(key)长度是64bits,有效长度是56bits,因为其中的8个bits用于校验。另外DES有一个特点就是对于同一个block,如果使用同一个密钥(key),加密出来的结果是相同的,换句话说攻击者可以通过寻找同样的密文block来推理出原文,这就是我们常说的回放攻击。
为了解决回放攻击的问题,最简单的方式就是CBC(cipher block chaining),简单说来就是每次加密一个block之后,把它和前一个block的密文进行XOR操作,作为这个block的密文,这样即便是同样的block,每次加密的结果也会不一样,攻击者就无法通过密文猜测原文了。另外第一个block因为在它之前没有其他block了,所以我们需要生成一个随机的64bit的intialization vector(类似我们在写程序对用户密码加密的时候搞的salt)给第一个block进行XOR操作。
3DES是在DES的基础上将密钥扩充为原来的3倍也就是192bits(实际有效密钥长度是56bits*3=168bits),3DES的加密过程其实就是将密钥分成三份,使用第一份密钥加密、第二份密钥解密、第三份密钥加密,然后解密的话刚好相反,使用第三份密钥解密,第二份密钥加密和第一份密钥解密,通过这种方式去扩展DES的加密长度问题,3DES更加安全,也能抵挡之前说的回放攻击。
AES的原理
3DES虽然很安全,但是因为多次的加解密导致性能其实很低的,所以数学家们希望在同样的密钥长度下使用更高效率的算法,于是有了AES。
AES允许密钥长度为128-bit,192-bit和256-bit,当前来说使用128-bit已经足够安全,并且性能比3DES快很多,所以现在使用最多的对称加密就是AES。
RC4的原理
如果把block的size变成了1 byte那么就变成了stream加密算法,当然具体的实现肯定不一样,block式加密主要是将block里面的每个bit进行移位,而stream加密是生成同样长度的、安全的字符串,然后再和原文进行XOR生成最终的密文,所以stream的安全性主要取决于这个安全字符串的生成算法,同样因为RC4安全性取决于安全字符串的生成,所以它不需要CBC或者IV。
总的来说对称加密性能高、速度快,但是它的前提是必须双方都知道这个共同的密钥,而这个密钥又不可以被第三方知道,否则他们也是可以解密密文。为了解决密钥传输的问题,数学家又发明了非对称加密的算法,相比而言,非对称加密解决了密钥本身的安全传输问题,但是他的性能相比对称加密来说相差几万倍,而且对所加密的内容也有长度的限制。
非对称加密
非对称加密和对称加密不一样,它有两个密钥,分别为公钥和私钥;公钥是公开给所有人的,私钥永远只能自己知道。使用公钥加密的信息只能使用私钥进行解密(这种情况下我们就可以进行密钥传输了,因为全世界只有拥有对应私钥的人可以解密),同样的使用私钥加密的信息只能使用公钥进行解密(因为公钥是公开给所有人的,所以这种情况下我们用来进行数字签名,也就是用来认证这个信息确实是从私钥的拥有者发布出来的),而且在理论上我们无法通过公钥去算出私钥(或者说以现在全世界合起来的计算能力都需要几亿万年才能算出来)。
RSA
最常用的非对称加密就是传说中的RSA算法,它的可靠性来源于对极大整数做因数分解是极其困难的,到现在为止还没有发现快速攻破RSA的方法,只需要密钥长度足够长(2048bits以上),就可以认为RSA加密的信息是无法破解的。细节可以参考阮一峰写得RSA算法原理(一)以及RSA算法原理(二)以及维基百科。
所以有了RSA,我们只需要可以将我们的公钥让所有人都知道(基于PKI体系,后面再说),那么希望和我们进行安全通信的人就可以使用我们的公钥加密之后传输给我们,而我们使用私钥解密之后就可以获得原文信息了。因为RSA对加密内容有长度限制而且性能非常低,所以我们一般使用RSA去传输AES或者RC4等对称密钥,然后使用对称密钥对我们要传输的内容进行加解密。
DH
DH(Diffie-Hellman)也是一种密钥交换的方式,但是他和RSA不一样,RSA是可以直接把密钥加密后传输给对方解密即可,而DH是无法传输内容的,他是通过数学的方式让双方计算出一个共同的密钥,它的基本思路是这样的:
- 首先有这样一个等式: ,我们用g^a表示g的a次方
- 服务端和客户端首先协商出一个共同的数字:g和p
- 然后服务端和客户端分别生成随机数a和b,这两个数字双方必须各自保存不能让别人知道;然后服务端计算出Ys=(g^a % p),客户端计算出Yc=(g^b %p),双方把计算出来的Ys、Yc进行交换
- 这时候服务端就拥有a、g、p以及Yc、Ys,客户端就拥有b、g、p以及Ys、Yc
- 服务端计算keya = Yc^a % p,服务端已经有了Yc、a和p,所以就能算出一个key来
- 客户端计算keyb = Ys^b % p,客户端有了Ys、b和p,就能算出一个key来
- 而因为Yc^a % p == ( g^b %p )^a % p = (g^a %p)^b % p = Ys^b % p,也就是keya=keyb,于是双方共同算出了一个相同的密钥
- 那中间人为什么无法计算出密钥呢?因为中间人只能获取Ys、Yc、g和p,在不知道a或者b的情况下,是无法计算出 g^(ab)%p、 g^(ba)%p、(g^a%p)^b%p、 g^b%p) ** a%p中的任意一个
- 如果每次进行密钥交换的时候g、p、a、b都是不一样的,我们称之为Ephemeral Diffie-Hellman,也就是我们常说的DHE。使用RSA作为密钥传输有一个问题就是如果私钥泄密了或者未来某一天计算能力足够强了,那么之前的加密流量就可以被解密;而如果使用DHE的话,因为我们我们并不传输密钥,只传输中间的参数,双方通过公开参数计算出最终的密钥,所以即使RSA的私钥被泄密了,之前的历史流量依旧无法被解密,我们称之为PFS(Perfect Forward Secrecy),所以说DHE是支持PFS,而RSA如果作为密钥传输是不支持PFS。
ECC
ECC全称是Elliptic Curve Cryptography,也就是我们常说的椭圆曲线算法,它可以使用更短的密钥来实现更加安全的密钥交换,ECC和DH结合就变成了ECDHE,它使用曲线去替换原来g、p的生成,具体太复杂没太看懂(囧)。一般认为256bits的ECDHE的安全性相当于3248bits的DHE,而更短的密钥使得ECDHE的计算量更小,所以性能比DHE快很多。
当然ECC不是所有浏览器都支持的,例如XP上的IE就不支持,据我们不完全统计,支持ECC的用户占比在50%左右。
数字签名(Digital Signature)
Alice给大家发了一封邮件,告诉大家周末来参加她的婚礼,于是大家纷纷打电话恭喜Alice。打电话的这个过程一方面是祝福Alice,另外一方面也是一种认证,确认这个邮件是Alice发送的,而不是别人的恶作剧。
电话太多,Alice接不过来了,于是Bob告诉Alice,你其实可以使用非对称加密算法来将Alice发送的邮件进行一个认证,确保这个是Alice发送的,那么方法是这样的:
- Alice把自己的公钥公布出去之后,这样她的每个朋友都有知道这个公钥是Alice的
- Alice使用自己的私钥对邮件进行加密,然后发送给大家
- 大家使用Alice的公钥对邮件进行解密,得到原始邮件,大家就可以放心的知道这个不是恶作剧,确实是Alice发送的邮件
整个过程也是基于之前所说的非对称加密算法,通过私钥加密后的信息使用公钥去解密来确保这个信息确实是私钥拥有者发送的,其他人发送的信息使用Alice的公钥是无法解密的,这个原理和之前公钥加密的信息私钥无法解密是一个道理。
但是问题也来了,我们刚才说过非对称加密的算法是非常耗费资源非常慢的,所以Alice这么长的一个邮件直接使用非对称加密是无法完成的。因为我们这次需要保证的是来源确实是Alice,不需要保证信息不被别人知道,所以我们可以将这段信息使用某些方式(例如md5,sha1,sha2等Message Digest算法)生成一段比较短的信息,我们称之为摘要,然后把摘要进行私钥加密所耗费的时间资源就少得多。大家接收到之后把Alice的未加密的邮件通过同样的方式(例如md5,sha1等)生成摘要,使用公钥对Alice传输的摘要进行解密,发现和自己生成的一样,那么就可以确认这个邮件内容没有被篡改,也没有被伪造,确实是Alice发送的。
消息摘要(Message Digest)
刚才说到我们可以对明文进行sha1、md5等摘要计算,通过对摘要的加密和验证来保证消息来源的准确性,所以摘要算法有几个特点:
- 即使对明文修改了一个字节,整个摘要的计算结果也必须完全不一样
- 生成的摘要长度是固定的值
- 无法通过摘要推算出明文,也不存在任何密钥可以推算出明文
前提到RSA在传输加密信息的时候非常好用,同样的,我们也可以使用它来进行数字签名认证,数字签名使用的是RSA的验证功能,也就是私钥加密签名信息,公钥解密后验证签名来确保消息发送来源是真实没有被篡改的。
SSl/TLS过程
- 好了,有了上面这些东西,我们看看SSL/TLS具体是如何实现安全传输的:
- 客户端发送一个ClientHello消息,消息里面会生成一个随机数,同时告诉服务端自己支持的SSL版本,加密算法等信息。
- 服务端接收到客户端的ClientHello消息之后也会生成一个随机数,然后选择SSL版本,加密算法等参数,把这些内容放到ServerHello消息里面发送给客户端。
- 服务端接着把自己的证书发送给客户端,证书里面包括服务端的公钥,第三方证书认证机构(CA)的签名,服务端的域名信息等内容,这个阶段叫做Certificate。
- ServerKeyExchange是一个可选的过程,如果在ServerHello的时候服务端选择了RSA加密作为密钥交换的话就没有这个过程,如果服务端选择的密钥的交换方式是DHE或者ECDHE等双方共同计算出来的密钥的话(而不是通过RSA加密后传输的话),那么这个阶段就是双方进行协商的阶段,服务端会提供他希望使用的p、g参数以及使用服务端自己计算出来的Ys,当然自己会留一个私有的参数(这几个参数的意思参考前面说的Diffie-Hellman)。
- ServerHelloDone表示服务端完成了它这一块的协商过程。
- ClientKeyExchange这个阶段用户会首先认证服务端的证书是否合法(使用CA的公钥对之前CA颁发的数字签名进行验证),验证通过之后就去计算Pre-Master Key,然后使用服务端证书对Pre-Master Key加密后传输给服务端(如果是DH的话就是协商的p、g以及客户端自己计算出来的Yc,并且根据之前服务端提供的Ys数据计算出Pre-Master Key);服务端收到这个内容后使用自己的私钥进行解密得到Pre-Master Key(如果是DH的话就是通过p、g、Yc以及自己的一个随机数计算出Pre-MasterKey),最终双方都能拿到或者计算出Pre-Master Key,通过Pre-Master Key我们会生成MasterKey,最终在生成对称密钥。
- ChangeCipherSpec阶段表示发送完密钥信息之后客户端就进入了加密模式并且通知服务端我已经进入加密模式了
- 客户端然后会对client finished加密后发送给服务端。
- 服务端收到加密后的client finished消息之后使用之前计算出来的MasterKey进行解密,如果解密成功那么同样也进入ChangeCipherSpec阶段,通知客户端我也进入加密模式了
- 然后服务端也会发送一个server finished消息,如果客户端能够成功解密,表示双方的密钥协商已经全部完成,之后就可以将数据使用对称密钥加密后进行传输了。特别注意的一点是在Finished消息中会带上一个verify_data字段,这个字段会对整个Handshake进行hash,为的是防止我们在协商过程中的数据被人篡改了。