TCP和TLS/SSL会话细节

 

TCP数据段格式说明
TCP建立连接和断开连接细节
Https如何保证通信安全
一次Https网络请求通信细节
网络数据包分析工具wireshark的使用
问题:
SYN、ACK、FIN具体含义是什么?
TCP建立连接超时的表现?

为什么需要证书来下发服务端公钥?
客户端是如何验证证书合法性的?
对称秘钥是如何协商出来的?
为什么不直接让客户端自己生成一个秘钥发送给服务端使用?
TLS如何避免重放攻击?

TCP数据包格式说明
TCP数据段分为首部+数据两部分。
首部又分为固定首部和可选项首部。
通过对TCP数据包格式的分析,就是了解TCP协议定义的过程。

来源连接端口(16比特位)-辨识发送连接端口
目的连接端口(16比特位)-辨识接收连接端口
序列号(seq,32比特位)-TCP数据包标识。
无惧传输时的乱序或丢包
建立连接时发送和接收方第一次数据段的seq均为随机生成(TCP序号预测攻击),之后是上次序列号加1
发送数据时seq为上一次发送的数据长度加1,如果数据长度为0则seq不变
确认号(ack,32比特位)-表示接收方期望下次收到的数据包的序列号的值,也是当前收到的数据的字节长度加1
数据偏移(4比特位)-以4字节为单位计算出的数据段开始地址的偏移值,例1111 -> 15 -> 60字节
保留位(3比特位)-需置为0
标志符(9比特位)
NS:Nonce Sum 随机和,
CWR:Congestion Window Reduced 拥塞窗口减少标志被发送主机设置,用来表明它接收到了设置ECE标志的TCP包。拥塞窗口是被TCP维护的一个内部变量,用来管理发送窗口大小
ECE:ECN-Echo(显式拥塞通知回显) ECN响应标志被用来在TCP3次握手时表明一个TCP端是具备ECN功能的,并且表明接收到的TCP包的IP头部的ECN被设置为11 NS/CWR/ECE三个标志组合实现估计网络拥塞情况的功能
URG:为1表示高优先级数据包,紧急指针字段有效
ACK:为1表示确认号字段有效
PSH:为1表示是带有PUSH标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满
RST:为1表示出现严重差错,可能需要重新创建TCP连接,还可以用于拒绝分发的报文段和拒绝连接请求
SYN:为1表示这是连接请求或是连接接受请求,用于创建连接和使顺序号同步
FIN:为1表示发送方没有数据要传输了,要求释放连接
窗口(WIN 16比特位)-表示从确认号开始,本报文的接收方可以接收的字节数,即接收窗口大小,用于流量控制
校验和(Checksum 16比特位) -对整个TCP报文段,包括TCP首部和TCP数据,计算出来的16位值,这是一个强制字段。校验方式为:将TCP报文段的头部和数据部分的和计算出来,再对其求反码
紧急指针(16比特位)-本报文段中的紧急数据的最后一个字节的序号
选项字段 -最多40字节,每个选项的开始是1字节的kind字段,说明选项的类型。下面具体说明支持的选项字段类型
0:(1字节)选项表结束
1:无操作(1字节)用于选项字段之间的字节边界对齐和分割不同的选项数据
2:最大报文长度(4字节,Maximum segment size,MSS) -在握手阶段告知接收方,发送方支持的最大报文数据段的长度,以太网一般为1460。只能出现在同步报文段中,否则将被忽略。通常将MSS设置为MTU-40字节(Maximum Transmission Unit 最大传输单元),这样携带TCP报文段的IP数据报的长度就不会超过MTU,从而避免本机发生IP分片。
3:窗口扩大因子(3字节,wscale)-取值0-14,用来把TCP的窗口的值左移的位数。只能出现在同步报文中,否则将被忽略。这是因为现在的TCP接收数据缓冲区(接收窗口)的长度通常大于65535字节
4:sackOK -发送端支持并同意使用SACK(Selective Acknowledgements,选择确认)选项
5:SACK实际工作的选项,存放丢包的边界信息,最多存放4个包的边界信息。例如123,丢了2,那么SACK存放的就是2号包的开始和结束的字节序列号
8:时间戳(10字节,TCP Timestamps Option,TSopt)
发送端的时间戳(Timestamp Value field, TSval,4字节)
时间戳回显应答(Timestamp Echo Reply field,TSecr,4字节)
时间戳的功能有两个:
用来计算往返时间RTT(Round-Trip Time),发送方在收到确认报文后,可以准确计算出RTT。
防止回绕的序列号,通过时间戳可以判断出相同序列号的数据报,哪个是最近发送的,哪个是以前发送的。
滑动窗口
简单一句话就是接收方可以动态控制发送方下次发送的TCP包数据段的大小。
当接收方处理数据较慢时,就可以通过WIN字段,在ACK包中告知发送方:“以后的数据段少发一些,我处理不过来了。”
在极端情况下,接收方连1字节的数据也不能处理了,那么WIN字段就设置为0,发送方就会停止发送后续的TCP包。
那么,当接收方缓过气来,可以处理更多数据时,发送方是怎么知道的呢?答案是Zero Window Probe(零窗口探针)技术。
发送方会在一定时间间隔内重复发送ZWP包,这时接收方就有机会告知发送方最新的窗口大小。
又有极端情况,接收方一直返回WIN为0,那么发送方在发送一定次数的ZWP后,就会发送RST包来断开连接(不同的系统有不同的实现)。

