0
点赞
收藏
分享

微信扫一扫

Go中TLS源码学习(4)之Server端第一次TLS握手


Go中TLS源码学习(4)之Server端第一次TLS握手_字段



????SSL/TLS专栏目录导航????

专栏包括的内容:

????1. SSL/TLS原理知识

????2. Go源码中TLS实现

????3. openssl中TLS实现

????4. SSL卸载

????5. SSL代理

????6. SSL V.P.N

????7. SSL 与 IPSec

????8. 其他




文章目录

  • ​​5.3.2 TLS第一次握手​​
  • ​​1. 读取Client Hello报文​​
  • ​​2. 解析ClientHello报文​​
  • ​​????????关于server random的说明:​​
  • ​​????????关于椭圆曲线和基点​​
  • ​​????????TODO: 证书相关、签名算法、解密算法​​
  • ​​3. TLS快速协商​​
  • ​​4. 算法套件选择​​

5.3.2 TLS第一次握手

先看看ClientHello报文内容,再看代码处理逻辑:

Go中TLS源码学习(4)之Server端第一次TLS握手_服务端_02

Server端的tls握手流程中,第一项工作便是读取ClientHello报文然后通过ClientHello确定当前TLS版本(目前有TLS1.0, TLS1.1, TLS1.2,TLS1.3),当下主流浏览器(Chrome, FireFox, IE等)都已经支持TLS1.3,不过网络上的大多数TLS服务还是TLS1.2, 以及少量的TLS1.1和TLS1.3。

服务端的TLS握手读取ClientHello报文,并通过“双方同时支持的最高版本”的原则确定TLS采用的版本,然后根据协商出的版本进入相应TLS版本处理函数:

Go中TLS源码学习(4)之Server端第一次TLS握手_https_03下面对代码中的关键部分做介绍。其中主要包括:

  • 1)读取ClientHello报文
  • 2)解析ClientHello报文,TLS版本协商
  • 3)TLS快速协商
  • 4)算法套件协商

1. 读取Client Hello报文

tls握手报文读取,全部是通过readHandshake()接口来完成的。readClientHello()接口对readHandshake()接口再次封装,只获取ClientHello报文。 在readClientHello成功获取到tls的clientHello协商报文之后,便是解析报文中的TLS版本信息

Go中TLS源码学习(4)之Server端第一次TLS握手_学习_04

关于TLS的版本选择,需要做个重点说明: 在 ClientHello报文中,有两个与版本有关的字段:记录层的version, 扩展字段supported_version。记录层的version用来标明当前客户端tls握手时采用的版本;supported_version来用标明客户端支持的所有版本。由于TLS版本需要握手双方进行协商确定,clientHello报文时,version字段只不过是临时采用的一个版本而已。

???? 对于TLS1.0, TLS1.1,TLS1.2, 可以通过记录层中的版本字段来获取(不发送supported_version),此时supported_version默认支持包括此版本在内的之前所有的TLS版本;

???? 但是TLS1.3有点特殊:TLS1.2与TLS1.3 的握手报文中version字段都是​0x0303​,也就是TLS1.2版本。TLS1.3则是通过扩展字段​supported_version​来特别说明

  • TLS1.2截图
    Go中TLS源码学习(4)之Server端第一次TLS握手_学习_05
  • TLS1.3截图
    Go中TLS源码学习(4)之Server端第一次TLS握手_学习_06因此,确定客户端支持的TLS版本时,需要通过记录层中的Version以及扩展字段中的SupportedVersion共同确定服务端支持的TLS版本则是通过配置来确定(c.config.mutualVersion(clientVersions))。

????????????:Go中TLS的版本可以在创建HTTPS服务时进行配置:例如下面代码中TLS版本限定为TLS1.2版本。

Go中TLS源码学习(4)之Server端第一次TLS握手_https_07分别获取到客户端和服务端的TLS版本后,便需要确定当前握手需要采用的TLS版本。协商的原则是:从客户端支持的版本中选择一个双方都支持的版本,版本越高优先级越高。长江后浪推前浪,前浪拍死在沙滩上。Go中协商TLS版本的函数如下:

Go中TLS源码学习(4)之Server端第一次TLS握手_学习_08

确定TLS的版本后,根据当前协商的版本进入对应TLS握手流程。后面主要学习TLS1.2版本的握手流程。

