You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言HTTP连接配置TLS-PSK遇握手失败问题求助

Go语言实现TLS-PSK HTTP连接遇到 cipher suite 不兼容问题

我正在学习用Go语言实现带有TLS-PSK的HTTP连接,使用了tls-psk和tls-ext两个包,按照tls-psk的示例配置操作,但始终无法成功运行。

服务端代码

...

type M map[string]interface{}

func main() {
    mux := new(http.ServeMux)
    mux.HandleFunc("/data", ActionData)

    s := &http.Server{
        Addr:    ":9080",
        Handler: mux,
    }

    cfg := &tls.Config{
        CipherSuites: []uint16{psk.TLS_DHE_PSK_WITH_AES_128_CBC_SHA, psk.TLS_DHE_PSK_WITH_AES_256_CBC_SHA},
        Certificates: []tls.Certificate{{}},
        MaxVersion:   tls.VersionTLS11,
        MinVersion:   tls.VersionTLS13,
        Extra: psk.PSKConfig{
            GetKey: func(identity string) ([]byte, error) {
                hexString := "166ACC41EC1D4E1DD001ECC130ED0810"
                key := make([]byte, hex.DecodedLen(len(hexString)))
                _, err := hex.Decode(key, []byte(hexString))
                if err != nil {
                    return nil, err
                }
                return key, nil
            },
        },
    }

    l, err := net.Listen("tcp", ":9080")

    if err != nil {
        log.Fatalln("Failed to start web server", err)
    }

    l = tls.NewListener(l, cfg)

    log.Println("Starting server at 9080")
    if err := s.Serve(l); err != nil {
        log.Fatalln("error starting server:", err)
    }

}