另外一种极端情况,接收方返回的WIN值特别小,相对于TCP的首部来说,发送较少的数据时一种浪费。这个时候接收方就会使用David D Clark’s 方案。接收方直接返回WIN为0,知道接收方有足够的能力处理新数据时再把WIN打开。

如果是由于发送方发送的数据特别少引起的,那么发送方就会使用Nagle’s algorithm。将多个小的数据包缓存起来,直到满足发送条件。

ECE和CWR
当两个支持ECN的TCP端进行TCP连接时,对于支持ECN的TCP端来说,SYN包的ECE和CWR标志都被设置了。SYN-ACK只设置ECE标志。

一个支持ECN的TCP主机在支持ECN的TCP连接上发送设置了IP头部为10或者01的TCP包。支持ECN的路由器在经历拥塞时设置IP头部的ECN域为11。当一个TCP接收端发送针对收到的一个设置ECN位为11的TCP包的响应时,它设置TCP包头中的ECE,并且在接下来的ACK中也做同样设置。

当发送主机接收到设置了ECE标志的ACK时,它就像感知到包丢失一样,开始减少发送窗口,运行慢启动过程和拥塞避免算法。在下一个数据包中,发送者设置CWR标志。在接收到新的设置CWR标志的包时,接受者停止在接下来的ACK中设置ECE标志。

超时重传
建立连接时:

如果发送方在发送一个SYN包后,在超时时间内没有收到确认包,则发送方会重新发送,称为超时重发。
默认Linux重试次数为5次,重试时间间隔由1s开始每次翻倍,即1s,2s,4s,8s,16s。如果经过1+2+4+8+16+32=63s后,仍没有收到确认包,则发送方认为接收方已掉线,会主动断开当前连接。

数据传输时:

为了网络整体的稳定,需要动态的根据往返时间设置数据包的超时时间。这里就不展开说具体算法过程了。
RTO(Retransmission Timeout)重传超时时间
RTT(Round-Trip Time)往返时间

拥塞控制
慢启动
拥塞避免
快速重传
快速恢复

三次握手 和 四次挥手
三次握手

这是简单的三次握手流程示意图,三次握手意思是需要在发送方和接收方之间传递三个数据包。通过设置不同的标识位,来告知对方当前数据包的意图。

第一次:C发送一个数据包P1给S,并将标识位的SYN置为1,表明“我要和你建立连接”。

第二次:S如果可以接受C的请求,会给C回发一个数据包P2,并将标识位SYN置为1,表明“我同意和你建立连接”。同时将ACK位置为1,表明“确认号ack”字段有效,其值为P1数据包序列号+1。

第三次:C接到P2后,会再次向S发送数据包P3,将ACK为置为1,其值为P2数据包的序列号+1,表明“我知道了你同意了”。

至此,连接就被建立完成了,双方就可以任意发送数据了。

但是,在三次握手过程中,除了要协商连接的建立,还有其他通讯参数的设置。下面以一个真实请求在三次握手过程中发生的数据交换作说明:

第一次:

第二次:

第三次:

四次挥手

这是TCP连接断开时四次挥手的示意图。划重点:

每一侧的连接都单独的被终止。
主动终止连接的一方在接收到ACK后,不能再发送数据,但可以接收数据,也就是半双工状态。
首先终止连接的一方,在给对方响应了ACK后,就会等待2*MSL(Max Segment Lifetime 报文最大生存时间)时间,然后关闭连接。RFC793定义了MSL为2分钟,Linux设置成了30s。
四次挥手也可以通过三次握手实现,即主机A发出FIN,主机B回复FIN&ACK,主机A回复ACK。
下面是三次握手实现的连接断开的报文传输细节:

