确保 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.key
和ca.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.key
、server.csr
和server.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.key
、client.csr
和client.crt
文件
签名方式:SHA-256,默认的 SHA-1 签名算法安全性不够高,Go 中会出现警告。
编写 Server 端
- 创建
cmd
目录,在cmd
文件夹中,创建server
文件夹,在server
文件夹中创建main.go
文件。
代码中需要加载config
文件夹中的ca.crt
根证书以及生成的服务端证书server.crt
、server.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.crt
、client.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
截图如下:
网友评论