首页 > 其他分享 >【Go语言】quic-go实现0-RTT传输

【Go语言】quic-go实现0-RTT传输

时间:2024-09-15 12:20:31浏览次数:3  
标签:tls err nil x509 RTT quic go

核心思路:在客户端的tls文件中缓存第一次连接留下来的会话票据,在第二次连接中就可以实现0-RTT。为此,重要的是实现tls.Config.ClientSessionCache这个接口的具体结构体


文件目录


tlscfg.go代码:这个模块主要用于实现客户端和服务器的tls配置

package tlscfg

import (
	"crypto"
	"crypto/ed25519"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"log"
	"math/big"
	"net"
	"time"
)

const alpn string = "zerortt"

// 产生自签名的根证书和私钥
func GenerateCA() (*x509.Certificate, crypto.PrivateKey, error) {
	certTempl := &x509.Certificate{
		SerialNumber:          big.NewInt(2019),
		Subject:               pkix.Name{},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().Add(24 * time.Hour),
		IsCA:                  true,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
	}
	pub, priv, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		return nil, nil, err
	}
	caBytes, err := x509.CreateCertificate(rand.Reader, certTempl, certTempl, pub, priv)
	if err != nil {
		return nil, nil, err
	}
	ca, err := x509.ParseCertificate(caBytes)
	if err != nil {
		return nil, nil, err
	}
	return ca, priv, nil
}

// 产生一个服务器(叶)证书和私钥
func GenerateLeafCert(ca *x509.Certificate, caPriv crypto.PrivateKey) (*x509.Certificate, crypto.PrivateKey, error) {
	certTempl := &x509.Certificate{
		SerialNumber: big.NewInt(1),
		DNSNames:     []string{"localhost"},
		IPAddresses:  []net.IP{net.IPv4(127, 0, 0, 1)},
		NotBefore:    time.Now(),
		NotAfter:     time.Now().Add(24 * time.Hour),
		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		KeyUsage:     x509.KeyUsageDigitalSignature,
	}
	pub, priv, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		return nil, nil, err
	}
	certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, ca, pub, caPriv)
	if err != nil {
		return nil, nil, err
	}
	cert, err := x509.ParseCertificate(certBytes)
	if err != nil {
		return nil, nil, err
	}
	return cert, priv, nil
}

// 产生服务器tls config
// 首字母必须大写才能够被main,go import识别
func GetTLSServerConfig() *tls.Config {
	ca, caPrivateKey, err := GenerateCA()
	if err != nil {
		log.Fatal(err)
	}
	leafCert, leafPrivateKey, err := GenerateLeafCert(ca, caPrivateKey)
	if err != nil {
		log.Fatal(err)
	}
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{{
			Certificate: [][]byte{leafCert.Raw},
			PrivateKey:  leafPrivateKey,
		}},
		NextProtos:         []string{alpn},
		InsecureSkipVerify: true,
	}
	return tlsConfig
}

// 产生客户端 tls config
func GetTLSClientConfig() *tls.Config {
	ca, _, _ := GenerateCA()
	root := x509.NewCertPool()
	root.AddCert(ca)
	tlsClientConfig := &tls.Config{
		ServerName:         "localhost",
		RootCAs:            root,
		NextProtos:         []string{alpn},
		InsecureSkipVerify: true,
	}

	return tlsClientConfig
}

sessionCache.go代码:主要实现了tls.Config.ClientSenssionCache的具体结构体

package cltSessionCache

import (
	"crypto/tls"
)

// 这段代码实现了一个clientSessionCache类型,这个类型实现了tls.ClientSessionCache的所有接口
type clientSessionCache struct {
	cache tls.ClientSessionCache

	gets chan<- string
	puts chan<- string
}

func NewClientSessionCache(cache tls.ClientSessionCache, gets, puts chan<- string) *clientSessionCache {
	return &clientSessionCache{
		cache: cache,
		gets:  gets,
		puts:  puts,
	}
}

var _ tls.ClientSessionCache = &clientSessionCache{}