第一次:

 

第二次:

第三次:

 

Https如何保证通信安全
简介
Https 超文本传输安全协议(Hypertext Transfer Protocol Secure),1994年由网景公司提出,Https经由Http进行通信,但利用SSL/TLS来加密数据,即在Http协议与TCP协议之间添加SSL/TLS层。

安全防护:

身份认证,防止中间人攻击
消息加密,防止被窃听
消息校验,防止被篡改
安全前提:
系统或浏览器正确的实现了Https并安装了正确的证书颁发机构

安全通信流程
对称加密
算法公开(AES)
一个秘钥,秘钥不公开
加解密速度快

非对称加密
算法公开(RSA)
两个秘钥,公钥公开,私钥不公开
公钥加密的数据,私钥可以解密。私钥加密的数据,公钥可以解密
加解密速度慢

非对称加密算法除了可以直接将隐私数据加密外,还可以实现对非隐私数据的防篡改校验功能,也就是数字签名。

Hash/散列/摘要算法
以任意长度的数据为输入,输出固定长度的数字“指纹”。
MAC,消息认证码,是带秘钥的Hash算法,即在对数据计算散列值时将秘钥和数据同时作为输入,并采用二次散列迭代的方式。

Alice和Bob
在没有SSL/TLS的世界里,Alice和Bob的通讯是这样的。

Alice(i love you)–明文(i love you)–>Bob(i love you)

一些坏人可以在明文的传输过程中,对数据进行更改。

Alice(i love you)–>坏人(i hate you)–>Bob(i hate you)

Bob(i still love you )–>坏人(i hate you too)–>Alice(i hate you too)

当两人见面后,发现对方误会了自己,就想到这个世界还是有坏人,然后双方约定将通信内容加密后再发送给对方。约定的加密算法为AES,秘钥为“Alice/Bob”。
加密后就会出现两种情况:
Alice(i love you)–加密(123abc)–>坏人(123abc)–>Bob(123abc)–>解密(i love you)

Alice(i love you)–加密(123abc)–>坏人(!!!)–>Bob(!!!)–>解密(???)
单纯使用对称加密会有两个问题:

坏人虽然不能窃听内容,但是仍能篡改。
对称加密的秘钥只能Alice和Bob两个人知道,如果想再和更多的人加密通信的话,就无能为力了。
Alice和Bob必须在通信之前就约定好秘钥(在互联网世界中办不到),中途无法更新。
所以,我们要解决这些问题,需要做到:

为了过滤掉被篡改的数据,通信过程需要有内容校验机制
为了可以和无限多人通信,需要能动态生成和更新秘钥
Alice和Bob非常聪明,他们想到了非对称加密算法RSA,Bob拿着私钥,然后把公钥给Alice,当Alice想要和Bob通信时,利用手中的公钥将对称加密算法的秘钥加密后发送给Bob,Bob拿着自己的私钥将对称加密的秘钥解密后,双方就可以继续用对称加密算法将数据加密后通信了。当然,其他人也可以拿到Bob的公钥,与Bob通信。

Alice(我想和你说话,给我公钥)------->Bob(我想和你说话,给我公钥)
Bob(给你公钥(Pubkey)) --------->Alice(拿到公钥PubKey)
Alice(秘钥:123)–>公钥PubKey加密(321)–>Bob(321)–>私钥解密(123)

Alice(i love you)–>秘钥123加密(123abc+摘要签名)---->Bob(123abc+摘要签名)–>正确性验证–>秘钥123解密(i love you)

上述第二个过程是无懈可击的,但是第一个过程中,如果存在坏人的话,就变成下面的情形

Bob(给你公钥(PubKey))---->坏人(PubKey换成自己的PubKey1)--------->Alice(拿到公钥PubKey1)
Alice(秘钥:123)–>公钥PubKey1加密(abc)–>坏人(abc),自己的私钥解密123,再用PubKey加密321–>Bob(321)–>私钥解密(123)

完犊子了,对称加密的秘钥被坏人窃取了,通信数据又相当于裸奔了。其中核心问题就是:

接收方无法确定公钥的合法性
那如果把公钥事先告知Alice可以么?也就是内置在系统中。理论上是没问题的,但当Alice需要和更多的人通信时,她需要记住很多很多的公钥,这是不可行的。

所以Alice和Bob需要商量出一套方案,能保证公钥在网络上安全的传输,如果受到篡改,接收方能感知到。这时,他们想到数字签名的方式。