func ActionData(w http.ResponseWriter, r *http.Request) {
    log.Println("Incoming request with method", r.Method)

    if r.Method != "POST" {
        http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
        return
    }

    payload := make(M)
    err := json.NewDecoder(r.Body).Decode(&payload)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    if _, ok := payload["Name"]; !ok {
        http.Error(w, "Payload `Name` is required", http.StatusBadRequest)
        return
    }

    data := M{
        "Message": fmt.Sprintf("Hello %s", payload["Name"]),
        "Status":  true,
    }

    w.Header().Set("Content-Type", "application/json")
    err = json.NewEncoder(w).Encode(data)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

客户端代码

...
type M map[string]interface{}

var (
    Config = &tls.Config{
        // ServerName: "client-PSK-test",
        PreferServerCipherSuites: true,
        InsecureSkipVerify:       true,
        CipherSuites:             []uint16{psk.TLS_PSK_WITH_AES_128_CBC_SHA},
        Certificates:             []tls.Certificate{{}},
        Extra: psk.PSKConfig{
            GetIdentity: func() string {
                return "123"
            },
            GetKey: func(identity string) ([]byte, error) {
                hexString := "166ACC41EC1D4E1DD001ECC130ED0810"
                key := make([]byte, hex.DecodedLen(len(hexString)))
                _, err := hex.Decode(key, []byte(hexString))
                if err != nil {
                    return nil, err
                }
                return key, nil
            },
        },
    }
)

func main() {
    baseURL := "http://localhost:9080"
    data := M{"Name": "Noval Agung"}

    responseBody, err := doRequest(baseURL+"/data", "POST", data)
    if err != nil {
        log.Println("ERROR: do request:", err.Error())
        return
    }

    log.Printf("%#v \n", responseBody)
}

func doRequest(url, method string, data interface{}) (interface{}, error) {
    var payload *bytes.Buffer = nil

    if data != nil {
        payload = new(bytes.Buffer)
        err := json.NewEncoder(payload).Encode(data)
        if err != nil {
            return nil, fmt.Errorf("error json encoding: %v", err)
        }
    }

    request, err := http.NewRequest(method, url, payload)
    if err != nil {
        return nil, fmt.Errorf("error creating new request: %v", err)
    }
    request.Header.Set("Content-Type", "application/json")

    client := &http.Client{

        Transport: &http.Transport{
            DialTLS: func(network, addr string) (net.Conn, error) {
                return tls.Dial(network, addr, Config)
            },
        },
    }

    response, err := client.Do(request)
    if err != nil {
        return nil, fmt.Errorf("error client.Do(): %v", err)
    }
    if response != nil {
        defer response.Body.Close()
    }

    responseBody := make(M)
    err = json.NewDecoder(response.Body).Decode(&responseBody)
    if err != nil {
        return nil, err
    }

    return responseBody, nil
}

已尝试的解决措施

  • 使用http客户端时,客户端侧出现EOF错误
  • 使用https客户端并调整TLS版本时,出现remote error: tls: handshake failure错误
  • 使用TLS 1.3版本时,出现remote error: tls: internal error错误

更新:错误定位

通过tls-ext包日志发现核心错误为:no cipher suite supported by both client and server,请求解决该问题。


解决方案

  1. 统一Cipher Suites:服务端配置的是DHE-PSK类套件,客户端用的是普通PSK套件,两者无交集。需要调整为双方都支持的套件:
    示例:服务端CipherSuites改为:

    CipherSuites: []uint16{psk.TLS_DHE_PSK_WITH_AES_128_CBC_SHA, psk.TLS_DHE_PSK_WITH_AES_256_CBC_SHA, psk.TLS_PSK_WITH_AES_128_CBC_SHA},
    

    客户端保持现有配置即可,或者也添加DHE-PSK套件。

  2. 修正TLS版本范围:服务端配置的MaxVersion: tls.VersionTLS11和MinVersion: tls.VersionTLS13矛盾,MinVersion不能大于MaxVersion。调整为合理范围:

    MaxVersion: tls.VersionTLS12,
    MinVersion: tls.VersionTLS10,
    

    注意TLS 1.3不支持PSK-CBC类套件,不要将MinVersion设为TLS13。

  3. 客户端使用HTTPS协议:客户端的baseURL要改为https://localhost:9080,否则DialTLS不会生效,普通HTTP连接到TLS监听端口会导致EOF错误。

  4. 移除无用证书配置:TLS-PSK不需要证书,服务端和客户端的Certificates: []tls.Certificate{{}}可以删除,避免干扰。

调整后的关键配置示例:

  • 服务端tls.Config:
cfg := &tls.Config{
    CipherSuites: []uint16{psk.TLS_DHE_PSK_WITH_AES_128_CBC_SHA, psk.TLS_PSK_WITH_AES_128_CBC_SHA},
    MaxVersion:   tls.VersionTLS12,
    MinVersion:   tls.VersionTLS10,
    Extra: psk.PSKConfig{
        GetKey: func(identity string) ([]byte, error) {
            hexString := "166ACC41EC1D4E1DD001ECC130ED0810"
            key := make([]byte, hex.DecodedLen(len(hexString)))
            _, err := hex.Decode(key, []byte(hexString))
            if err != nil {
                return nil, err
            }
            return key, nil
        },
    },
}
  • 客户端tls.Config:
Config = &tls.Config{
    PreferServerCipherSuites: true,
    InsecureSkipVerify:       true,
    CipherSuites:             []uint16{psk.TLS_PSK_WITH_AES_128_CBC_SHA, psk.TLS_DHE_PSK_WITH_AES_128_CBC_SHA},
    Extra: psk.PSKConfig{
        GetIdentity: func() string {
            return "123"
        },
        GetKey: func(identity string) ([]byte, error) {
            hexString := "166ACC41EC1D4E1DD001ECC130ED0810"
            key := make([]byte, hex.DecodedLen(len(hexString)))
            _, err := hex.Decode(key, []byte(hexString))
            if err != nil {
                return nil, err
            }
            return key, nil
        },
    },
}
  • 客户端baseURL改为:
baseURL := "https://localhost:9080"

内容的提问来源于stack exchange,提问作者ansuf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 05:55:09