go 标准库的 encoding/asn1 包严格校验 x.509 证书中的 utc 时间格式,当服务器返回 yymmddhhmmss+0000(带偏移)而非标准 yymmddhhmmssz(zulu 时间)时会报错“time did not serialize back to the original value”,且无法通过 insecureskipverify: true 绕过。
go 标准库的 encoding/asn1 包严格校验 x.509 证书中的 utc 时间格式,当服务器返回 yymmddhhmmss+0000(带偏移)而非标准 yymmddhhmmssz(zulu 时间)时会报错“time did not serialize back to the original value”,且无法通过 insecureskipverify: true 绕过。
该错误并非 TLS 握手失败,而是发生在证书解析阶段——Go 在调用 crypto/x509 解析服务器证书时,底层依赖 encoding/asn1.parseUTCTime 进行时间字段反序列化。而该函数硬编码仅接受 Z 结尾格式(如 141020123326Z),对符合 ASN.1 规范但带 +0000 偏移的合法格式(如 141020123326+0000)直接拒绝,且该校验逻辑在 InsecureSkipVerify: true 下依然执行,因此跳过证书验证无法解决此问题。
为什么其他语言能绕过?
Python(requests)、Ruby(net/http)等语言的 TLS 实现通常使用 OpenSSL 或更宽松的 ASN.1 解析器,对 UTC 时间格式容忍度更高;而 Go 选择自研、严格遵循 RFC 的解析逻辑,导致在面对某些自签名或老旧设备(如 oVirt 早期版本)生成的非标准证书时出现兼容性问题。
可行的解决方案(按推荐顺序)
✅ 首选:升级 Go 版本(Go 1.19+)
该问题已在 Go 1.19 中被官方修复(issue #15732)。新版本 encoding/asn1 扩展支持 +HHMM/-HHMM 格式(如 +0000),无需任何代码修改。请运行 go version 确认,并升级至 Go 1.19 或更高版本:
# 检查当前版本 go version # 若低于 1.19,请升级(以 macOS/Linux 为例) curl -L https://go.dev/dl/go1.22.5.linux-amd64.tar.gz | sudo tar -C /usr/local -xzf - export PATH="/usr/local/go/bin:$PATH"
✅ 临时兼容方案:使用自定义 http.Transport + tls.Config.VerifyPeerCertificate(Go 1.15+)
若暂无法升级 Go,可通过手动跳过 ASN.1 时间校验环节实现变通:在 TLS 握手后,直接信任证书链(绕过 x509.ParseCertificate 的时间解析),仅保留基础连接安全性:
package main
import (
"crypto/tls"
"fmt"
"io/ioutil"
"net/http"
)
func doRequest(url string) ([]byte, error) {
transCfg := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
// 关键:禁用默认证书验证,改由空函数替代
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// 不做任何校验 —— 此处跳过所有 x509 解析(包括 ASN.1 时间)
return nil
},
},
}
client := &http.Client{Transport: transCfg}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.SetBasicAuth("user", "pass")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Error: %v\n", err)
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
func main() {
body, err := doRequest("https://ovirt.example.com/")
if err != nil {
panic(err)
}
fmt.Println("Response Status:", string(body))
}
⚠️ 注意事项:
- 此方案仅适用于开发/测试环境,生产环境务必使用有效证书;
- VerifyPeerCertificate: nil 与 InsecureSkipVerify: true 效果相同,但显式设为空函数更清晰;
- 需手动导入 crypto/x509(示例中省略,实际需添加 import "crypto/x509");
- Go 1.15+ 支持该钩子,旧版本需升级或采用其他方案。
❌ 不推荐方案
- 修改 Go 源码($GOROOT/src/encoding/asn1/asn1.go)并重编译标准库:易引入维护风险,且每次 Go 升级需重复操作;
- 替换整个 crypto/x509 或 tls 包:工程复杂度高,破坏标准库稳定性。
总结
根本原因在于 Go 对 ASN.1 UTC 时间格式的严格实现,而非 TLS 安全策略问题。升级 Go 至 1.19+ 是最安全、最可持续的解法;若受限于环境,可借助 VerifyPeerCertificate 钩子临时规避。切勿在生产系统中长期依赖 InsecureSkipVerify,应推动 oVirt 集群更新为符合 RFC 5280 的标准证书。