Bob(给你公钥+签名(摘要的私钥加密))------>Alice(公钥+签名,对签名解密,并再次计算摘要,然后比对)

上述方式可以做到防篡改么?可以,但是做不到防替换。中间人可以把签名连同公钥全部换成自己的。

接下来就到了数字签名证书出场的时刻了。

Bob找到了一个非常权威的机构,“人民政府”。
Bob向“人民政府”证明“我是真Bob”,并提供自己的公钥。
“人民政府”根据Bob的信息和公钥颁发给Bob一个证书文件.cer,里面写了颁发机构的信息、Bob的信息和公钥、摘要算法以及最重要的颁发机构的签名。

公钥下发过程变成了数字证书下发过程。

同时,Alice也是非常相信“人民政府”的,只要是“人民政府”签名的证书,Alice就认为证书上面的公钥就是Bob的。

但是,Alice并不能无脑的相信,她需要判断了两点:

接收到的证书是否是”人民政府“签发的。
证书上面的信息是否被篡改。
那么具体的判断流程是怎样的呢?

由于Alice信任”人民政府“这个机构,所以Alice可以内置一份”人民政府“自签名的证书,上面有”人民政府“生成的公私钥中的公钥,私钥由”人民政府“自己保管。
在下发证书过程中,Alice拿到Bob发过来的由”人民政府“私钥签名过得证书cert0。
Alice首先根据cert0上的证书颁发机构信息判断自己是否信任这个机构颁发的证书,cert0是由”人民政府“签发的,而Alice是信任”人民政府“的,所以Alice信任cert0。
Alice通过查找内置的”人民政府“的自签名证书拿到”人民政府“的公钥
用此公钥验证cert0上的信息是否被篡改
问题:Bob的公钥不是由”人民政府“签发的,而是由其下属的”地方政府“签发的。而Alice只有”人民政府“的自签名政府,如何判断证书合法性呢?

答:在证书下发过程中,实际是下发的一个证书链,类似于”Bob的证书“–>“地方政府的证书”–>”人民政府的证书“,Alice可以逐级查找,直到根证书。

证书内容
Https证书格式遵循的是X.509标准。X.509是ITU-T标准化部门基于他们之前的ASN.1定义的一套证书标准。

在浏览器中随便下载一个证书,通过以下命令获取到其文本格式。

openssl x509 -in *.xxx.com.cer -inform der -text -noout >> cer.txt 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            09:3e:8a:aa:5a:f8:14:de:9d:d9:4d:28:2e:97:a8:16
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=GeoTrust RSA CA 2018  //颁发者信息
        Validity
            Not Before: Aug 22 00:00:00 2018 GMT
            Not After : Nov 12 12:00:00 2020 GMT
        Subject: C=CN, ST=\xE5\x8C\x97\xE4\xBA\xAC, L=\xE5\x8C\x97\xE4\xBA\xAC, 
