Total Pageviews

Monday 23 October 2017

TLS加密通信的一些原理

本文完全不涉及对TLS协议细节的分析。本文不会介绍太多关于对称密钥,非对称密钥算法的实现细节。为了简单起见,你可以将下面提到的对称加密理解为AES算法,非对称加密理解为RSA算法。
最近在折腾某黑色产业,因此花了不少心思在TLS协议上面。我看过不少博客对于TLS 的介绍,现在回过头来审视这些文章,感觉大部分的介绍都不太准确。以下是我个人对TLS原理的一些理解。

入门知识

哈希(HASH)函数

将输入的数据转换成为定长输出的算法就是哈希算法,常见的哈希算法有md5sha1, sha256等等。哈希算法一般有着不可逆的特点,即给定输出,不能够轻易的反向计算出输入。比如big和bug的md5如下:
> echo -n "big" | openssl md5
> d861877da56b8b4ceb35c8cbfdf65bb4
> echo -n "bug" | openssl md5
> ae0e4bdad7b5f67141743366026d2ea5
你可以看见尽管big以及bug相差一个字母,其哈希值是完全不一样的。
将变长的数据(一个无穷的集合)映射到定长的输出(有限集合),根据抽屉原理,必然会存在多个不同输入拥有相同的输出,也就是相同的哈希值,这也就是我们所谓的碰撞。从碰撞的角度而言:
  1. md5是不安全的哈希算法,因为目前已经有人能够使用普通计算机在秒级别内找到碰撞
  2. sha1是不安全的算法,谷歌近来的一个研究成果是构建了两个拥有相同sha1的不同pdf文件。

对称加密(symmetric cryptography)

对称加密即使用密钥对数据进行加密后得到了密文,之后可以使用同样的密钥对数据进行解密运算获得明文。因为加密解密的密钥相同,所以叫做“对称密钥”。
常见的对称密钥加密算法有DES,3DES,以及AES。DES以及3DES都被认为是不安全的,目前依然广泛使用的算法是AES。AES接受128bit的输入,使用128bit,192bit或者256bit的密钥对数据进行加密,获得128bit的输出。如果你对AES实现的细节有兴趣,你可以看看这个来自酷壳的动图

非对称加密(asymmetric cryptography)