func (c *clientSessionCache) Get(sessionKey string) (*tls.ClientSessionState, bool) {

	session, ok := c.cache.Get(sessionKey)
	if c.gets != nil {
		if session != nil {
			//fmt.Printf("Client:The Session Ticket is in my Cache!\n")
			//fmt.Printf("Info: %#v\n", session)
		}
		//fmt.Printf("Ticket: %v\n",c.ticket)
		c.gets <- sessionKey
	}
	return session, ok
}

func (c *clientSessionCache) Put(sessionKey string, cs *tls.ClientSessionState) {
	//fmt.Printf("Client Get a Session Ticket %s!\n", sessionKey)
	//fmt.Printf("Info: %#v\n", cs)
	c.cache.Put(sessionKey, cs)
	if c.puts != nil {
		c.puts <- sessionKey
	}

}

zerorttExpir.go代码:进行三次连接,后面两次连接实现了0-RTT

package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"zerorttExpir/cltSessionCache"
	"zerorttExpir/tlscfg"

	quic "github.com/quic-go/quic-go"
)

// 服务器监听的端口号
const port string = "4242"

func getQuicConfig(conf *quic.Config) *quic.Config {
	if conf == nil {
		conf = &quic.Config{Allow0RTT: true}
	} else {
		conf = conf.Clone()
		conf.Allow0RTT = true
	}
	return conf
}

// 产生指定大小的比特数据,用于模拟数据传输
func GeneratePRData(l int) []byte {
	res := make([]byte, l)
	seed := uint64(1)
	for i := 0; i < l; i++ {
		seed = seed * 48271 % 2147483647
		res[i] = byte(seed)
	}
	return res
}

func Transfer0RttData(serverTLSConf *tls.Config, serverConf *quic.Config, clientTLSConf *tls.Config, clientConf *quic.Config) {

	fmt.Printf("Start Server Listening on Port %s \n", port)
	//服务器监听指定端口
	ln, err := quic.ListenAddrEarly(
		"localhost:"+port,
		serverTLSConf,
		serverConf,
	)

	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()

	//启动一个goroutine等待客户端发来的stream数据
	go func() {
		conn1, err := ln.Accept(context.Background())
		if err != nil {
			log.Fatal(err)
		}
		str, err := conn1.AcceptStream(context.Background())
		if err != nil {
			log.Fatal(err)
		}
		_, _ = io.ReadAll(str)
	}()

	//客户端连接服务器
	fmt.Printf("Start Client Connect to %s!\n", "localhost:"+port)

	//在client的TLS配置中加入Session Ticket缓存,用于保存0-RTT 的Session Ticket
	puts := make(chan string, 100)
	cache := clientTLSConf.ClientSessionCache
	if cache == nil {
		cache = tls.NewLRUClientSessionCache(100)
	}
	clientTLSConf.ClientSessionCache = cltSessionCache.NewClientSessionCache(cache, make(chan string, 100), puts)

	//连接服务器
	var conn quic.EarlyConnection
	conn, err = quic.DialAddrEarly(
		context.Background(),
		fmt.Sprintf("localhost:%s", port),
		clientTLSConf,
		getQuicConfig(nil),
	)

	if err != nil {
		log.Fatal(err)
	}

	//等待握手完成
	<-conn.HandshakeComplete()
	fmt.Println("Handshake Complete! 0-RTT used: ", conn.ConnectionState().Used0RTT)

	//这里必须进行实际数据的传输,否则Client将不会保存Session Ticket。会话票据会在握手完成后传输给客户端
	str, _ := conn.OpenStream()
	testdata := GeneratePRData(500 * 1024)
	_, err = str.Write(testdata)

	if err != nil {
		log.Fatal(err)
	}

	conn.CloseWithError(0, "")
	ln.Close()
}
func main() {

	serverTLSConf := tlscfg.GetTLSServerConfig()
	clientTLSConf := tlscfg.GetTLSClientConfig()
	serverQuicConf := getQuicConfig(nil)
	clientQuicConf := getQuicConfig(nil)

	for _, l := range []int{0, 1, 2} {
		fmt.Printf("%d Connection!\n", l)
		Transfer0RttData(serverTLSConf, serverQuicConf, clientTLSConf, clientQuicConf)
	}

}