O=Beijing Qfpay Technology Co., ltd., CN=*.qfpay.com //公钥主题信息,公钥所有者的信息
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:e6:c0:8b:f0:12:1e:f0:92:9c:17:7d:7e:d5:69:
                    47:fc:dd:0b:51:5a:0e:79:df:b1:0e:b4:d6:7d:d0:
                    5a:bc:f9:93:f6:3c:e3:40:a8:66:9f:d0:ae:3c:e1:
                    f8:9a:55:a2:84:0e:9c:1d:65:f9:d2:63:51:48:b2:
                    88:a5:09:a6:be:92:80:f8:3b:eb:b8:78:1b:35:58:
                    47:ac:eb:47:cd:3d:7f:36:74:30:7a:01:86:48:96:
                    b3:7b:14:82:b6:63:0b:b6:43:20:98:3f:07:9d:1a:
                    56:76:25:cf:cd:d5:49:fd:6e:dc:86:f0:7f:15:f3:
                    7d:58:98:75:a5:7f:f9:ab:b2:c4:ec:fc:30:bd:75:
                    27:b3:0e:72:3d:44:d1:04:42:52:65:9b:3e:53:9b:
                    a5:c2:eb:ac:c5:01:b6:1d:0d:2f:75:79:7d:98:d4:
                    2b:b6:c0:28:ea:c7:dc:14:04:b6:4d:a3:dc:01:2c:
                    f0:14:13:b9:d2:29:31:00:37:af:17:d6:82:a6:f9:
                    57:9e:4c:2f:27:27:08:50:16:e3:ca:fa:58:32:c7:
                    f5:04:43:b4:5d:0e:97:81:e9:c3:01:36:f9:b7:c8:
                    14:ec:98:27:e9:31:86:ab:f5:c4:ff:50:aa:c4:df:
                    cc:6e:7d:1c:5a:fa:b8:47:c9:fa:78:b4:de:6d:15:
                    8d:27
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Authority Key Identifier: //授权秘钥标识符
                keyid:90:58:FF:B0:9C:75:A8:51:54:77:B1:ED:F2:A3:43:16:38:9E:6C:C5

            X509v3 Subject Key Identifier: //主题秘钥标识符
                EC:99:74:D5:FF:C6:1B:4F:FB:39:88:8C:E2:C1:7B:8D:90:59:AB:1F
            X509v3 Subject Alternative Name: 
                DNS:*.xxx.com, DNS:xxx.com
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 CRL Distribution Points: 

                Full Name:
                  URI:http://cdp1.digicert.com/GeoTrustRSACA2018.crl

            X509v3 Certificate Policies: 
                Policy: 2.16.840.1.114412.1.1
                  CPS: https://www.digicert.com/CPS
                Policy: 2.23.140.1.2.2

            Authority Information Access: 
                OCSP - URI:http://ocsp1.digicert.com
                CA Issuers - URI:http://cacerts.geotrust.com/GeoTrustRSACA2018.crt

    Signature Algorithm: sha256WithRSAEncryption
         07:8d:b9:34:62:e8:4b:83:00:af:ab:38:d4:b1:24:12:a4:37:
         5e:8f:e7:ff:d9:96:48:ae:72:6f:d2:0b:41:6a:55:92:2a:06:
         39:86:a9:78:18:cd:0d:5f:33:fa:22:81:50:b7:67:2f:dc:a1:
         b4:ee:0f:6c:f8:73:87:0d:65:e7:19:9a:55:07:49:a4:2d:09:
         11:8b:5f:1c:c1:46:ce:94:22:fa:b0:1b:88:f0:f0:6f:63:11:
         e4:56:f4:51:3c:12:90:db:44:63:8b:fd:17:d2:e5:7a:66:5e:
         f5:d8:90:70:5c:d6:c2:74:d9:74:b3:75:ce:83:e3:db:57:bb:
         b6:3a:81:e7:ca:7a:48:82:6c:0b:01:a8:ed:a2:8e:d0:b0:ed:
         25:15:a2:2a:7f:6f:a5:6d:da:5a:ac:91:f4:dc:23:d8:9f:9d:
         d3:0a:f4:c7:8f:b0:c2:18:54:97:f5:00:30:36:65:e1:aa:25:
         9c:f1:b8:77:d6:7d:33:79:39:0e:41:86:1d:79:47:0e:34:cc:
         fd:e8:63:83:9d:f5:86:d6:e2:0c:fa:58:d5:d2:81:c1:92:da:
         e7:41:45:bc:a0:91:d5:40:6e:c8:22:76:69:e4:67:9a:d4:03:
         ca:8d:28:d5:ca:98:09:e0:d6:dd:ae:c2:6f:08:82:1b:89:79:
         14:d6:ca:b7

 

SSL/TLS握手细节

SSL/TLS报文格式

SSL协议属于分层协议,一个SSL报文可以包含多个记录层,每个记录层分为两部分:头部 + 协议数据。

Content Type:协议类型
Version:TLS版本
Length:报文长度
协议数据,不同的协议包含数据字段不同
报文支持的协议类型
Handshake Protocol
握手协议是最主要的协议,负责协商会话的安全属性。按照不同的功能区分为不同握手类型。

 1 enum {
 2           hello_request(0), client_hello(1), server_hello(2),
 3           certificate(11), server_key_exchange (12),
 4           certificate_request(13), server_hello_done(14),
 5           certificate_verify(15), client_key_exchange(16),
 6           finished(20), (255)
 7       } HandshakeType;
 8       
 9 struct {
10     HandshakeType msg_type;    /* handshake type */
11     uint24 length;             /* bytes in message */
12     select (HandshakeType) {
13         case hello_request:       HelloRequest;
14         case client_hello:        ClientHello;
15         case server_hello:        ServerHello;
16         case certificate:         Certificate;
17         case server_key_exchange: ServerKeyExchange;
18         case certificate_request: CertificateRequest;
19         case server_hello_done:   ServerHelloDone;
20         case certificate_verify:  CertificateVerify;
21         case client_key_exchange: ClientKeyExchange;
22         case finished:            Finished;
23     } body;
24 } Handshake;

 

每种握手协议类型,也都包含一些通用字段,如
HandshakeType
Version
Length

