文章目录
- 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)
// 获取主机名 hostname
colonPos := strings.LastIndex(addr, ":")
if colonPos == -1 {
colonPos = len(addr)
}
hostname := addr[:colonPos]
// 握手阶段 此处初始化了Client
conn := 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.clientHandshake
return c
}
- 点到
conn.HandshakeContext(ctx)
分析
// 删掉无关代码
func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
// 在此处做了 client 的 handshake
c.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.PrivateKey
return 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 transcript
msg, err := c.readHandshake(nil)
serverHello, ok := msg.(*serverHelloMsg)
return nil
}
获取ServerHello
如上:在发送完ClientHello
信息后使用c.readHandshake()
,获取从服务器过来的ServerHello
信息。然后是使用类型强转serverHello, ok := msg.(*serverHelloMsg)
然后根据SeverHello
中选择的TLS
版本和ClientHello
中的版本范围进行校验。看服务器发送过来的TLS版本是否在ClientHello
指定的范围中。