Golang中mTLS双向认证配置咨询(含SmallStep证书需求)
问题描述
我希望在Golang中使用mTLS实现一台服务器与多个客户端的连接,需求如下:
- 服务器作为唯一证书生成源,负责生成客户端证书
- 客户端仅能与服务器通信,彼此无法互联
- 客户端在80端口暴露Golang HTTP服务,由服务器主动发起API请求调用客户端(仍称此节点为服务器,因它是证书唯一生成源并向客户端提供内容)
已尝试用SmallStep生成证书,但配置存在错误,当前未使用ca.crt,现咨询:该场景应如何配置?需生成哪些证书?能否让客户端使用服务器生成的证书监听请求?
解决方案
一、证书生成(SmallStep 实现)
1. 准备CA根证书
首先确保已初始化SmallStep CA(未初始化先执行step ca init),然后导出CA根证书——这是所有节点信任链的核心,必须在服务器和客户端都配置:
step ca root ca.crt
2. 生成服务器证书
服务器证书的标识要和实际访问地址匹配,保证客户端能识别:
step ca certificate "pm1" server.crt server.key
这里的pm1是服务器的唯一标识(域名/IP均可)。
3. 生成客户端证书
每个客户端需要唯一证书,且限制用途为客户端认证(避免客户端用此证书搭建服务端),同时添加唯一标识方便服务器验证:
step ca certificate --san "client-01" --profile "client" client-01.crt client-01.key
--profile "client":指定证书仅用于客户端认证,默认配置可能包含服务端用途,这样能防止客户端用该证书监听服务--san "client-01":给客户端添加唯一标识,后续可用于身份校验
二、Golang代码配置
1. 服务器端(主动调用客户端HTTP服务)
服务器需要用自身证书作为客户端发起请求,同时验证客户端证书的合法性:
package main import ( "crypto/tls" "crypto/x509" "log" "net/http" "os" ) func main() { // 加载CA根证书 caCert, err := os.ReadFile("ca.crt") if err != nil { log.Fatalf("读取CA证书失败: %v", err) } caCertPool := x509.NewCertPool() if !caCertPool.AppendCertsFromPEM(caCert) { log.Fatal("添加CA证书到信任池失败") } // 加载服务器自身证书(发起请求时作为客户端身份凭证) serverCert, err := tls.LoadX509KeyPair("server.crt", "server.key") if err != nil { log.Fatalf("加载服务器证书失败: %v", err) } // 配置HTTP Transport,用于发起mTLS请求 transport := &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: caCertPool, // 信任CA根证书,验证客户端证书合法性 Certificates: []tls.Certificate{serverCert}, // 服务器自身证书,供客户端验证身份 MinVersion: tls.VersionTLS13, }, } // 创建HTTP客户端 client := &http.Client{Transport: transport} // 调用客户端API示例 resp, err := client.Get("https://client-01:80/api/endpoint") if err != nil { log.Fatalf("调用客户端服务失败: %v", err) } defer resp.Body.Close() // 处理响应逻辑... }
2. 客户端(监听HTTP服务,仅接受服务器请求)
客户端用自身证书作为服务端监听,同时验证服务器身份,确保只有指定服务器能访问:
package main import ( "crypto/tls" "crypto/x509" "log" "net/http" "os" ) func main() { // 加载CA根证书 caCert, err := os.ReadFile("ca.crt") if err != nil { log.Fatalf("读取CA证书失败: %v", err) } caCertPool := x509.NewCertPool() if !caCertPool.AppendCertsFromPEM(caCert) { log.Fatal("添加CA证书到信任池失败") } // 加载客户端自身证书(作为服务端监听时的身份凭证) clientCert, err := tls.LoadX509KeyPair("client-01.crt", "client-01.key") if err != nil { log.Fatalf("加载客户端证书失败: %v", err) } // 配置TLS,验证服务器身份并限制访问 tlsConfig := &tls.Config{ ClientCAs: caCertPool, // 信任CA根证书,验证服务器证书合法性 ClientAuth: tls.RequireAndVerifyClientCert, // 要求发起请求的一方(即服务器)提供证书 Certificates: []tls.Certificate{clientCert}, // 客户端自身证书,供服务器验证身份 MinVersion: tls.VersionTLS13, // 验证服务器身份,确保只有指定服务器能访问 VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { cert, err := x509.ParseCertificate(rawCerts[0]) if err != nil { return err } // 检查服务器证书的标识是否为指定的"pm1" if cert.Subject.CommonName != "pm1" { return log.Fatalf("未授权的服务器: %s", cert.Subject.CommonName) } return nil }, } // 创建HTTP服务器,监听80端口 server := &http.Server{ Addr: ":80", TLSConfig: tlsConfig, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("来自客户端服务的响应!")) }), } // 启动HTTPS服务器 log.Println("客户端服务启动于 :80") if err := server.ListenAndServeTLS("", ""); err != nil { log.Fatalf("服务启动失败: %v", err) } }
三、关键说明
- 必须使用CA根证书:之前的核心错误是未加载
ca.crt,导致双方无法验证对方证书的合法性,根证书是信任链的基础。 - 客户端证书用途限制:通过
--profile "client"生成的证书仅能用于客户端认证,无法作为服务端证书使用,从根源避免客户端滥用证书搭建服务。 - 客户端监听的安全性:客户端可以使用服务器生成的证书监听请求,但需通过
VerifyPeerCertificate校验服务器身份,确保只有指定服务器能调用客户端API,实现客户端之间无法互联的需求。
内容的提问来源于stack exchange,提问作者Maggie
相关产品推荐
相关产品推荐