go.mod文件

module zerorttExpir

go 1.22.7

require github.com/quic-go/quic-go v0.47.0

require (
	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
	github.com/onsi/ginkgo/v2 v2.9.5 // indirect
	go.uber.org/mock v0.4.0 // indirect
	golang.org/x/crypto v0.26.0 // indirect
	golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
	golang.org/x/mod v0.17.0 // indirect
	golang.org/x/net v0.28.0 // indirect
	golang.org/x/sys v0.23.0 // indirect
	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
)

标签:tls,err,nil,x509,RTT,quic,go
From: https://blog.csdn.net/xingxinbg/article/details/142218631

相关文章

  • Django 聚合查询
    文章目录一、聚合查询二、使用步骤1.准备工作2.具体使用3.分组查询(annotate)1.定义2.使用3.具体案例4.F()查询1.定义2.使用5.Q()查询1.定义2.查询一、聚合查询使用聚合查询前要先从django.db.models引入Avg、Max、Min、Count、Sum(首字母大写)聚合查询返回值......
  • Study Plan For Algorithms - Part32
    1.不同路径II一个机器人位于一个mxn网格的左上角(起始点在下图中标记为“Start”)。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?网格中的障碍物......
  • Study Plan For Algorithms - Part30
    1.螺旋矩阵II给定一个正整数n,生成一个包含1到n2所有元素,且元素按顺时针顺序螺旋排列的nxn正方形矩阵matrix。classSolution:defgenerateMatrix(self,n:int)->List[List[int]]:matrix=[[0]*nfor_inrange(n)]num=1......
  • Study Plan For Algorithms - Part31
    1.旋转链表给定一个链表的头节点head,旋转链表,将链表每个节点向右移动k个位置。classListNode:def__init__(self,val=0,next=None):self.val=valself.next=nextclassSolution:defrotateRight(self,head:Optional[ListNode],k:int)->O......
  • 【开题报告】基于django+vue外卖订餐管理系统(论文+程序)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着互联网的飞速发展和生活节奏的加快,外卖订餐服务已成为现代都市生活中不可或缺的一部分。它不仅极大地便利了消费者的日常生活,也为餐饮......
  • django实现分页的模块(导入即可用)
    `fromdjango.utils.safestringimportmark_safeimportcopyclassPagerPlay(object):definit(self,request,customer_list):self.customer_list=customer_listself.request_GET=copy.deepcopy(request.GET)self.request_GET._mutable=Trueself.page=request......
  • 【开题报告】基于django+vue基于Web的小型社区配送系统(论文+源码) 计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着电子商务的蓬勃发展和消费者对即时配送服务需求的日益增长,小型社区配送系统逐渐成为连接商家与居民的重要桥梁。传统社区配送往往面临......
  • python+django+mysql 教师培训反馈系统05141-计算机毕业设计项目选题推荐(赠源码)
       目   录摘  要Abstract第1章  前  言1.1 研究背景1.2 研究现状1.3 系统开发目标第2章  系统开发环境62.1HTTP协议62.2HTML网页技术62.3B/S结构62.4django脚本语言72.5MySQL数据库72.6Apache简介8第3章  需求分析......
  • php和go进行跨进程fifo通信
    fifo的文件需要提前建立好,或者用程序处理php<?php$fifoName="my_fifo";//打开命名管道进行读取$fifo=fopen($fifoName,"r+");if($fifo===false){die("UnabletoopenFIFO:$fifoName");}//读取数据$data=fread($fifo,1024);echo"Re......
  • 2024-09-14:用go语言,给定一个正整数数组 nums,定义一个加密函数 encrypt(x),其将一个整数
    2024-09-14:用go语言,给定一个正整数数组nums,定义一个加密函数encrypt(x),其将一个整数x的每一位数字都替换为x中的最大数字,然后返回加密后的数字。例如,encrypt(523)会返回555,encrypt(213)会返回333。现在需要计算数组中所有元素加密后的和,然后返回这个和。输入:nums=[10,2......