tls 代码阅读
概览
分别从客户端和服务端角度,解决整个握手流程,基于go 1.16版本进行分析
重点:三个随机数:client hello随机数、server hello随机数、keyExchange 随机数
三个随机数生成后续使用对称算法减的密钥
客户端和服务端都是基于Conn进行操作的
客户端处理流程
概览
使用单独goroutine迁移处理tls
代码实现,handshake_client.go
入口:clientHandshake
- makeClientHello:
- 支持版本
- 支持的cipher suite
- 随机数
- 支持的算法
- loadSession:如果开启了session,客户端和服务端均开启
- 发送helloMsg,writeRecord(hello.marshal())
- 获取serverMsg
- 读取serverHelloMsg
- 处理serverHelloMsg
- 确认cipher suite
- server确认是否开启session
如果开启,session没有过期- establishKeys
- readSessionTicket
- readFinished
- sendFinished
- 如果session不存在,doFullHandshake,做全量握手
- 读取certificateMsg
- 读取certificateStatusMsg请求
- 如果第一次握手,校验服务端证书verifyServerCertificate
- 读取serverKeyExchangeMsg请求
- processServerKeyExchange
- 读取certificateRequestMsg
- 如果请求存在,则发送客户端证书
- 读取serverHelloDoneMsg请求
- generateClientKeyExchange,创建客户端keyExchange
- establishKeys
- 设置当前Conn的cipher suite
- sendFinished
- readSessionTicket,读取newSessionTicketMsg请求,
- 读取finishedMsg请求
服务端处理流程
概览
代码实现,handshake_server.go
入口:serverHandshake、serverHandshakeState
- readClientHello
- 读取clientHelloMsg请求,决策使用什么tls版本
- handshake
- serverHandshakeStateTLS13
- serverHandshakeState
- processClientHello
- 是否resume session
- pickCipherSuite:确认使用的cipher suite
- doFullHandshake
- writeRecord(serverHello)
- writeRecord(certMsg)
- writeRecord(certStatus)
- generateServerKeyExchange
- writeRecord(serverKeyExchangeMsg)
- 如果校验客户端证书,writeRecord(certificateRequestMsg)
- writeRecord(serverHelloDoneMsg)
- 读取客户端请求certificateMsg
- 读取客户端clientKeyExchangeMsg
- processClientKeyExchange,获取masterSecret
- 读取certificateVerifyMsg
- establishKeys
- readFinished
- sendSessionTicket
- sendFinished
session实现
概览
session需要客户端和服务端均开启,通过server加密ticket,返回给客户端
客户端使用lru算法,存储服务端ticket
实现
服务端:
type sessionState struct {
vers uint16
cipherSuite uint16
createdAt uint64
masterSecret []byte // opaque master_secret<1..2^16-1>;
// struct { opaque certificate<1..2^24-1> } Certificate;
certificates [][]byte // Certificate certificate_list<0..2^24-1>;
// usedOldKey is true if the ticket from which this session came from
// was encrypted with an older key and thus should be refreshed.
usedOldKey bool
}
客户端
存储lruSessionCache
type ClientSessionState struct {
sessionTicket []uint8 // Encrypted ticket used for session resumption with server
vers uint16 // TLS version negotiated for the session
cipherSuite uint16 // Ciphersuite negotiated for the session
masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret
serverCertificates []*x509.Certificate // Certificate chain presented by the server
verifiedChains [][]*x509.Certificate // Certificate chains we built for verification
receivedAt time.Time // When the session ticket was received from the server
ocspResponse []byte // Stapled OCSP response presented by the server
scts [][]byte // SCTs presented by the server
// TLS 1.3 fields.
nonce []byte // Ticket nonce sent by the server, to derive PSK
useBy time.Time // Expiration of the ticket lifetime as set by the server
ageAdd uint32 // Random obfuscation factor for sending the ticket age
}