下面具体说明每种握手类型的作用:

Hello Request

”你好,请求“,该类型的握手协议作为一个简单的通知,由服务端发送,告知客户端重新开始协商过程。作为响应,客户端应该在方便时发送ClientHello消息。
如果客户端不希望重新协商会话,可以选择忽略此消息,或者返回一个no_renegotitation消息。
如果服务端发送HelloRequest后没有收到ClientHello作为响应,它可能会通过致命警报关闭连接。

Client Hello
”你好,我是客户端“,该类型的消息用于首次连接服务器时,或响应服务端发送的HelloRequest,或主动发送,以便重新协商连接中的安全参数。

Random 由安全随机数生成器生成的28字节随机数,用于对称秘钥计算

Session ID 会话标识,可变长,用于会话恢复时重用其安全参数

Cipher Suites 加密算法套件,客户端提供支持的密码算法套件列表,按照客户端的偏好顺序排列,一般由四个算法组成,格式为秘钥交换算法+身份认证算法+批量加密算法+摘要算法

例:Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)

ECDHE 秘钥交换算法,由于对称秘钥并不是客户端直接生成发送给服务端的,而是双方又各自生成了一对"公私钥",然后互相交换“公钥”,这里生成“公私钥”的算法就是ECDHE
ECDSA 身份认证算法,用于加密握手消息,确认未被篡改
AES_256_GCM 批量加密算法,用于应用数据加密,它还包括秘钥大小及显示和隐式初始化向量的长度
SHA384 散列算法,用于计算主秘钥和创建会话消息摘要

Compression Methods 压缩算法列表

这四个属于固定字段,如果还需要协商其他内容,可以通过下面的扩展字段。

renegotiation_info 重新协商信息,
signature_algorithms 签名算法,列出客户端支持的哈希和签名算法,用于后续消息签名时的参考。
server_name 服务器域名,用于解决一台机器部署多个不同域名的站点,而引起证书冲突的情况。
aplication_layer_negotiation 用来确定后续的应用层协议,例http/1.1 spdy h2

Server Hello
”你好,我是服务端“,该类型的消息用于响应ClientHello消息。如果服务器找到一组可以接受的算法套件时,则回复此消息,否则响应握手失败的警报。

Random 由服务端独立生成的随机数,28字节,用于对称秘钥的计算。

Session ID 当前的会话标识,如果ClientHello的参数SessionID不为空,则服务器将在其会话告诉缓存中查找匹配项,找到并且愿意是定指定的会话状态建立连接,则服务器使用与客户端提供的SessionID作为响应,然后恢复会话并进入Finished消息流程。否则将重新生成该值以标识新会话。如果SessionID为空表明服务端不缓存此会话,无法恢复。

Cipher Suite 服务端从ClientHello.cipher_suites列表中选择的单个密码套件。如果是恢复会话,此字段为正在恢复的会话状态的值。

例:Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
ECDHE 椭圆曲线离散对数算法
RSA 非对称加密算法
AES_256_GCM 高级加密算法,对称加密,32字节的秘钥 GCM为加密模式
SHA384 安全散列算法

Compress Method 服务端从ClientHello.CompressionMethods列表中选择的压缩算法

Server Hello消息也可以包含扩展,但是这些扩展必须在ClientHello中有相同的扩展类型,服务端不能擅自添加。如果客户端在ServerHello中收到它未在ClientHello请求中关联的扩展类型,客户端必须使用unsupported_extension致命警告来终止握手。

Certificate
”服务器证书“,只要商定的秘钥交换方法使用证书进行身份验证,则服务端必须发送该证书消息。该消息紧跟在Server Hello消息后,用于向客户端下发证书。
什么时候服务端不需要下发证书呢?就是在秘钥协商过程中使用DH_anon(Diffie-Hellman ANON)匿名Diffie-Hellman算法,即不配合其他身份验证算法,而单独只读使用DH算法,这样无法保证数据被”篡改“。

 

Certificates 证书链,服务端的证书必须放在第一位,以后的证书必须直接证明其前面的证书。
证书限制

证书类型必须是X.509v3,除非另行协商。
证书必须适用于协商密码套件的秘钥交换算法和任何协商扩展。
如果客户端提供了”signature_algorithms“扩展,那么服务端提供的所有证书必须由该扩展中出现的散列/签名算法进行签名。
Server Key Exchange
”服务器秘钥交换“,用于发送服务端的预主秘钥。此消息在Certificates消息后立即发送。
当且仅当与秘钥交换算法相关联的证书类型不能为客户端提供足够的信息来交换预主秘钥时,才会发送服务器秘钥交换信息。

