Golang中如何获取TLS请求的域名
Golang中如何获取TLS请求的域名
func main() {
fmt.Println("hello world")
}
在使用自签名证书的TLS Go服务器监听器时,如何获取域名?如果浏览器显示证书通知且未添加例外,只有一条请求会发送到服务器。域名信息存在于net.Conn中,虽然可以通过fmt.Println(conn)查看,但如何将该名称以字符串形式存入变量?
6 回复
首先,在生成自签名证书时,请务必注意完全限定域名(FQDN)并正确填写域名。其次,不要使用Chrome处理自签名证书。Chrome进行了一些更改,处理此类证书较为困难。建议改用Firefox。如果握手成功,您可以进行日志记录或其他操作。
您可以挂接到 TLS 配置的 GetCertificate 方法:
tlsCfg := &tls.Config{
GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
fmt.Println("client requested", hello.ServerName)
return nil, errors.New("no certificate for you")
},
...
在您拥有与请求名称匹配的证书的情况下,您还需要实际提供该证书。如果只有一个证书,您可以始终提供该证书。
问题不在于创建TLS服务器,而在于当服务器运行时获取请求的域名。如果使用自签名证书,用户无法接受,这种情况下只会有一个请求到达服务器。我想知道请求使用的是哪个域名。
示例:
- 客户端在浏览器中打开地址 https://somestrange.com
- 服务器接收请求并返回证书(握手)
- 在日志文件中写入请求的域名 somestrange.com
- 客户端未将证书添加到例外并关闭浏览器
我需要实现第3步。
这是"网络TLS连接自签名证书"在谷歌上的第一个搜索结果,其中包含一些可能对你有用的示例。
https://gist.github.com/denji/12b3a568f092ab951456
golang-tls.md
### 已移至Git仓库:https://github.com/denji/golang-tls
##### 生成私钥(.key)
```sh
# RSA算法密钥考虑因素 ≥ 2048位
openssl genrsa -out server.key 2048
# ECDSA算法密钥考虑因素 ≥ secp384r1
# 列出支持的ECDSA曲线(openssl ecparam -list_curves)
该文件已被截断。显示原文
你需要将自签名证书添加到连接来源计算机的证书存储中。
在Go语言中,可以通过类型断言从net.Conn中获取TLS连接信息,然后访问ConnectionState来提取客户端请求的域名。以下是具体实现方法:
package main
import (
"crypto/tls"
"fmt"
"log"
"net"
"net/http"
)
func main() {
// 创建自签名证书的TLS配置
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
}
// 创建TLS监听器
listener, err := tls.Listen("tcp", ":8443", config)
if err != nil {
log.Fatal(err)
}
defer listener.Close()
fmt.Println("TLS服务器监听在 :8443")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 从TLS连接中获取域名
if tlsConn, ok := r.TLS; ok {
serverName := tlsConn.ServerName
fmt.Fprintf(w, "请求的域名: %s\n", serverName)
// 将域名存入变量
domain := serverName
fmt.Printf("获取到的域名已存入变量: %s\n", domain)
} else {
fmt.Fprintf(w, "非TLS连接\n")
}
})
log.Fatal(http.Serve(listener, nil))
}
对于原始套接字级别的处理,可以使用以下方法:
package main
import (
"crypto/tls"
"fmt"
"log"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close()
// 类型断言为TLS连接
if tlsConn, ok := conn.(*tls.Conn); ok {
// 完成TLS握手
err := tlsConn.Handshake()
if err != nil {
log.Printf("TLS握手失败: %v", err)
return
}
// 获取连接状态
state := tlsConn.ConnectionState()
serverName := state.ServerName
// 将域名存入变量
domain := serverName
fmt.Printf("客户端请求的域名: %s\n", domain)
fmt.Printf("域名已存入变量: %s\n", domain)
// 其他处理逻辑...
} else {
fmt.Println("非TLS连接")
}
}
func main() {
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
}
listener, err := tls.Listen("tcp", ":8443", config)
if err != nil {
log.Fatal(err)
}
defer listener.Close()
fmt.Println("TLS服务器监听在 :8443")
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("接受连接失败: %v", err)
continue
}
go handleConnection(conn)
}
}
关键点说明:
- 使用类型断言
conn.(*tls.Conn)将普通连接转换为TLS连接 - 调用
Handshake()方法完成TLS握手 - 通过
ConnectionState().ServerName获取客户端请求的域名 - 在HTTP处理中,可以直接通过
r.TLS.ServerName获取域名
这样就能将TLS请求中的域名信息提取并存储到字符串变量中。