通过上面的流程,便成功协商出要采用的TLS版本,后面便是解析ClientHello报文。

2. 解析ClientHello报文

TLS握手的核心处理函数!!!,关键操作都在此函数中实现。 后面主要介绍此函数及其子函数的实现。Go中TLS源码学习(4)之Server端第一次TLS握手_服务端_09这里先介绍ClientHello解析函数相关部分:processClientHello()​,其他的函数在后面TLS交互报文中再介绍。processClientHello函数中有几个关键操作:

  • 确定clientHello中支持的压缩算法(compressionNone)
  • 分配Server Random随机数空间,并填充;如果TLS版本降级,则需要特殊处理ServerRandom
  • 检查是否支持client hello中的椭圆曲线组以及基点
  • 证书采用的解密算法和签名算法(搞什么鬼,没看懂)
  • 填充ServerHello报文的部分字段

源码实现如下:

Go中TLS源码学习(4)之Server端第一次TLS握手_https_10从上面可以看出:此函数并未解析完整的ClientHello报文,只解析了其中的压缩算法和椭圆曲线部分,以及生成了ServerHello中的Random的值;其他部分的解析仍在后面的流程中完成。以下对代码中关键部分做一个说明:

????????关于server random的说明:

TLS协商过程中有两个重要的随机数:client random,server random;分别位于ClientHello,ServerHello报文中。random的大小为32字节,填充的内容由Client和Server各自随机产生。

Go中TLS源码学习(4)之Server端第一次TLS握手_学习_11

如果TLS协商过程中,存在降级情况(双方协商出的TLS版本不是服务端最高版本,一般是协商出的版本为TLS1.1或者TLS1.0时存在降级现象),在这种情况下,为了安全起见,在Server Random随机数据的后8个字节填充上降级信息,前24字节填充随机数。降级时填充的信息如下:

Go中TLS源码学习(4)之Server端第一次TLS握手_golang_12

????????关于椭圆曲线和基点

报文中的椭圆曲线和基点载荷信息如下:

Go中TLS源码学习(4)之Server端第一次TLS握手_https_13

目前TLS支持的椭圆曲线共有四组,而基点统一采用0。因此这两个载荷的校验包括:1)椭圆曲线ID是否为四者之一;2)基点是否为0

Go中TLS源码学习(4)之Server端第一次TLS握手_https_14

????????TODO: 证书相关、签名算法、解密算法

在解析ClientHello报文过程中,会加载本端的证书、以及证书中采用的加解密算法、签名算法信息。其中加载证书时,服务端证书配置存在三种情况:

  • 未配置证书
    这种情况需要从对端的ClientHello中获取证书。
  • 配置一个证书Go中TLS源码学习(4)之Server端第一次TLS握手_学习_15 一般情况下都是配置一个证书,此时直接返回该证书即可
  • 配置多个证书
    此时需要根据ClientHello中的ServerName载荷或者SupportsCertificates载荷来确定最终采用的证书。

针对上述三种情况,目前我只见过第二种;其他两种情况权当扩展了。Go中实现如下:

在processClientHello()最后便是获取证书中的签名算法和解密算法,供后续报文交互时使用。

3. TLS快速协商

在​​func (hs *serverHandshakeState) handshake()​​中,有一部分是关于TLS复用当前链接从而实现TLS快速协商的流程,这里暂不介绍。后期如果需要,单独介绍TLS快速协商流程。

4. 算法套件选择

下面是分别获取客户端和服务端支持的算法列表,并确定优先采用的算法列表,采取的原则是:如果本端配置了算法信息,则优先采用本端算法;如果本端未配置算法参数,则对端算法优先。

Go中TLS源码学习(4)之Server端第一次TLS握手_golang_16在优先算法列表中,从头开始遍历,找到客户端和服务端都支持的一个算法即为最终协商后的算法。具体的算法选择函数如下:

Go中TLS源码学习(4)之Server端第一次TLS握手_https_17Go中目前包括的算法有(部分算法信息):

Go中TLS源码学习(4)之Server端第一次TLS握手_学习_18

至此,server端的ClientHello报文便处理完了;上文中主要介绍了ClientHello报文中的版本、ClientRandom、算法套件、支持的椭圆曲线算法和基点这几个主要载荷。Session ID载荷也非常重要,不过在协商流程中,它主要是为了复用其他session信息完成快速协商时使用,这里暂未介绍。



举报

相关推荐

0 条评论