Go 1.19+默认禁用TLS_RSA_WITH_AES_128_CBC_SHA等旧套件,导致老客户端因无密码套件交集而RST;需显式配置MinVersion=TLS12、限定安全旧套件(如TLS_RSA_WITH_AES_128_CBC_SHA)、CurvePreferences=[P256]并设PreferServerCipherSuites=true。
go 默认不启用 tls_rsa_with_aes_128_cbc_sha 这类旧套件,老客户端(比如海康 ipc、java 7、某些嵌入式固件)连不上不是证书问题,是 clienthello 发出去后,服务端没一个能匹配的 cipher_suite,直接 rst —— 解决方案就是显式列出它们,但必须控制边界。
为什么加了 CipherSuites 还是握手失败
常见错误是只改了 CipherSuites 却忽略 MinVersion 或 CurvePreferences,导致协商在更早阶段就中断:
-
MinVersion设太高(如tls.VersionTLS13):老客户端根本不发 TLS 1.3 的ClientHello,连接直接被拒 -
CurvePreferences留空或含tls.CurveP384:部分旧 OpenSSL 实现无法处理非 P256 曲线,ServerHello 不回包 - 没设
PreferServerCipherSuites: true:客户端优先选自己列表里靠前的套件,哪怕服务端根本没列它
该保留哪些旧套件(底线清单)
不是“全开”,而是守住 TLS 1.2 底线,剔除已知高危项(_RC4_、_3DES_、_MD5_、_SHA(非 SHA256/SHA384)),只留尚可运转的:
-
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(首选,有前向保密) -
tls.TLS_RSA_WITH_AES_128_GCM_SHA256(兼容性最广,RSA 密钥交换) -
tls.TLS_RSA_WITH_AES_128_CBC_SHA(最后兜底,海康 IPC、部分 Java 7 必需) - 绝对不要加
tls.TLS_RSA_WITH_RC4_128_SHA:RC4 已被证明可被实时破解,2026 年仍启用等于主动放行中间人
服务端配置示例(http.Server 场景)
关键点:所有字段显式赋值,不依赖 nil 默认行为
config := &tls.Config{
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12, // 明确锁死,避免 TLS 1.3 干扰
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
},
CurvePreferences: []tls.CurveID{tls.CurveP256},
PreferServerCipherSuites: true,
}
server := &http.Server{
Addr: ":443",
TLSConfig: config,
}
server.ListenAndServeTLS("cert.pem", "key.pem")
验证是否真生效(别信 curl -v)
curl 只校验单个 cipher,容易误判。真实验证要分三层:
- 用
openssl s_client -connect yoursite.com:443 -tls1_2 -cipher 'AES128-SHA':看输出里Protocol是否为TLSv1.2,且无ssl handshake failure - Wireshark 抓包过滤
tls.handshake.type == 1,展开ServerHello→cipher_suite,确认值是0x002f(AES128-SHA)这类你允许的 ID,不是0x0000 - 拿真实设备连,比如某款海康 IPC 固件——它只认
TLS_RSA_WITH_AES_128_CBC_SHA,你漏掉这一条,它 TCP 建连后 100ms 内就 RST,日志里甚至看不到 TLS 错误
真正难的不是加套件,是判断哪些旧设备还在用、哪些套件已彻底不可信;加一条 TLS_RSA_WITH_AES_128_CBC_SHA 容易,但得清楚它意味着放弃前向保密,且必须确保私钥永不泄露——否则历史流量可被批量解密。

















