前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 GoLang 获取 TLS 的 Client Hello Info

使用 GoLang 获取 TLS 的 Client Hello Info

作者头像
饶文津
发布2020-07-14 10:44:40
2.9K0
发布2020-07-14 10:44:40
举报
文章被收录于专栏:饶文津的专栏饶文津的专栏

TLS 介绍

TLS(Transport Layer Security)是一个保证信息安全的应用层协议。它的前身是 SSL(Secure Socket Layer)。它是一套定义了如何对由 TCP 传输的报文进行加密的协议。

HTTP 协议传输报文时,数据是明文传递的,意味着你和服务器之间的通信是可以被别人截获、监听、篡改的。所以没有安全性。因此就有了 SSL,后来发展为了 TLS。我们平时使用的 HTTPS 其实就是 HTTP+SSL/TCP 的简称。

TLS 握手过程

简而言之,服务器和客户端通过 TLS 协议进行沟通时,客户端发给服务器一个随机数,然后双方用这个随机数生成一个密钥,之后就用它对报文做对称加密。为了防止随机数被窃听,它俩会先互相 hello,传递版本号、支持的加密方法等,然后服务器给客户端自己的证书(RSA 加密算法里的公钥),客户端用服务器的证书加密自己的证书及随机数,发给服务器。

用 GoLang 获取 TLS 的 Client Hello 报文

下面我们实现一个可以获取所有 ClientHello 报文信息的服务器。

证书生成
代码语言:javascript
复制
# 生成私钥
openssl genrsa -out server.key 2048
# 生成公钥(证书)
openssl req -new -x509 -key server.key -out server.pem -days 3650
使用 crypto/tls

GoLang 中的 crypto/tls 库实现了 TLS 协议。因此只要参考它的文档就可以实现客户端和服务器。

服务器:

代码语言:javascript
复制
func handler(conn net.Conn) {
	defer conn.Close()
	r := bufio.NewReader(conn)
	for {
		msg, err := r.ReadString('\n')
		if err != nil {
			log.Println(err)
			return
		}
		fmt.Println(msg)
		_, err = conn.Write([]byte("world\n"))
		if err != nil {
			log.Println(err)
			return
		}
	}
}

func main() {
	cert, err := tls.LoadX509KeyPair("server.pem", "server.key")
	if err != nil {
		log.Fatal(err)
		return
	}
	ln, err := tls.Listen("tcp", ":443", &tls.Config{
		Certificates: []tls.Certificate{cert},
	})
	if err != nil {
		log.Println(err)
		return
	}
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println(err)
			continue
		}
		go handler(conn)
	}
}

客户端:

代码语言:javascript
复制
func main() {
	conn, err := tls.Dial("tcp", "localhost:443", &tls.Config{InsecureSkipVerify: true})
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	_, err = conn.Write([]byte("hello\n"))
	if err != nil {
		log.Fatal(err)
	}
	buf := make([]byte, 1000)
	n, err := conn.Read(buf)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(buf[:n]))
}

通过 GetConfigForClient 回调函数获取 ClientHelloInfo

tls.Config 中有个 GetConfigForClient 属性,通过它我们可以拿到 ClientHelloInfo,然后可以存在本地,比如说定期导出到 json 文件里。

下面是完整的服务器的例子:

代码语言:javascript
复制
package main

import (
	"bufio"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"sync"
	"time"
)

type CollectInfos struct {
	ClientHellos []*tls.ClientHelloInfo
	sync.Mutex
}

var collectInfos CollectInfos
var currentClientHello *tls.ClientHelloInfo

func (c *CollectInfos) collectClientHello(clientHello *tls.ClientHelloInfo) {
	c.Lock()
	defer c.Unlock()
	c.ClientHellos = append(c.ClientHellos, clientHello)
}

func (c *CollectInfos) DumpInfo() {
	c.Lock()
	defer c.Unlock()
	data, err := json.Marshal(c.ClientHellos)
	if err != nil {
		log.Fatal(err)
	}
	ioutil.WriteFile("hello.json", data, os.ModePerm)
}

func getCert() *tls.Certificate {
	cert, err := tls.LoadX509KeyPair("server.pem", "server.key")
	if err != nil {
		log.Println(err)
		return nil
	}
	return &cert
}

func buildTlsConfig(cert *tls.Certificate) *tls.Config {
	cfg := &tls.Config{
		Certificates: []tls.Certificate{*cert},
		GetConfigForClient: func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) {
			collectInfos.collectClientHello(clientHello)
			currentClientHello = clientHello
			return nil, nil
		},
	}
	return cfg
}

func serve(cfg *tls.Config) {
	ln, err := tls.Listen("tcp", ":443", cfg)
	if err != nil {
		log.Println(err)
		return
	}
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println(err)
			continue
		}
		go handler(conn)
	}
}

func handler(conn net.Conn) {
	defer conn.Close()
	r := bufio.NewReader(conn)
	for {
		msg, err := r.ReadString('\n')
		if err != nil {
			log.Println(err)
			return
		}
		fmt.Println(msg)
		data, err := json.Marshal(currentClientHello)
		if err != nil {
			log.Fatal(err)
		}
		_, err = conn.Write(data)
		if err != nil {
			log.Println(err)
			return
		}
	}
}

func main() {
	go func() {
		for {
			collectInfos.DumpInfo()
			time.Sleep(10 * time.Second)
		}
	}()
	cert := getCert()
	if cert != nil {
		serve(buildTlsConfig(cert))
	}
}

参考

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-07-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • TLS 介绍
  • TLS 握手过程
  • 用 GoLang 获取 TLS 的 Client Hello 报文
    • 证书生成
      • 使用 crypto/tls 库
      • 通过 GetConfigForClient 回调函数获取 ClientHelloInfo
      • 参考
      相关产品与服务
      SSL 证书
      腾讯云 SSL 证书(SSL Certificates)为您提供 SSL 证书的申请、管理、部署等服务,为您提供一站式 HTTPS 解决方案。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档