非对称加密有两把密钥,公之于众的密钥叫做公钥(音:"公月”,不是“公药”),个人保存的叫做私钥。由一把密钥加密的内容,只有对应的那把密钥才可以解开。目前广泛使用的算法是RSA。RSA不同于AES的一个地方在于,RSA的密钥长度是不固定的,其加密的安全性可以通过增加密钥的长度来保证。目前为止1024bit的密钥被人为是不安全的,RSA的密钥至少需要2048位。

从加密一个数据块开始

目标

信息安全的三个要素是availability(可用性),Integerty(完整性),confidentiality(机密性)。我们通过在这三个因素之间权衡,从而实现一个尽可能安全的系统。
我们现在的一个目标是向对方发送一个加密过的数据块,并且过程尽可能的安全。

为了availibility,使用AES传输数据

非对称密钥加密的安全程度高于对称密钥加密,但是在实际应用场景中,数据的加密是通过AES也就是对称加密算法实现的。这是因为和非对称加密算法相比,对称加密算法的速度明显要快。因此我们选择了机密性次之的AES。

使用AES加密需要解决的第一个问题,密钥交换

使用AES对数据进行加密从而实现点对点通信的前提是二者拥有用于AES加密的对称密钥。 没有对称密钥就不能加密通信的数据,而通信的数据如果不加密就无法传递对称密钥。这是一个鸡生蛋,蛋生鸡的问题。目前的解决方法有两个:
解决方法1, 基于RSA的密钥协商。大致的流程如下:
  1. 服务器将自己的证书传递给客户端,里面包含了服务器的公钥。
  2. 客户端生成对称密钥,使用服务器的公钥加密并传递给服务器。
  3. 服务器使用私钥解密数据包,获得对称密钥。

这种模式的一个弊端在于前向安全性(Forward Secrecy)不能保证。所谓前向安全性就是在密钥泄漏的情况下,历史的加密数据依然安全不会被解密。
假定服务器与客户端的通信受到某Big Brother的监视,并且不幸服务器的私钥泄漏了。那么:
  1. Big Brother使用私钥对上图的步骤2的数据包进行解密,从而获得对称密钥。
  2. 使用对称密钥对之后的数据包进行解密。 这样客户端与服务器之间的历史通信数据也就被解密出来了。
解决方法2, 基于DH的密钥协商。其实现的数学原理如下,图片来自维基百科:
  1. Alice准备3个数:素数p以,一个较小整数g,以及需要秘密保存的数a。
  2. Alice将p, g,以及A(某种方式计算出来的)以明文的方式传递给Bob。
  3. Bob准备一个秘密保存的数b。
  4. Bob接收到g, p,A 之后按照某种算法得到B,并将其明文传输给Alice。
  5. Alice 和Bob根据从对方获取的数,二者可以计算出一个相同的数K。
  6. 二者使用K,根据某种算法生成对称密钥(一个简单的例子,直接对这个数md5).
DH的密钥协商不能防止中间人攻击,因此DH协商的流程需要服务器使用私钥対之进行签名,保证数据的完整性。

使用AES加密需要解决的第二个问题, padding

AES的算法特点之一就是每次只能够对特定长度(128bit)的数据进行加密。对于长于128bit的数据,我们将它分割为多个128bit。而最后一块数据块往往不是128bit,我们需要先将它填充为128bit后进行之后的加密流程。
PSCK7是常见的一种填充方式。其填充规则是向被填充数据后面填充若干相同的字节,字节的值即为需要被填充字节的长度。假定我们要填充数据至8byte长度,那么:
DD DD DD DD DD DD DD ==> DD DD DD DD DD DD DD 01 # 填充1个字节,字节的内容为01
DD DD DD DD DD DD    ==> DD DD DD DD DD DD 02 02 # 填充2个字节,字节的内容为02
...
                     ==> 08 08 08 08 08 08 08 08 # 填充8个字节,字节内容为08

使用AES加密需要解决的第三个问题,工作模式(mode of operation)

AES 算法对一块数据加密,加密的结果是比较机密的,我们无法从中获得任何有用的信息。对称密钥加密的一个特点是相同的数据块,加密之后的密文是不变的。因此使用一个密钥对多块数据进行加密时,会出现一些弊端,以下图片来自维基百科:

第一张图片为明文的信息,第二张为使用对称密钥,分别对图片的每一个数据块进行加密之后的结果,你依然可以看到企鹅的轮廓。因此可以认为数据并没有被完全保护起来。
  • 将原始数据块分割,分别用对称密钥加密的方式叫做ECB 模式。这种模式下获得第二张图片的结果。
  • 使用一个初始化向量(IV, 长度等于对称密钥长度)和明文做OXR,再使用对称密钥加密,将加密后的数据用作下一个数据块的IV。如此循环下去。这种模式叫做CBC 模式。这种模式获得第三张图片的结果。
  • 其他的模式,比如CFB, OFB也可以获得第三张图片的结果。
  • 更多的工作模式参看维基百科

加密仅仅保证了Confidentiality(机密性), 我们还需要Integrity (完整性)

大多数人理解的安全, 仅仅是对数据进行加密,保证其不会被解读出来。比如一个银行转账的请求,希望将一笔钱转到帐号为12345678的账户。中间人随机对加密数据的某一位进行翻转,比如恰好将帐号变成了12345679。这种情况是存在的。
明文传输的一些协议比如TCP,就会计算数据的校验和,如果接收方发现校验不一致,则会丢弃数据包等待重传。在TLS的前提下我们则会使用其他机制保证数据的完整性。

MAC(消息验证码)

消息验证码是对在加密数据之外附带的一些数据,它用来验证两个问题:
  1. 证明数据是完整的,没有被修改。
  2. 证明数据确实是由对方发送的(即认证)。
消息验证码可以简单的理解为数字签名。我们先对数据先hash,然后用非对称加密对hash后的数据进行加密。对方如果能够用自己的非对称密钥解密数据,解密出来的数据与自己计算的hash一致则表示上面的两个要求被满足了。
下图是一个简单的流程。
  • 下面是对明文分别进行加密和签名的流程。
  • 实际应用场景下可以先对明文进行加密,再对密文进行签名。==>这种方法目前认为是更加安全的
  • 也可以先对数据进行签名,然后对(签名+明文)进行加密。
  • 对hash进行签名,而不是原文进行签名的一个原因是对原文签名得到的签名长度等于原文。这意味着传输的数据量加倍了。
  • 实际的认证算法是HMAC,OMAC等等,和下图的方法有轻微的出入,但原理是一样的。

AEAD(Authenticated-Encryption With Addtional data)

上面我们提到有两种消息验证有三种方式:
  • 先加密,再对加密数据进行签名(encrypt then mac)。
  • 先对数据进行签名,再对签名+数据进行加密(mac then encrypt)。
  • 对数据签名,对数据加密(encrypt and mac)。
不论何种方式,一方传输的数据必然包含两个部分,即加密的数据部分以及消息验证码部分。然而一个更加高级的方式是使用一种算法能够同时实现加密和mac两个目的,这一类的算法就叫做AEAD。常见的有GCM。

总结

TLS先定义好了一些通信的基本原则,比如需要使用对称密钥算法加密数据,比如需要使用密钥交换算法,需要使用某种机制保证数据的完整性。在这些前提下,我们选用一些具体的实现方式对这个流程进行填充,从而实现了一种特定的通信方式。这就叫做加密套件(Cipher Suite)。具体来讲你可以这样设计一个加密套件:
  1. 选择一种对称加密算法,比如AES。
  2. 提出一类密钥交换算法,比如DH, ECDHE,使得客户端与服务器之间能够生成AES加密用到的对称密钥,且通信具有前向安全性。
  3. AES只能够处理一块固定大小的数据,为此我们使用填充(padding)算法。
  4. 为了使AES能够安全地处理多块数据,我们使用某种工作模式, 比如ECB,CBC等等。
  5. 为了防止数据被篡改,我们或者使用消息验证码(MAC),或者使用AEAD类算法,保证数据的完整性。
解决了以上问题之后,我们就能够基本上实现一套通用的TLS协议了。在火狐浏览器(是的,我不太喜欢Chrome)里面浏览一个https的网站,然后你就可以点击地址栏的绿色锁图标,查看自己与服务器是通过什么样的方式(也就是加密套件)通信的。


比如上图中的例子,客户端与服务器通过ECDHE算法交换密钥,RSA 认证,AES加密,密钥长度128bit,GCM算法实现加密以及认证,SHA256 作为GCM底层的哈希算法。在未来的某一天,我们发现某种算法比如ECDH密钥交换算法不再安全,那我们可以使用一个新的密钥交换算法替换ECDH,而不会对整个TLS加密套件的其他部分产生影响。

参考资料

  1. TLS协议分析 与 现代加密通信协议设计。 这篇文章的作者对TLS原理的分析非常透彻。
  2. 电子书crypto101。尽管是英文的电子书,但是它的介绍算比较通俗易懂的,强烈建议读一读。
  3. Keyless SSL: The Nitty Gritty Technical Details。由浅入深地介绍了TLS的流程。
  4. 维基百科关于DH, Block cipher mode of operation, Hash-based message authentication code等等的介绍,内容和艰深晦涩的RFC文档相比,已经算是比较易懂了.

No comments:

Post a Comment