前面我已经写过一些有关TLS1.3协议的文章,主要是从理论出发去了解TLS1.3协议,为了更加深入的理解TLS1.3协议,我将尝试去实现它,目前有部分站点已经开始支持TLS1.3,我们可以利用这些站点来进行测试代码是否成功实现TLS1.3的部分结构,我现在主要实现了ClientHello的整体结构和部分扩展,但是在进行测试的时候不尽如人意,我们先来看一下测试情况。 我们发现服务器并没有回应,具体原因我还没有找到。 服务器发送回了ServerHello和Certificate以及ServerHelloDone消息可以说明整体结构编写没有问题,可能还存在一些不符合协议的错误,我会及时更新改正后的实现到博客,下面我们先来看一下整体结构的实现。 下面给出我实现的整体架构 通过这张图就可以清晰的了解到TLS1.3实现的基本流程,首先是实现ClientHello的结构以及里面包含的扩展,然后实现handshake的整体结构,获取ClientHello的数据放入handshakeData中,之后实现TLSPlaintext的结构,获取handshake的数据放入fragment字段,最后封装数据采用大端字节编码,将数据发送给服务器,其中TLSPlaintext属于Record层,它会把数据分割成可处理的块,每个块的大小不能超过2^14字节。 TLS1.3 包含一系列子协议,如 Record Protocol、Handshake Protocol 、Alert Protocol 、ApplicationData Protocol 等 三者的关系如图: 这是实现的一个用于序列化数据的接口,所有字段都需要实现这三个方法。 前面已经提到过,ClientHello的数据返回给handshakeData 其中每个字段都需要重新定义结构体,并且要实现接口中的方法用于序列化和组织数据。 random是32字节的随机数,前4个字节用于显示当前的unix时间,uint32是表示占32位的无符号整数,所以正好是4字节。以此为例说一下三个方法的含义: ClientHello中其他字段的实现方式与其类似,就不再赘述了,主要是搞清楚数据的结构以及所占的字节数,我建议大家用wireshark截取包之后参照里面的字段大小,这样更加准确、快捷。 legacyVersion设置为0x0303,是为了兼容版本,除此之外还有legacySessionID、legacyCompressionMethods都是为了兼容其他版本。 cipherSuites是Client所支持的密码套件 主要是通过对每个字段的联合组织,然后将它们放到一个数组中去,组织成一个整体的数据。 其中extensionData我采用了接口类型,所以其它扩展也需要组织数据,即每一个字段都要实现前面提到的三个方法。 请注意lenth指的是字段Extensions的长度,而不是length和Extensions的长度和。 我们要遍历数组中的所有extension然后把它们转换成byte 其它的我就不详细说了,实现过程都是类似的,要特别注意的就是字段的长度要搞清楚,再下手。 这个扩展实现起来比较简单,主要难点就是对里面数组的处理,也就是NamedGroups 因为它需要实现三个方法,所以需要重新定义,不然只需要给出一个数组就可以了如下: 扩展中要有生成对应扩展的函数: 首先生成NamedGroups,然后用其生成SupportedGroup,最后调用NewExtension函数生成新的Extension。 TLS1.3主要的扩展之一KeyShare,它里面的生成的公钥对应于SupportedGroup中的曲线,本文实现的主要是椭圆曲线:P-256、P-384 、P-521。 最主要的部分是KeyShareEntry 这个结构我是参考别人实现的过程实现的,主要的功能就是生成对应曲线的公钥并返回。 其中的UncompressedPointRepresentation结构如下: 我在前一篇博客里面对应的也有详细的介绍。 其它的扩展实现过程与这两个比较相似,参考实现即可。 handshakeData是接口类型,其中的值对应ClientHello的值,而且它的值又对应TLSPlaintext的值,所以其实现结构也与ClientHello类似。 值得强调的是,handshakeSize的大小是3个字节24位,因为handshakeData是接口类型,所以可以返回数据的长度,是int类型的,需要转换成字节类型存储在handshakeSize中,所以需要进行位运算转换成byte。 handshake中的数据封装到字段fragment中,然后打包传输到服务器,我们来看一下它的结构: 其中的legacyRecordVersion字段有好几张中说法,有的说设置成0x0301,有的说设置成0x0303兼容版本,具体我还没搞明白。 类比handshakeSize,同样要进行类型的转换。 这样就基本实现了ClientHello的整体结构,编码成大端字节发送给服务器就可以了,就是将组合成的TLSPlaintext数据编码发送即可,我们来看一下实现代码: 在golang中可以写成 其它的枚举像:ExtensionType、ContentType等都与其类似。 这种数据类型包含两部分,head+body,也可以理解为head是body的长度,而body就是存储的数据,在golang中我们可以表示成: 因为head是2所以占两个字节也就是16位,又因为是无符号的类型,所以我们可以用uint16来表示。它这个可变长的意思就是里面可能存在多个CipherSuites。 表示Random类型占用了32个字节,其中opaque表示不透明的数据结构,可以理解为byte数组。 在golang中实现如下: 但是考虑到random的结构包括unix时间所以结构是: 其中的random_bytes为28个字节长度。 还有一些其他的数据结构,请大家自行解决吧。 最后我们来看一下,我发送出去的包的样子吧! StrideMaxZZ,欢迎大家访问!前言
TLS1.3实现-整体架构
协议之间的关系
接口
type Serializable interface { GetSize() int Serialize() []byte SerializeInto([]byte) }
ClientHello
整体结构代码
type ClientHello struct { legacyVersion ProtocolVersion random ClientRandom legacySessionID legacySessionId cipherSuites CipherSuites legacyCompressionMethods legacyCompressionMethods extensions Extensions }
type ClientRandom struct { gmt_unix_time uint32 random_bytes []byte } func NewClientRandom()ClientRandom{ var random = ClientRandom{ gmt_unix_time: uint32(time.Now().Unix()), random_bytes: make([]byte,28), } rand.Read(random.random_bytes) return random } func (random ClientRandom) GetSize() int { return 32 } func (random ClientRandom) SerializeInto(buf []byte) { binary.BigEndian.PutUint32(buf[0:4], random.gmt_unix_time) copy(buf[4:31],random.random_bytes) } func (random ClientRandom) Serialize() []byte { obj := make([]byte,random.GetSize()) random.SerializeInto(obj) return obj }
New结构体的代码
func NewClientHello(cp []CipherSuite,exts ...Extension) ClientHello{ var NewRandom ClientRandom NewRandom = NewClientRandom() //rand_bytes := make([]byte,32) //rand.Read(rand_bytes) return ClientHello{ legacyVersion: TLS12, random: NewRandom, legacySessionID: NewlegacySessionId(nil), cipherSuites: NewCipherSuites(cp), legacyCompressionMethods: NewlegacyCompressionMethods([]legacyCompressionMethod{0}), extensions: NewExtensions(exts...), } }
const ( CIPHER_SUITE_UNKNOWN CipherSuite = 0x0000 TLS_AES_128_GCM_SHA256 CipherSuite = 0x1301 TLS_AES_256_GCM_SHA384 CipherSuite = 0x1302 TLS_CHACHA20_POLY1305_SHA256 CipherSuite = 0x1303 TLS_AES_128_CCM_SHA256 CipherSuite = 0x1304 TLS_AES_256_CCM_8_SHA256 CipherSuite = 0x1305 )
组织结构体数据
func (hello ClientHello) GetSerialization() NestedSerializable { return NewNestedSerializable([]Serializable{ hello.legacyVersion, hello.random, hello.legacySessionID, hello.cipherSuites, hello.legacyCompressionMethods, hello.extensions, }) }
Extensions
Extension
type Extension struct { extensionType ExtensionType length ExtensionSize extensionData Serializable } func NewExtension(ext_type ExtensionType,ext_data Serializable) Extension { length := ext_data.GetSize() return Extension{ extensionType: ext_type, length: ExtensionSize(length), extensionData: ext_data, } }
extensions
type Extensions struct { length uint16 Extensions []Extension } func NewExtensions(exts ...Extension)Extensions{ l := 0 for _,ext := range exts{ l += ext.GetSize() } return Extensions{ uint16(l), exts, } }
转成byte
func (ext Extensions) SerializeInto(buf []byte) { binary.BigEndian.PutUint16(buf[0:2],ext.length) var start int = 2 for _,ext := range ext.Extensions { var end int = start + ext.GetSize() copy(buf[start:end],ext.Serialize()) start = end } }
SupportedGroup
type SupportedGroup struct { length SgSize Group NamedGroups } func NewSupportedGroup(group NamedGroups) SupportedGroup { l := NewSgSize(group.GetSize()) return SupportedGroup{ length: l, Group: group, } }
type SupportedGroup struct { length SgSize Group []NamedGroup }
func NewSupportedGroupExtension(group []NamedGroup) Extension { sg := NewNamedGroups(group) ssg := NewSupportedGroup(sg) return NewExtension(supported_groups ,ssg.GetSerializetion()) }
KeyShare
type KeyShare struct { length KsSize shares KeyShareEntrys } func NewKeyShare(share KeyShareEntrys)KeyShare { l := 0 l = share.GetSize() return KeyShare{ length: KsSize(l), shares: share, } }
type KeyShareEntry struct { group NamedGroup length uint16 keyExchange []byte } func NewKeyShareEntry(group NamedGroup) (KeyShareEntry,[]byte) { var curve elliptic.Curve switch group { case Secp256r1: curve = elliptic.P256() break case Secp384r1: curve = elliptic.P384() break case Secp521r1: curve = elliptic.P521() break } priv, x, y, err := elliptic.GenerateKey(curve,rand.Reader) if err != nil { panic(err) } nu := NewUncompressedPointRepresentation(x.Bytes(),y.Bytes()) ks := KeyShareEntry{ group: group, length: uint16(nu.GetSize()), keyExchange: nu.Serialize(), } return ks,priv }
type UncompressedPointRepresentation struct { legacyForm uint8 X []byte Y []byte } func NewUncompressedPointRepresentation(x,y []byte) UncompressedPointRepresentation { return UncompressedPointRepresentation{ legacyForm: 4, X: x, Y: y, } }
Handshake
结构
type Handshake struct { msgType HandshakeType length HandshakeSize handshakeData Serializable } func NewHandshake(msg_type HandshakeType,data Serializable)Handshake{ size := NewHandshakeSize(data.GetSize()) return Handshake{ msgType: msg_type, length: size, handshakeData: data, } }
handshakeSize
type HandshakeSize [3]byte func NewHandshakeSize(num int)HandshakeSize { return [3]byte{ uint8(num >> 16), uint8(num >> 8), uint8(num)} }
TLSPlaintext
结构
type TLSPlaintext struct { ContentType ContentType legacyRecordVersion ProtocolVersion length ContentSize fragment Serializable } func NewTLSPlaintext(contentType ContentType,fragment Serializable)TLSPlaintext{ length := NewContentSize(fragment.GetSize()) return TLSPlaintext{ ContentType: contentType, legacyRecordVersion: TLS12, length: length, fragment: fragment, } }
ContentSize
type ContentSize [2]byte func NewContentSize(num int) ContentSize { var ret [2]byte binary.BigEndian.PutUint16(ret[0:2], uint16(num)) return ret }
小结
func firstClientHello(){ fmt.Println("正在发送ClientHello") //extension servername := tls.NewServerNameListExtension([]tls.ServerName{"baidu.com"}) supportedVersion := tls.NewSupportedVersionsExtension([]tls.ProtocolVersion{ tls.TLS13, tls.TLS12, tls.TLS11, tls.TLS10, }) supportedGroup := tls.NewSupportedGroupExtension([]tls.NamedGroup{ tls.Secp256r1, tls.Secp384r1, tls.Secp521r1}) keyshare := tls.NewKeyShareExtension([]tls.NamedGroup{ tls.Secp256r1, tls.Secp384r1, tls.Secp521r1}) signaturealgorithms := tls.NewSignatureAlgorithmsExtension([]tls.SignatureScheme{ tls.Ecdsa_secp256r1_sha256, tls.Ecdsa_secp384r1_sha384, tls.Ecdsa_secp521r1_sha512}) //body ClientHelloBody := tls.NewClientHello([]tls.CipherSuite{ tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_128_CCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_AES_256_CCM_8_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 , tls.TLS_CHACHA20_POLY1305_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_RSA_WITH_AES_256_CBC_SHA},supportedVersion,signaturealgorithms,servername,supportedGroup,keyshare) ClientHandshake := tls.NewHandshake(tls.HandshakeTypeClientHello,ClientHelloBody.GetSerialization()) ClientHandshakeMessage := tls.NewTLSPlaintext(tls.RecordTypeHandshake,ClientHandshake.GetSerialization()) getConnect("tcp","www.baidu.com:443",ClientHandshakeMessage.GetSerialization().Serialize()) fmt.Println("发送成功!") }
有关TLS数据结构的实现
枚举类型
enum { client_hello(1), server_hello(2), new_session_ticket(4), end_of_early_data(5), encrypted_extensions(8), certificate(11), certificate_request(13), certificate_verify(15), finished(20), key_update(24), message_hash(254), (255) } HandshakeType;
type HandshakeType uint8 const ( HandshakeTypeClientHello HandshakeType = 1 HandshakeTypeServerHello HandshakeType = 2 HandshakeTypeNewSessionTicket HandshakeType = 4 HandshakeTypeEndOfEarlyData HandshakeType = 5 HandshakeTypeHelloRetryRequest HandshakeType = 6 HandshakeTypeEncryptedExtensions HandshakeType = 8 HandshakeTypeCertificate HandshakeType = 11 HandshakeTypeCertificateRequest HandshakeType = 13 HandshakeTypeCertificateVerify HandshakeType = 15 HandshakeTypeServerConfiguration HandshakeType = 17 HandshakeTypeFinished HandshakeType = 20 HandshakeTypeKeyUpdate HandshakeType = 24 HandshakeTypeMessageHash HandshakeType = 254 )
可变长数组类型
CipherSuite cipher_suites<2..2^16-2>; uint8 CipherSuite[2];
type CipherSuites struct { length uint16 ciphersuites []CipherSuite } type CipherSuite uint16
定长数组
opaque Random[32]
type ClientRandom [32]byte
type ClientRandom struct { gmt_unix_time uint32 random_bytes []byte }
wireshark截取包
我的blog
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算