该消息主要描述了使用协商的秘钥交换算法后,服务端计算出来的公开秘钥。
本示例中使用的是ECDHE算法。其原理简单理解如下所示:

服务端生成随机整数a,作为私钥
计算出A = f1(a),其中a为私钥,A为公钥,并将A发送给客户端
客户端重复相同的步骤,先生成随机整数b,计算 B = f1(b),并将B发送给服务端
客户端持有A和b,服务端持有a和B
服务端和客户端计算出相同的预主秘钥 K = f2(a, B) = f2(b, A)
之后再根据预主秘钥和两个随机数计算出主秘钥
上述计算过程由ECDHE提供
服务端为了证明此消息是真实可靠的,需要用自己证书私钥和ClientHello提供的扩展signature_algorithms里选择合适摘要和签名算法对参数进行签名。

Certificate Request 可选
”要求证书“,如果服务端需要客户端提供证书以验证客户端身份,则使用此消息。消息格式与服务端下发给客户端的证书消息格式相同。

Server Hello Done
“服务器Hello完成”,表示服务端已经把支持秘钥交换的消息发送完成。发送此消息后,服务端等待客户端响应。客户端应该检查服务器是否提供了有效的证书(如果需要),并检查服务端Hello消息参数是否可接受。

Client Certificate
“客户端证书”,用于将客户端证书发送给服务端。

这是客户端在收到ServerHelloDone消息后可以发送的第一条消息。仅当服务器请求证书时才会发送此消息。如果没有合适的证书,客户端必须发送不包含证书的证书消息。也就是说,certificate_list结构的长度为零。如果客户端没有发送任何证书,服务器可以自行决定是否在没有客户端身份验证的情况下继续握手,或者使用致命的handshake_failure警报进行响应。此外,如果证书链的某些方面是不可接受的(例如,它没有由已知的,可信的CA签名),服务器可以自行决定是继续握手(考虑客户端未经身份验证)还是发送致命警报。

Client Key Exchange
“客户端秘钥交换”,用于发送客户端计算的预主秘钥。该消息是在客户端收到ServerHelloDone消息后发送的第一条消息。或者是跟在Client Certificate消息后发送。

 

这个消息内容比较简单,就是用服务端证书公钥加密的预主秘钥。

Certificate Verify
“证书验证”,此消息用于客户端证书的显示验证。发送这个消息的前提有两个:

服务端请求了客户端证书
客户端发送了非0长的证书
此时,客户端需要证明自己拥有该证书,需要用自己的私钥签名一段数据发送给服务端做验证。
签名的数据为:此消息之前的所有接收和发送过的握手消息。

Finished
“协商完成”,此消息是第一个使用上述握手过程中协商的算法和秘钥来加密的消息。接收方必须验证内容是否正确。
该消息在Change Cipher Spec消息之后被发送,以验证密钥交换和身份验证过程是否成功。
发送的数据为:PRF(master_secret, finished_label, Hash(handshake_messages))
finished_label为“client finished”/“server finished”
handshake_messages包含除去Hello Request消息之外的之前所有发送和接收的握手消息。

Change Cipher Spec Protocol
更改密码规范协议,该协议在TSL1.3被移除。

Change Cipher Spec
“变更秘钥规范”,用于通知接收方后续的通讯数据将在新协商的秘钥规范保护下交换。
此消息在Finished消息之前被发送。

客户端在交换预主秘钥后,就立即发送了“Change Cipher Spec”消息。

之后,服务端也发送一个“Change Cipher Spec”消息。

Application Data Protocol

应用数据协议
如果握手过程结束一切正常,Alice和Bob就用这条加密信道放心的发送数据了,这些数据都是被加密过的。

http-over-tls

Alert Protocol
警报消息传达消息的严重性(警告或致命)以及警报的描述。 具有致命级别的警报消息导致连接立即终止。在这种情况下,对应于会话的其他连接可以继续,但是会话标识符必须无效,防止失败的会话被用于建立新连接。与其他消息一样,警报消息按当前连接状态的指定进行加密和压缩。

警报协议包含很多异常场景,如证书过期、没有符合密码套件、未知的ca等,这里就不一一列出了。

TLS握手简化流程图
Client                 Server

ClientHello    ----------->
                   ServerHello
                   Certificate*
                   ServerKeyExchange*
                   CertificateRequest*
          <-----------    ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished      ----------->
                  [ChangeCipherSpec]
          <-----------     Finished
Application Data  <----------->  ApplicationData

