Golang中如何处理由未知机构签发的Crypto/x509证书
Golang中如何处理由未知机构签发的Crypto/x509证书 在使用SSL连接进行证书验证时,我遇到了以下问题。
已知的意大利SMTP服务器 smtp.tim.it 发送了以下证书数据(页面底部附有 openssl 命令的结果)。
看起来,它的证书链似乎存在某种断裂:
USERTrust RSA Certification Authority(已知受信任的CA) => 验证TI Trust Technologies DV CATI Trust Technologies OV CA=> 验证smtp.tim.it
但在这个链中,缺少对 TI Trust Technologies OV CA 的验证环节。
运行以下代码会失败,并提示 certificate: x509: certificate signed by unknown authority:
tlsConfig := &tls.Config{
InsecureSkipVerify: false, // If noCert is true, skip Go's verification entirely
ServerName: "smtp.tim.it",
}
tlsConn := tls.Client(socket.conn, tlsConfig)
// Perform the TLS handshake
err := tlsConn.Handshake()
但这种情况仅发生在 Linux 和 Android 上。 在 MAC 和 iOS 上,此代码可以正常工作并正确验证证书链。尚未在 Windows 上测试,但我推测它也能正常工作。经过一些调试,我发现差异来自以下代码部分:
verify.go:
// Use platform verifiers, where available, if Roots is from SystemCertPool.
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
// Don't use the system verifier if the system pool was replaced with a non-system pool,
// i.e. if SetFallbackRoots was called with x509usefallbackroots=1.
systemPool := systemRootsPool()
if opts.Roots == nil && (systemPool == nil || systemPool.systemPool) {
return c.systemVerify(&opts)
}
if opts.Roots != nil && opts.Roots.systemPool {
platformChains, err := c.systemVerify(&opts)
// If the platform verifier succeeded, or there are no additional
// roots, return the platform verifier result. Otherwise, continue
// with the Go verifier.
if err == nil || opts.Roots.len() == 0 {
return platformChains, err
}
}
}
我猜测这段代码导致操作系统处理验证链。我的问题是,对于其他平台(如 Android),我们该如何处理?(我们有许多使用此服务提供商的 Android 用户,并且强制要求证书验证,因此这对我们非常重要。此外,在我们之前的 Java 实现中,这曾经是有效的(不确定是如何实现的,因为那是很久以前开发的,代码也已过时)。)
感谢提供的任何帮助,如果这个问题在其他地方已经解答过,我表示抱歉(我个人未能找到答案),另外我对这些东西的工作原理了解不多。
openssl s_client -starttls smtp -connect smtp.tim.it:587 -showcerts 结果:
Connecting to 34.141.221.156 CONNECTED(00000003)
depth=0 C=IT, ST=Milano, O=TELECOM ITALIA SPA, CN=smtp.tim.it
verify error:num=20:unable to get local issuer certificate
verify return:1 depth=0 C=IT, ST=Milano, O=TELECOM ITALIA SPA, CN=smtp.tim.it verify error:num=21:unable to verify the first certificate verify return:1
depth=0 C=IT, ST=Milano, O=TELECOM ITALIA SPA, CN=smtp.tim.it
verify return:1
---
Certificate chain
0 s:C=IT, ST=Milano, O=TELECOM ITALIA SPA, CN=smtp.tim.it
i:C=IT, ST=Roma, L=Pomezia, O=TI Trust Technologies S.R.L., CN=TI Trust Technologies OV CA
a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
v:NotBefore: May 28 00:00:00 2025 GMT; NotAfter: Jun 27 23:59:59 2026 GMT
-----BEGIN CERTIFICATE-----
MIIHXzCCBkegAwIBAgIQBaf7Bz3GKlRh4r37qfgd/TANBgkqhkiG9w0BAQsFADB7
MQswCQYDVQQGEwJJVDENMAsGA1UECBMEUm9tYTEQMA4GA1UEBxMHUG9tZXppYTEl
MCMGA1UEChMcVEkgVHJ1c3QgVGVjaG5vbG9naWVzIFMuUi5MLjEkMCIGA1UEAxMb
VEkgVHJ1c3QgVGVjaG5vbG9naWVzIE9WIENBMB4XDTI1MDUyODAwMDAwMFoXDTI2
MDYyNzIzNTk1OVowUTELMAkGA1UEBhMCSVQxDzANBgNVBAgTBk1pbGFubzEbMBkG
A1UEChMSVEVMRUNPTSBJVEFMSUEgU1BBMRQwEgYDVQQDEwtzbXRwLnRpbS5pdDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALeUXDVdSR3vX69HqJcPTK/7
IkZKz5Hu3bVlxt5BVgfq8ikgAFYtGNVSChkm8cC1aqf39HK/lGbjBxmfoXqeqOXU
95yorSHKKS5M65434Gh/qxgZX0rNGOnkPSR9cGxhW5lOitPK755SXWFpXsWEo/A7
ChmuBkJvmWettaKRU4dFfxr9rxCIq/KMDW0pNOx6AVWYw9W22+Fcia7b2nMwI7lJ
NYDlPWEN7Y+v/xhsnR1WoDcjfpHGZFGijAcLKX9EFuzvE+AxNyiKk7JTPDJJvJIS
bN6PBBqLwkTRmZVPUZTZ+EUxIUlndpLeNd3GLUsTrSCxe7o5nsKr20hZeRBR/k8C
AwEAAaOCBAcwggQDMB8GA1UdIwQYMBaAFGPlP/jPJ7ExGSorXM3/LnH7KTjfMB0G
A1UdDgQWBBTt5pcOD+PaovEkJbMeaY3TZJYaNzAOBgNVHQ8BAf8EBAMCBaAwDAYD
VR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwSQYDVR0g
BEIwQDA0BgsrBgEEAbIxAQICSTAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3Rp
Z28uY29tL0NQUzAIBgZngQwBAgIwSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL3Rp
VHJ1c3QuY3JsLnNlY3RpZ28uY29tL1RJVHJ1c3RUZWNobm9sb2dpZXNPVkNBLmNy
bDCBgwYIKwYBBQUHAQEEdzB1MEYGCCsGAQUFBzAChjpodHRwOi8vdGlUcnVzdC5j
cnQuc2VjdGlnby5jb20vVElUcnVzdFRlY2hub2xvZ2llc09WQ0EuY3J0MCsGCCsG
AQUFBzABhh9odHRwOi8vdGlUcnVzdC5vY3NwLnNlY3RpZ28uY29tMIIBfwYKKwYB
BAHWeQIEAgSCAW8EggFrAWkAdQCWl2S/VViXrfdDh2g3CEJ36fA61fak8zZuRqQ/
D8qpxgAAAZcV1yCqAAAEAwBGMEQCIFFVWNEsc2UbBPBpkhqnTj0/KUtKHMAfALFM
DK9O9IF5AiBGa2EYjszuEg9BheOqodEayZZ9MHQBsif+hG+d8G+f0QB3ABmG1Mco
qm/+ugNveCpNAZGqzi1yMQ+uzl1wQS0lTMfUAAABlxXXIEsAAAQDAEgwRgIhAMhe
ncRRlLMTkW0OZr3N5JZqBcsjY83cBIQ+CTsJtx4QAiEAllswEQIQlonTy80tV3/A
bKzlbZLJiv3YD/V8TjHXK4MAdwAOV5S8866pPjMbLJkHs/eQ35vCPXEyJd0hqSWs
YcVOIQAAAZcV1yBNAAAEAwBIMEYCIQDj0Th5VH8vK1cToYt7hiHgSBjBa62e1FcX
KbaBkPb8JwIhAPiqdWtqVhyynkgJarD/WKDAvTLd69U7JPkiSeek1tt0MIHiBgNV
HREEgdowgdeCC3NtdHAudGltLml0gg4qLnBvc3RhLnRpbS5pdIIKYm94LnRpbi5p
dIILaW1hcC50aW0uaXSCC2ltYXAudGluLml0ggxpbWFwcy50aW4uaXSCC2luLmFs
aWNlLml0ggttYWlsLnRpbi5pdIIJbXgudGltLml0gglteC50aW4uaXSCDG91dC5h
bGljZS5pdIIKcG9wLnRpbS5pdIIKcG9wLnRpbi5pdIILcG9wcy50aW4uaXSCC3Nt
dHAudGluLml0ggxzbXRwcy50aW4uaXSCBnRpbi5pdDANBgkqhkiG9w0BAQsFAAOC
AQEAYXfyRlm5bU3Mm2fyXHi9OXl90cAZvd57sWb5geXDL1Ubwmmqqk/eCLxDsj8d
3bXmMjWQjTs/ciphG358H1PonrhF7Ct1ZW1njYEK0RZJsOM0uppBMFEhrCg3C77z
6iXNUoZ8Nx5bfUsST3dwR0ieA99OKXV8qRKolO23FGACoH4eIhrZc7X2J0euQeGY
gGNduEVael2IIS2eAoXVmA0t06UuF/FsVbvmrX6WC7W/SjZ04u6zCkNZkIi17I4W
+1TtZ9GR3TiEO1rAl7vsAaaMKLoAG8T5PuiG7iO96KTa/bj5lhXMm5UvyLY0RLxi
++bD6UvAIbf/+wtMFumZwctOXQ==
-----END CERTIFICATE-----
1 s:C=IT, ST=Roma, L=Pomezia, O=TI Trust Technologies S.R.L., CN=TI Trust Technologies DV CA
i:C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust RSA Certification Authority
a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA384
v:NotBefore: Jul 30 00:00:00 2019 GMT; NotAfter: Jul 29 23:59:59 2029 GMT
-----BEGIN CERTIFICATE-----
MIIGBjCCA+6gAwIBAgIRALMcXS7T8KrJaCn0VoiLO44wDQYJKoZIhvcNAQEMBQAw
gYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtK
ZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYD
VQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE5
MDczMDAwMDAwMFoXDTI5MDcyOTIzNTk1OVowezELMAkGA1UEBhMCSVQxDTALBgNV
BAgTBFJvbWExEDAOBgNVBAcTB1BvbWV6aWExJTAjBgNVBAoTHFRJIFRydXN0IFRl
Y2hub2xvZ2llcyBTLlIuTC4xJDAiBgNVBAMTG1RJIFRydXN0IFRlY2hub2xvZ2ll
cyBEViBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKRYvlj4gqxr
SMQPfD5tXRBiK9tifjCoeFZ6BnPwT+G6XthhxODxX1ZVrqXjREjtef6uQc1K9ao/
gXJ5jA4soV72lRLvxyVZ9uHcw83WZft+6oqpQaSOdo8Bvag3HEegoSb3zElSmPhv
NArENCdK6uiHmXvOW6uzPPdXG4uMeqyTdZVlR1u27UVJ5xB2Um6ryNfHqAA4eN22
djopHRIeVAu5sQoeAurLTBOfq4QEpecDm0xa+ySe642X+HVNzQGye7diI0sD/ngf
Ir9x7h1HxyqxC24rXT4XZlK+ZA/d5bpuvoMQMDEV4CKo6T9J7oo+Oq3u+HFwBo94
BClLlXBc9aMCAwEAAaOCAXUwggFxMB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvA
nfKyA2bLMB0GA1UdDgQWBBTWUeJ5s8ZXr95WY+CywoVtRjQVpjAOBgNVHQ8BAf8E
BAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
KwYBBQUHAwIwIgYDVR0gBBswGTANBgsrBgEEAbIxAQICSTAIBgZngQwBAgEwUAYD
VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz
dFJTQUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUFBwEBBGowaDA/
BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJT
QUFkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1
c3QuY29tMA0GCSqGSIb3DQEBDAUAA4ICAQA0MXhgkf6J+NnG0SAwIkNrC0zv27vT
IwtAkVcp3TaIIdnIT11XJ1mVmt7MwidqMC7cYXqe81zlIlVB65+NwZWuhEiM+1ce
vu8MFA6xcaFHFTTFYFIsBGHw7Fgbo54rP5cIHKehyCcCxvFFdQ2tvbMVjGWMubZO
5sD0rAWb1gGkVQ8uNgwR2RJrrfllPDqbNz/PNyxAAJWtY7dpTd1duEvA8u+uJ2wk
0DHKtPX8MvmojNwL89KZMUbXE+Ecg4Dc120SWjk6tcExkkceRP91MQv9oSABGw03
mSO2F02LguHJbfrS3kP1Twx68B33SIzR7OUcP93aAU+eKjCiY+jMqfr/knaqmaY1
U5AEANMHIFLoM7SOYvQhBa0sqNAxiwIZPT2lGxN8/znPAf9wEk7zHOpsJ2Qd+Tb1
Or6F+Y0Q2SsVGCxEB+HiFG9Az/rBk0sZhMuCyvd81XYPO7aTJjf/ppwtiB更多关于Golang中如何处理由未知机构签发的Crypto/x509证书的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中如何处理由未知机构签发的Crypto/x509证书的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中处理由未知机构签发的x509证书,可以通过自定义证书验证逻辑来实现。以下是几种解决方案:
解决方案1:添加中间证书到信任链
import (
"crypto/tls"
"crypto/x509"
)
// 中间证书(TI Trust Technologies OV CA)
const intermediateCert = `-----BEGIN CERTIFICATE-----
MIIF...(完整的中间证书PEM)...Q==
-----END CERTIFICATE-----`
func createTLSConfig() (*tls.Config, error) {
// 创建证书池
rootCAs, err := x509.SystemCertPool()
if err != nil {
rootCAs = x509.NewCertPool()
}
// 添加中间证书
if ok := rootCAs.AppendCertsFromPEM([]byte(intermediateCert)); !ok {
return nil, fmt.Errorf("failed to append intermediate certificate")
}
return &tls.Config{
RootCAs: rootCAs,
ServerName: "smtp.tim.it",
InsecureSkipVerify: false,
}, nil
}
// 使用
tlsConfig, err := createTLSConfig()
if err != nil {
// 处理错误
}
tlsConn := tls.Client(socket.conn, tlsConfig)
err = tlsConn.Handshake()
解决方案2:完全自定义验证
import (
"crypto/tls"
"crypto/x509"
"errors"
)
func customVerify(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(rawCerts) == 0 {
return errors.New("no certificates provided")
}
// 解析服务器证书
serverCert, err := x509.ParseCertificate(rawCerts[0])
if err != nil {
return err
}
// 创建证书池
certPool := x509.NewCertPool()
// 添加系统根证书
systemPool, err := x509.SystemCertPool()
if err == nil && systemPool != nil {
certPool = systemPool
}
// 添加中间证书
intermediateCert := `-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----`
certPool.AppendCertsFromPEM([]byte(intermediateCert))
// 构建验证选项
opts := x509.VerifyOptions{
Roots: certPool,
DNSName: "smtp.tim.it",
Intermediates: x509.NewCertPool(),
}
// 添加所有中间证书到中间池
for i := 1; i < len(rawCerts); i++ {
intermediate, err := x509.ParseCertificate(rawCerts[i])
if err != nil {
continue
}
opts.Intermediates.AddCert(intermediate)
}
// 执行验证
_, err = serverCert.Verify(opts)
return err
}
// 使用
tlsConfig := &tls.Config{
ServerName: "smtp.tim.it",
InsecureSkipVerify: true, // 禁用默认验证
VerifyConnection: customVerify,
}
tlsConn := tls.Client(socket.conn, tlsConfig)
err := tlsConn.Handshake()
解决方案3:动态获取并缓存中间证书
import (
"crypto/tls"
"crypto/x509"
"sync"
)
var (
certCache = make(map[string]*x509.Certificate)
certCacheLock sync.RWMutex
)
func getIntermediateCert(serverName string) (*x509.Certificate, error) {
certCacheLock.RLock()
if cert, ok := certCache[serverName]; ok {
certCacheLock.RUnlock()
return cert, nil
}
certCacheLock.RUnlock()
// 这里可以实现从服务器获取中间证书的逻辑
// 或者从预定义的映射中获取
intermediatePEM := getPredefinedCert(serverName)
cert, err := x509.ParseCertificate([]byte(intermediatePEM))
if err != nil {
return nil, err
}
certCacheLock.Lock()
certCache[serverName] = cert
certCacheLock.Unlock()
return cert, nil
}
func createDynamicTLSConfig(serverName string) (*tls.Config, error) {
intermediateCert, err := getIntermediateCert(serverName)
if err != nil {
return nil, err
}
rootCAs, _ := x509.SystemCertPool()
if rootCAs == nil {
rootCAs = x509.NewCertPool()
}
// 将中间证书添加到池中
rootCAs.AddCert(intermediateCert)
return &tls.Config{
RootCAs: rootCAs,
ServerName: serverName,
InsecureSkipVerify: false,
}, nil
}
解决方案4:针对Android平台的特定处理
// +build android
package main
import (
"crypto/tls"
"crypto/x509"
)
func createAndroidTLSConfig() *tls.Config {
// Android系统证书池可能不完整,手动添加所需证书
certPool := x509.NewCertPool()
// 添加USERTrust RSA根证书
certPool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF...USERTrust根证书...Q==
-----END CERTIFICATE-----`))
// 添加TI Trust Technologies中间证书
certPool.AppendCertsFromPEM([]byte(`-----BEGIN CERTIFICATE-----
MIIF...TI Trust OV CA证书...Q==
-----END CERTIFICATE-----`))
return &tls.Config{
RootCAs: certPool,
ServerName: "smtp.tim.it",
InsecureSkipVerify: false,
}
}
完整示例:处理smtp.tim.it证书
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"net"
)
func main() {
// 连接到SMTP服务器
conn, err := net.Dial("tcp", "smtp.tim.it:587")
if err != nil {
panic(err)
}
defer conn.Close()
// 创建TLS配置
tlsConfig := &tls.Config{
ServerName: "smtp.tim.it",
RootCAs: createCertPool(),
}
// 执行TLS握手
tlsConn := tls.Client(conn, tlsConfig)
if err := tlsConn.Handshake(); err != nil {
fmt.Printf("TLS握手失败: %v\n", err)
// 尝试使用自定义验证
tlsConfig.InsecureSkipVerify = true
tlsConfig.VerifyConnection = createCustomVerifier()
tlsConn = tls.Client(conn, tlsConfig)
if err := tlsConn.Handshake(); err != nil {
panic(err)
}
}
fmt.Println("TLS连接成功建立")
}
func createCertPool() *x509.CertPool {
pool := x509.NewCertPool()
// 添加TI Trust Technologies OV CA证书
ovCert := `-----BEGIN CERTIFICATE-----
MIIF...(实际的TI Trust Technologies OV CA证书)...Q==
-----END CERTIFICATE-----`
if ok := pool.AppendCertsFromPEM([]byte(ovCert)); !ok {
fmt.Println("警告: 无法添加中间证书")
}
return pool
}
func createCustomVerifier() func(tls.ConnectionState) error {
return func(cs tls.ConnectionState) error {
opts := x509.VerifyOptions{
DNSName: "smtp.tim.it",
Intermediates: x509.NewCertPool(),
Roots: createCertPool(),
}
for _, cert := range cs.PeerCertificates[1:] {
opts.Intermediates.AddCert(cert)
}
_, err := cs.PeerCertificates[0].Verify(opts)
return err
}
}
这些解决方案允许你在Android和其他平台上正确处理缺失中间证书的情况。建议使用解决方案1或2,它们提供了最灵活和安全的证书验证方式。

