文章目录
- tls.go中的流程梳理
- b站博主的 tls 加密过程
- 自己推理的过程(未完待续)
- 发送ClientHello
- 获取ServerHello
tls.go中的流程梳理
b站博主的 tls 加密过程
- 客户端发送
ClentHello
(tls版本 +加密套件+ 随机数1) - 服务器发送
ServerHello
(tls版本 + 加密套件 +随机数2)- 这个阶段之后,双方都知道了tls版本,选定的加密算法,两个随机数
- 服务器发送一个
X.509
证书,客户端用于验证且知道了服务器的公钥,用于后续传输数据加密 - 服务器发送它自己的公钥,若上一步有,则这一步不需要
- 服务器发送
server Hello Done
- 客户端生成 随机数3(预主密钥),并用服务器公钥发送给客户端
- 至此 双方都知道了3个随机数,根据3个随机数得到对称加密的秘钥
Change Cipher Spec
表示随后的信息都将用双方商定的加密方法和密钥发送
自己推理的过程(未完待续)
发送ClientHello
- 客户端发送
Dial(network, addr string, config *Config) (*Conn, error)
首先调用了Dialer
拨号方法得到了 rawConn
,然后通过Client(conn net.Conn, config *Config)
封装了tls
包下的Conn
结构。然后进行握手c.HandshakeContext
// 重要代码 ctx context.Context
func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {rawConn, err := netDialer.DialContext(ctx, network, addr)// 获取主机名 hostnamecolonPos := strings.LastIndex(addr, ":")if colonPos == -1 {colonPos = len(addr)}hostname := addr[:colonPos]// 握手阶段 此处初始化了Clientconn := Client(rawConn, config)if err := conn.HandshakeContext(ctx); err != nil {rawConn.Close()return nil, err}return conn, nil
}
- 分析
conn := Client(rawConn, config)
发现有一个函数 c.handshakeFn = c.clientHandshake
后续要用到
func Client(conn net.Conn, config *Config) *Conn {c := &Conn{conn: conn,config: config,isClient: true,}c.handshakeFn = c.clientHandshakereturn c
}
- 点到
conn.HandshakeContext(ctx)
分析
// 删掉无关代码
func (c *Conn) handshakeContext(ctx context.Context) (ret error) {// 在此处做了 client 的 handshakec.handshakeErr = c.handshakeFn(handshakeCtx)
}
- 点到
c.handshakeFn(handshakeCtx)
func (c *Conn) clientHandshake(ctx context.Context) (err error) { // 此处初始化了 hello 报文hello, ecdheKey, err := c.makeClientHello()
}// 下面的函数生成了 hello 报文 包括密钥空间 密钥等等
func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {hello := &clientHelloMsg{vers: clientHelloVersion,compressionMethods: []uint8{compressionNone},random: make([]byte, 32),extendedMasterSecret: true,ocspStapling: true,scts: true,serverName: hostnameInSNI(config.ServerName),supportedCurves: config.curvePreferences(),supportedPoints: []uint8{pointFormatUncompressed},secureRenegotiationSupported: true,alpnProtocols: config.NextProtos,supportedVersions: supportedVersions,}var key *ecdh.PrivateKeyreturn hello, key, nil
}
- 生成
hello
报文后,调用函数c.writeHandshakeRecord
发送数据,c.readHandshake
读取数据
func (c *Conn) clientHandshake(ctx context.Context) (err error) {hello, ecdheKey, err := c.makeClientHello()if _, err := c.writeHandshakeRecord(hello, nil); err != nil {return err}// serverHelloMsg is not included in the transcriptmsg, err := c.readHandshake(nil)serverHello, ok := msg.(*serverHelloMsg)return nil
}
获取ServerHello
如上:在发送完ClientHello
信息后使用c.readHandshake()
,获取从服务器过来的ServerHello
信息。然后是使用类型强转serverHello, ok := msg.(*serverHelloMsg)
然后根据SeverHello
中选择的TLS
版本和ClientHello
中的版本范围进行校验。看服务器发送过来的TLS版本是否在ClientHello
指定的范围中。