中括号号标识的协议不属于握手协议
星号标识的不是必须的握手流程

至此TLS握手过程说完了,但貌似没有说到**对称秘钥(会话秘钥)**是如何被计算出来的?
我们知道,通过前面的握手流程,客户端和服务端都知道了以下信息:

预主秘钥(计算出来的)
客户端随机数
服务端随机数
通过这三个参数,客户端和服务端就可以分别计算出主秘钥:

master_secret = PRF(pre_master_secret, "master secret",
                          ClientHello.random + ServerHello.random)

master_secret 长度为48个字节。Master secret是有系列的hash值组成的

PRF (pseudo random function)伪随机函数,也就是选择的加密套件中的第四个算法SHA384。计算完主秘钥,就应当把预主秘钥从内存中删除。
PRF算法需要一个“秘钥”、一个“种子”和一个“文本标识”作为输入,然后产生一个不定长的输出。
其中的“秘钥”就是预主秘钥,“种子”就是两个随机数的和,“文本标识”就是 master secret。

因为客户端和服务端的输入参数都是一样的,所以计算出来的主秘钥也是一致的。

主秘钥是用来做对称加密的秘钥的么?
不是,需要由主秘钥再次计算,主秘钥在创建会话秘钥时作为一个熵来源。最终计算出来的结果如下:

客户端写入MAC密钥
服务器写入MAC密钥
客户端写入加密密钥
服务器写入加密密钥
客户端写入IV
服务端写入IV

实际上是生成了两个会话秘钥:

MAC(Message Authentication Code),是一个数字签名,用来验证数据的完整性,可以检测到数据是否被串改。
当客户端向服务端发送消息时,使用“客户端写入MAC秘钥”生成消息摘要附加在消息结尾,使用“客户端写入秘钥”加密;服务端接收到消息后,使用“客户端写入秘钥”解密,使用“客户端写入MAC秘钥”做消息摘要并对比两次摘要结果。
反过来则是,服务端向客户端发送消息时,使用“服务端写入MAC秘钥”生成消息摘要附加在消息结尾,使用“服务端写入秘钥”加密;客户端接收到消息后,使用“服务端写入秘钥”解密,使用“服务端写入MAC秘钥”做消息摘要并对比两次摘要结果。
如果需要在TLS层压缩数据,则在加密之前先压缩。

一些AEAD(认证加密)秘钥套件可能额外需要一个客户端写入向量和服务端写入向量

那么这些值是怎么计算出来的呢?

key_block = PRF(master_secret,“key expansion”,server_random + client_random); 
          = P_hash(master_secret, “key expansion"+server_random + client_random)

P_hash定义为: 
P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                        HMAC_hash(secret, A(2) + seed) +
                        HMAC_hash(secret, A(3) + seed) + ...
                        
A(0) = seed;
A(i) = HMAC_hash(secret, A(i-1));
...

HMAC: 基于消息认证码的哈希算法,也就是对消息进行哈希运算时添加一个秘钥。

以TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384为例,AES256会话加密算法需要32字节的会话秘钥,而我们需要2个会话秘钥(2 * 32)和2个MAC秘钥(2 * 48),就是2 * 32 + 2 * 48= 160字节。而摘要算法SHA384每次的运算输出为48字节,为了能满足160个字节,至少需要计算4次,拼接后得到172字节的输出。

然后按照上述顺序:

0~47字节为“客户端写入MAC秘钥”
48~95字节为“服务器写入MAC秘钥”
96~127字节为“客户端写入加密秘钥”
128~159字节为“服务端写入加密秘钥”
剩下的12字节则舍弃。

总结:

握手过程中,客户端和服务端交换了哪些东西?

两个随机数
DH算法的公钥
证书
握手过程中,哪些消息是经过加密的?加密的目的是什么?

Server/Client Key Exchange 使用服务器私钥加密,防止中间人篡改
Finished 使用主秘钥加密并签名,验证主秘钥是否正确生成和握手消息是否被篡改
如果服务端的私钥泄漏,坏人能否解密会话数据?
RSA 可以
DH 不可以
PSK 不可以

参考:TLS握手协议分析与理解——某HTTPS请求流量包分析

解密应用数据方式:

启动Chrome并制定SSL的日志文件

sudo /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome  --ssl-key-log-file=/Users/joye/Downloads/sslkeylog.log

在WireShark中Preferences->Protocols->SSL-(Pre)-Master-Secret log filename设置日志文件

 

posted on 2019-05-01 19:26  林肯公园  阅读(9171)  评论(0编辑  收藏  举报

导航