使用 Go 实现 TLS socket server

女主宣言

安全传输层协议 TLS,以前称为 SSL(Secure Sockets Layer) ,由于HTTPS的推出受到了很多人的欢迎。但是正如TLS的名称 Transport Layer Security 所示的那样,它实际上是独立于 HTTP,一个更深入的安全协议,我们可以将 TLS 视为 TCP 的安全版本,其提供了对 socket 通信进行加密和签名的功能。在我们的日常开发中,会将 gRPC 协议运行在TLS之上以确保安全。

今天我们来了解一下如何创建一个通过 TLS 加密的 socket 服务,

PS:丰富的一线技术、多元化的表现形式,尽在“360云计算”,点关注哦!

1

TLS socket server

服务端示例

func main() {
  port := flag.String("port", "8360", "listening port")
  certFile := flag.String("cert", "cert.pem", "certificate PEM file")
  keyFile := flag.String("key", "key.pem", "key PEM file")
  flag.Parse()
  cert, err := tls.LoadX509KeyPair(*certFile, *keyFile)
  if err != nil {
    log.Fatal(err)
  }
  config := &tls.Config{Certificates: []tls.Certificate{cert}}
  log.Printf("listening on port %s\n", *port)
  l, err := tls.Listen("tcp", ":"+*port, config)
  if err != nil {
    log.Fatal(err)
  }
  defer l.Close()
  for {
    conn, err := l.Accept()
    if err != nil {
      log.Fatal(err)
    }
    log.Printf("accepted connection from %s\n", conn.RemoteAddr())
    go func(c net.Conn) {
      io.Copy(c, c)
      c.Close()
      log.Printf("closing connection from %s\n", conn.RemoteAddr())
    }(conn)
  }
}
go

这个服务端程序接受来自多个客户端并发请求,并向客户端发送的所有的镜像数据。和非TLS服务相比,这里用 tls.Listen 替换了 net.Listen,同时需要提供一个可用的 tls.Config,我们可以使用 mkcert 命令来生成证书和密钥对文件。

2

TLS socket client

客户端示例:

func main() {
  port := flag.String("port", "8360", "port to connect")
  certFile := flag.String("certfile", "cert.pem", "trusted CA certificate")
  flag.Parse()
  cert, err := os.ReadFile(*certFile)
  if err != nil {
    log.Fatal(err)
  }
  certPool := x509.NewCertPool()
  if ok := certPool.AppendCertsFromPEM(cert); !ok {
    log.Fatalf("unable to parse cert from %s", *certFile)
  }
  config := &tls.Config{RootCAs: certPool}
  conn, err := tls.Dial("tcp", "localhost:"+*port, config)
  if err != nil {
    log.Fatal(err)
  }
  _, err = io.WriteString(conn, "Hello simple secure Server\n")
  if err != nil {
    log.Fatal("client write error:", err)
  }
  if err = conn.CloseWrite(); err != nil {
    log.Fatal(err)
  }
  buf := make([]byte, 256)
  n, err := conn.Read(buf)
  if err != nil && err != io.EOF {
    log.Fatal(err)
  }
  fmt.Println("client read:", string(buf[:n]))
  conn.Close()
}
go

和非 TLS 客户端相比,我们同样也只是把 net.Dial 换成 tls.Dial, tls.Config 中填写的证书可以选择权威 ca 颁发的证书,也可以使用自签名证书。

3

证书链

一般来说,我们将自己生成的 CSR 提交给签名商,他们用中级证书机构的私钥 Private Key 给我们的签名成证书,Root CA 通过它的私钥对中级机构提交的CSR进行签名。

证书颁发机构是一个树形结构的。比如在验证我们证书X的有效性的时候,会一层层的去寻找颁发者的证书,直到自签名的根证书,然后通过相应的公钥再反过来验证下一级的数字签名的正确性。直到找到X证书,这就是证书链(Certificate Chains)。

我们可以使用以下程序检查任何服务器的证书链:

func main() {
addr := flag.String("addr", "localhost:8360", "dial address")
flag.Parse()
cfg := tls.Config{}
conn, err := tls.Dial("tcp", *addr, &cfg)
if err != nil {
log.Fatal("TLS connection failed: " + err.Error())
}
defer conn.Close()
certChain := conn.ConnectionState().PeerCertificates
for i, cert := range certChain {
fmt.Println(i)
fmt.Println("Issuer:", cert.Issuer)
fmt.Println("Subject:", cert.Subject)
fmt.Println("Version:", cert.Version)
fmt.Println("NotAfter:", cert.NotAfter)
fmt.Println("DNS names:", cert.DNSNames)
fmt.Println("")
}
}
go

给定IP地址后,启动程序后会与服务器建立一条 TLS 连接,并上报其使用的证书给服务端。如果我们使用未处理过的自签的证书,TLS 服务端验证是通不过的。所以我们需要权威ca 颁发的证书,或者使用 mkcert 为我们的服务器生成证书来使他生效。

打开终端,执行 mkcert 命令:

➜  kangkai-iri ./mkcert localhost
➜  kangkai-iri go run tls-socket-server.go -cert localhost.pem -key localhost-key.pem

新打开一个终端,运行 tls-dial-port:

➜  kangkai-iri go run tls-dial-port.go -addr localhost:4040

我们看到生成了证书 mkcert。由于 mkcert 将此证书添加到服务器的系统根存储中,直接使用 tls.Dial 将信任该证书。

360云计算

由360云平台团队打造的技术分享公众号,内容涉及数据库、大数据、微服务、容器、AIOps、IoT等众多技术领域,通过夯实的技术积累和丰富的一线实战经验,为你带来最有料的技术分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值