Go 实现 TLS 双向认证

确保 openssl 正常安装

openssl version
LibreSSL 2.8.3

生成CA 根证书

# 生成根证书私钥
openssl genrsa -out ./config/ca.key 2048
# 生成根证书请求文件
openssl req -new -x509 -days 3650 -key ./config/ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ./config/ca.crt

将会在 config 文件夹中生成ca.keyca.crt文件

签发服务端证书

openssl req -newkey rsa:2048 -sha256 -nodes -keyout ./config/server.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ./config/server.csr
openssl x509 -req  -sha256 -extfile <(printf "subjectAltName=DNS:example.com,DNS:localhost,IP:127.0.0.1") -days 3650  -in ./config/server.csr   -CA ./config/ca.crt -CAkey ./config/ca.key -CAcreateserial -out ./config/server.crt

将会在 config文件夹中生成server.keyserver.csrserver.crt文件
签名方式:SHA-256,默认的 SHA-1 签名算法安全性不够高,Go 中会出现警告。

签发客户端证书

openssl req -newkey rsa:2048 -sha256 -nodes -keyout ./config/client.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ./config/client.csr
openssl x509 -req  -sha256 -extfile <(printf "subjectAltName=DNS:example.com,DNS:localhost,IP:127.0.0.1") -days 3650  -in ./config/client.csr   -CA ./config/ca.crt -CAkey ./config/ca.key -CAcreateserial -out ./config/client.crt

将会在 config 文件夹中生成client.keyclient.csrclient.crt文件
签名方式:SHA-256,默认的 SHA-1 签名算法安全性不够高,Go 中会出现警告。

编写 Server 端

  • 创建cmd 目录,在cmd文件夹中,创建server文件夹,在server文件夹中创建main.go 文件。
    代码中需要加载 config文件夹中的ca.crt根证书以及生成的服务端证书server.crtserver.key
package main

import (
    "bufio"
    "crypto/tls"
    "crypto/x509"
    "io"
    "io/ioutil"
    "log"
    "net"
)

var (
    ServerAddr = "127.0.0.1:9090"
    rootCrt    = "./config/ca.crt"
)

func main() {
    startTCPServerWithTLS(ServerAddr, "./config/server.crt", "./config/server.key")
}

func startTCPServerWithTLS(addr string, certFile string, certKey string) {
    pem, err := ioutil.ReadFile(rootCrt)
    if err != nil {
        log.Fatalln(err)
    }
    pool := x509.NewCertPool()
    if !pool.AppendCertsFromPEM(pem) {
        return
    }
    cert, err := tls.LoadX509KeyPair(certFile, certKey)
    if err != nil {
        log.Fatalln(err)
    }
    tlsConfig := &tls.Config{
        RootCAs:      pool,
        ClientCAs:    pool,
        ClientAuth:   tls.RequireAndVerifyClientCert,
        MinVersion:   tls.VersionTLS12,
        Certificates: []tls.Certificate{cert},
    }
    listener, err := tls.Listen("tcp", addr, tlsConfig)
    if err != nil {
        log.Fatalln(err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Printf("received err:%s", err.Error())
            continue
        }
        log.Printf("handle a new conn:%s", conn.RemoteAddr().String())
        go handleConn(conn)
    }
}

func handleConn(conn net.Conn) {
    w := bufio.NewWriter(conn)
    for {
        buf := make([]byte, 1024)
        n, err := conn.Read(buf)
        if err != nil {
            if err == io.EOF {
                log.Printf("connnect %s is disconnected", conn.RemoteAddr())
                return
            }
            log.Printf("err:%s", err.Error())
            return
        }
        log.Printf("received data from client:%s", buf[:n])

        if _, err := w.Write(buf[:n]); err != nil {
            log.Printf("write data err:%s", err.Error())
        }
        w.Flush()
    }
}

编写 Client 端

  • cmd文件夹中,创建client文件夹,在client文件夹中创建main.go 文件。
    代码中需要加载 config文件夹中的ca.crt根证书以及生成的服务端证书client.crtclient.key
package main

import (
    "bufio"
    "crypto/tls"
    "crypto/x509"
    "io"
    "io/ioutil"
    "log"
    "net"
)

var (
    ServerAddr = "127.0.0.1:9090"
    rootCrt    = "./config/ca.crt"
)

func main() {
    startTCPServerWithTLS(ServerAddr, "./config/server.crt", "./config/server.key")
}

func startTCPServerWithTLS(addr string, certFile string, certKey string) {
    pem, err := ioutil.ReadFile(rootCrt)
    if err != nil {
        log.Fatalln(err)
    }
    pool := x509.NewCertPool()
    if !pool.AppendCertsFromPEM(pem) {
        return
    }
    cert, err := tls.LoadX509KeyPair(certFile, certKey)
    if err != nil {
        log.Fatalln(err)
    }
    tlsConfig := &tls.Config{
        RootCAs:      pool,
        ClientCAs:    pool,
        ClientAuth:   tls.RequireAndVerifyClientCert,
        MinVersion:   tls.VersionTLS12,
        Certificates: []tls.Certificate{cert},
    }
    listener, err := tls.Listen("tcp", addr, tlsConfig)
    if err != nil {
        log.Fatalln(err)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Printf("received err:%s", err.Error())
            continue
        }
        log.Printf("handle a new conn:%s", conn.RemoteAddr().String())
        go handleConn(conn)
    }
}

func handleConn(conn net.Conn) {
    w := bufio.NewWriter(conn)
    for {
        buf := make([]byte, 1024)
        n, err := conn.Read(buf)
        if err != nil {
            if err == io.EOF {
                log.Printf("connnect %s is disconnected", conn.RemoteAddr())
                return
            }
            log.Printf("err:%s", err.Error())
            return
        }
        log.Printf("received data from client:%s", buf[:n])

        if _, err := w.Write(buf[:n]); err != nil {
            log.Printf("write data err:%s", err.Error())
        }
        w.Flush()
    }
}

运行

  • 启动 Server 端
go run cmd/server/main.go
  • 启动 Client 端
go run cmd/client/main.go

wireshark 截图如下:

wireshark.png

你可能感兴趣的:(Go 实现 TLS 双向认证)