Golang中客户端TLS认证遇到的问题

Golang中客户端TLS认证遇到的问题 我的问题是,在客户端首次向服务器发送请求后,服务器要求客户端提供证书,但客户端在响应中没有返回证书。我也用NodeJS测试过证书是否正常,在NodeJS中是可以工作的。我在几个论坛上询问过这个问题,但没有人真正知道如何解决这个问题 😔

我的代码:

import (
    "bytes"
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {

    xml := `
    <Packet>
<Header>
 <ID>1231232132132354234</ID>
 </Header>
 <Body>
 <Item>
 <Content>SMS</Content>
 <Phone_no>+123123213</Phone_no>
 <Content>test</Content>
 <OriginAddress>Posiljatelj</OriginAddress>
 </Item>
 </Body>
</Packet>
    `

    // Load client cert
    cert, err := tls.LoadX509KeyPair("../cert/certNEW.pem", "../cert/serverNEW.key")
    if err != nil {
        log.Fatal(err)
    }

    // Load CA cert
    caCert, err := ioutil.ReadFile("../cert/cacerts.cer")
    if err != nil {
        log.Fatal(err)
    }
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)

    // Setup HTTPS client
    tlsConfig := &tls.Config{
        Certificates:       []tls.Certificate{cert},
        RootCAs:            caCertPool,
        InsecureSkipVerify: true,
    }

    tlsConfig.BuildNameToCertificate()
    transport := &http.Transport{TLSClientConfig: tlsConfig}
    client := &http.Client{Transport: transport}

    resp, err := client.Post("https://api.url", "text/xml", bytes.NewBuffer([]byte(xml)))

    if err != nil {
        fmt.Println(err)
    }
    contents, err := ioutil.ReadAll(resp.Body)
    fmt.Printf("%s\n", string(contents))
}

rcKas


更多关于Golang中客户端TLS认证遇到的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

11 回复

非常感谢!现在可以正常工作了

更多关于Golang中客户端TLS认证遇到的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我不需要在信任列表中包含根证书,因为根证书已经存在于信任列表中。但是,如果其他编程语言可以正常工作,那么这里应该也能正常工作。

那我就不是很确定了。你做的比我通常做的要多(设置CA池之类的),但除此之外看起来应该能正常工作。从你的文件名来看,密钥可能与证书不匹配,但如果是这种情况,它应该无法加载。

是的,我尝试过这个方法,如果密钥和公钥不匹配,Go 程序在启动时就会报错。
但我认为证书本身应该是没问题的,因为在 NodeJs 中使用相同的证书是可以正常工作的。

大致如下:

package main

import (
    "fmt"
)

func main() {
    fmt.Println("hello world")
}

代码示例链接

我猜测可以重写该函数并从中返回你的证书。如果这个方法可行,但当证书不满足约束条件时,我就不知道该如何处理了。

calmh:

猜测重写该函数并从中返回您的证书。如果这种方法有效,当证书不满足约束条件时,我就不知道该如何处理了。

@calmh 这就是问题所在。该如何使用这个函数来实现呢 🙂 谢谢

calmh:

onfig.BuildNameToCertificate.

你好 @calmh

我已经尝试过这个方法了,很抱歉没有在问题中说明这一点

//tlsConfig.BuildNameToCertificate()

但结果还是一样的 🙁

当你在邮件列表中提问时,我给出了这个建议:

尝试不使用 Config.BuildNameToCertificate。

你没有说明是否尝试过。使用该方法会使 TLS 包根据证书的通用名称来响应证书请求。在客户端场景中,这很可能无法满足你的需求。很可能你只是想发送列表中的第一个证书。

func main() {
    fmt.Println("hello world")
}

hey。

能否请教你关于如何使用 tls.Config.GetClientCertificate 的问题?它在 TLS 配置中定义为 GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)

我在 Git 上得到了很好的反馈,但不知道该如何定义这个😊: “我认为这里的情况是证书请求施加了某些限制(RSA 与 ECDSA,或特定颁发机构),而你的证书未能满足这些要求。”

你可以通过使用 tls.Config.GetClientCertificate 来绕过默认逻辑

在Go中实现客户端TLS证书认证时,常见问题是证书配置不正确或TLS握手参数缺失。以下是针对您代码的修改方案:

import (
    "bytes"
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {
    xml := `
    <Packet>
<Header>
 <ID>1231232132132354234</ID>
 </Header>
 <Body>
 <Item>
 <Content>SMS</Content>
 <Phone_no>+123123213</Phone_no>
 <Content>test</Content>
 <OriginAddress>Posiljatelj</OriginAddress>
 </Item>
 </Body>
</Packet>
    `

    // 加载客户端证书
    cert, err := tls.LoadX509KeyPair("../cert/certNEW.pem", "../cert/serverNEW.key")
    if err != nil {
        log.Fatal("加载客户端证书失败:", err)
    }

    // 加载CA证书
    caCert, err := ioutil.ReadFile("../cert/cacerts.cer")
    if err != nil {
        log.Fatal("加载CA证书失败:", err)
    }
    caCertPool := x509.NewCertPool()
    if !caCertPool.AppendCertsFromPEM(caCert) {
        log.Fatal("无法解析CA证书")
    }

    // 配置TLS
    tlsConfig := &tls.Config{
        Certificates: []tls.Certificate{cert},
        RootCAs:      caCertPool,
        // 关键:明确设置客户端认证
        ServerName: "api.url", // 替换为实际服务器域名
    }

    transport := &http.Transport{
        TLSClientConfig: tlsConfig,
        // 可选:启用连接复用
        MaxIdleConns: 10,
    }
    
    client := &http.Client{
        Transport: transport,
        Timeout:   30 * time.Second, // 添加超时控制
    }

    resp, err := client.Post("https://api.url", "text/xml", bytes.NewBuffer([]byte(xml)))
    if err != nil {
        log.Fatal("请求失败:", err)
    }
    defer resp.Body.Close()

    contents, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal("读取响应失败:", err)
    }
    fmt.Printf("响应状态: %d\n", resp.StatusCode)
    fmt.Printf("响应内容: %s\n", string(contents))
}

主要修改点:

  1. 移除了InsecureSkipVerify: true,这会跳过服务器证书验证
  2. 添加了ServerName字段,用于TLS握手时的服务器名称指示
  3. 增加了证书加载的错误检查
  4. 添加了响应处理和资源清理

如果问题仍然存在,可以添加TLS调试信息:

import "crypto/tls"

// 在tlsConfig中添加
tlsConfig := &tls.Config{
    Certificates: []tls.Certificate{cert},
    RootCAs:      caCertPool,
    ServerName:   "api.url",
}

// 启用TLS调试(需要设置环境变量)
// GODEBUG=x509ignoreCN=0,x509sha1=1 go run yourprogram.go

证书文件路径需要确保正确,且证书格式应为PEM格式。如果使用DER格式的CA证书,需要使用x509.ParseCertificate代替AppendCertsFromPEM

回到顶部