.NET SslStream / HttpClient.NET
错误信息
The remote certificate is invalid because of errors in the certificate chain: NotTimeValid
HttpClient 会把它报告为“The SSL connection could not be established, see inner exception.”的内部异常。多个标志可能会一起列出,例如“NotTimeValid, UntrustedRoot”。
.NET SslStream / HttpClient — X509ChainStatusFlags.NotTimeValid
含义
按照这台机器的时钟,.NET 构建的证书链中有一张证书不在有效期内:已过期或尚未生效。NotTimeValid 不会指出是哪一张证书;验证回调中的证书链元素会指出来。根证书和名称都没有问题,否则还会同时出现其他标志。
原因
服务器证书(或中间证书)已过期
常见没有续期,或者续期后的证书没有绑定(IIS 绑定、Kestrel 配置、Azure/Key Vault 引用、负载均衡器)。
时钟偏慢的客户端遇到刚签发的证书
偶见从“此刻”起生效的证书,在时钟慢了几分钟的机器上会验证失败。
客户端时钟不准
偶见从快照恢复的虚拟机、没有启用 NTP 的设备,或者运行在时钟漂移的主机上的容器。
修复方法
- 1.
检查服务器实际发送的证书日期
将 notBefore 和 notAfter 与当前 UTC 时间比较。
bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \ | openssl x509 -noout -subject -startdate -enddate - 2.
记录出错的证书链元素
用一个验证回调记录 ChainElementStatus 并返回默认判定结果,就能看出是哪张证书不在有效期内,而不会削弱验证。
csharp // Log which certificate failed and why, without accepting it var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => { foreach (var element in chain!.ChainElements) foreach (var status in element.ChainElementStatus) Console.WriteLine($"{element.Certificate.Subject}: {status.Status} {element.Certificate.NotAfter:u}"); return errors == System.Net.Security.SslPolicyErrors.None; }, }; - 3.
续期并重新绑定
续期证书,并更新所有提供该证书的绑定。
bash sudo certbot renew && sudo systemctl reload nginx # or renew in your CA / key vault and redeploy