证书被拒:Certificate Transparency 日志不匹配 —— 排查指南

Chrome 拒绝一张本来有效的证书,报 NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED。证书签了但没进 CT log,或者 SCT 损坏。

证书链看着干净:openssl verify 过、链完整、主机名匹配、过期日期还远。但是 Chrome 50+ 拒绝连接,报 NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED。Firefox 和 Safari 可能照常加载。这是 Certificate Transparency(CT):从 2018 年起 Chrome 要求每张公开受信任的 TLS 证书通过 Signed Certificate Timestamps(SCT)证明自己已被至少两个 CT log 收录。SCT 缺失、损坏、来自被取消资格的 log、或者 CA 忘了 embed,Chrome 就直接拒绝、没有用户点穿。私有 CA 和企业 CA 只在它们的 root 不在 Chrome 默认信任库时才豁免;用了”真”CA 就必须 CT。

常见原因

按实际频率排序。

1. 内部 CA 证书没 SCT,但意外链上了 Chrome 信任的 root

你用内部 CA 签了证书。内部 CA 的中间证书恰好链到一个公开受信任的 root(因为方便交叉签了、或者把 Chrome 信任的 root 导进了链)。Chrome 把它当公共证书要求 CT——但内部 CA 不往 CT log 写,没 SCT。

怎么判断openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 显示链终止在公共 root。Leaf 证书没 SCT 扩展。

2. CA 失误,签发时没 embed SCT

正规公共 CA 出 bug 或人工流程漏了 CT log 步骤。证书是真的、但缺 SCT。大 CA(Let’s Encrypt / DigiCert)罕见,小 CA 或新产品偶发。

怎么判断openssl x509 -in cert.pem -noout -text | grep -A 10 "CT Precertificate" 啥都没有。crt.sh 用序列号搜不到。

3. SCT 通过 TLS 扩展送、但 server 不支持

CA 送 SCT 有三种方式:embed 在证书里(X.509 扩展)、TLS 握手扩展、OCSP stapling。CA 选了 TLS 扩展、但你 web server(旧版 nginx、某些 haproxy)不转发,Chrome 就看不到 SCT。

怎么判断:证书本身没 embed SCT。openssl s_client -ct -connect yourdomain.com:443 握手里看不到 SCT。Server 版本老到没这个功能。

4. SCT 签发时有效、但 log 后来被取消资格

Chrome 会周期性 retire CT log(log 运营商倒闭、或者发现被攻陷)。SCT 全都来自已退役 log 的证书追溯性失去 CT 合规。

怎么判断:证书有 SCT,但 tls.shcrt.sh 显示发证 log 已不在 Chrome 信任列表。同一家 CA 新签的证书 SCT 来自当前 log。

5. 证书刚签出来,SCT 时间窗还没对上

CT 要求至少两个 SCT 来自不同组织运营的 log,还有时间分布规则。刚签出来几分钟的证书,某些客户端临时 CT 校验失败。

怎么判断:证书签发在 1 小时内。错误几分钟后消失。重新签一张立刻好。

6. MitM 代理注入证书破坏 CT

企业 TLS 检查代理(Zscaler / Bluecoat 等)把每张证书重新用企业 CA 签。注入的证书没 SCT,因为企业 CA 不往 CT log 写。Chrome 企业策略下允许、企业策略外失败。

怎么判断:问题只在企业 / 学校网出现。浏览器 DevTools 里证书 issuer 是类似 “Corporate Root CA”、不是你真正的 CA。

开始前

  • 确认用户真的是 CT 错误,不是普通证书错误——NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED 这个具体错误码是唯一诊断信号。
  • 找到签证书的 CA、看它的 CT 日志策略。
  • 准备证书的 PEM 文件——要离线检查。
  • 看清楚受影响用户是不是在带 TLS 检查的企业 / 校园网。

需要收集的信息

  • 用户那边看到的 Chrome 错误码。
  • openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -showcerts 输出。
  • openssl x509 -in cert.pem -noout -text | grep -A 30 "CT" 输出。
  • 证书序列号——用来在 crt.sh 搜索。
  • Issuer CA 和中间证书名。
  • 受影响客户端的 Chrome 版本。

一步步修

顺序:先确认 CT 真的是问题,再在签发侧修。

第 1 步:用 openssl 和 crt.sh 确认诊断

抓证书:

openssl s_client -connect yourdomain.com:443 -servername yourdomain.com </dev/null 2>/dev/null | openssl x509 -outform PEM > cert.pem

查 embed 的 SCT:

openssl x509 -in cert.pem -noout -text | grep -A 20 "Precertificate"
openssl x509 -in cert.pem -noout -text | grep -A 5 "Signed Certificate Timestamp"

要看到 SCT 列表带 log ID 和 timestamp。输出空 = 没 embed SCT。

到 crt.sh 交叉验证:

https://crt.sh/?q=yourdomain.com

证书出现 = 进了 log。没出现 = CA 没发布。

第 2 步:证书完全没 SCT,换 CA 重签

最快的修法是从 Let’s Encrypt / ZeroSSL / 任意主流公共 CA 重签——他们现在都在签发时 embed SCT。

sudo certbot certonly --webroot -w /var/www/letsencrypt -d yourdomain.com

新证书查 embed SCT:

openssl x509 -in /etc/letsencrypt/live/yourdomain.com/cert.pem -noout -text | grep -A 5 "Signed Certificate Timestamp"

至少 2 条。部署完错误就清掉。

第 3 步:用的是内部 CA,选两条路

路径 A:让内部 CA 不链到公共 root。这样 Chrome 当私有 CA 处理、不要求 CT。怎么做:

  • 用专属于你公司的自签 root。
  • 通过 MDM / 组策略分发这个 root 到客户端信任库。
  • 移除任何到公共 root 的交叉签名。

路径 B:用真公共证书(Let’s Encrypt 加 DNS-01 challenge 对内部域名也能用,因为验证靠 DNS、不靠 HTTP)。

除非域名在私有 TLD(.internal / .corp、不在公网 DNS),都选 B。

第 4 步:Server 不转发 TLS 扩展 SCT,升级或换送达方式

CA 用 TLS 扩展送 SCT(DigiCert / Sectigo 某些私部产品),确认 web server 支持转发:

  • nginx 1.13+ 部分构建有 ssl_ct on; ssl_ct_static_scts /path/to/scts/;
  • haproxy 2.0+ 原生支持 TLS 扩展 SCT 送达。
  • 现代 Apache 通过 mod_ssl_ct(三方)支持。

更简单:让 CA 把送达方式切到 “embedded”(SCT 烤进证书扩展),web server 啥都不用动。

第 5 步:企业 TLS 检查问题:修策略 / 接受限制

企业网用户因为 TLS 检查打不开你的站点:

  • 建议企业 IT 把你的域名加到 TLS-inspection bypass list。
  • 或者接受:受管 Chrome 装机一般有 RequireCT = OFF 策略对企业 CA 内联签的证书生效。
  • 这其实不是你源站要修的——把约束讲清楚,别去削弱自己证书。

第 6 步:加 crt.sh / CT 监控提前发现问题

https://crt.sh/?q=yourdomain.com,或自动化 CT log 监听:

# 伪监控:发现 CT 里出现我们没签的证书就告警
curl -s "https://crt.sh/?q=yourdomain.com&output=json" | jq '.[] | select(.entry_timestamp > "2026-05-01")'

既检测异常签发、又确认自己的证书在被 log。

验证

  • openssl x509 -in cert.pem -noout -text | grep "Signed Certificate Timestamp" 至少返回 2 条 SCT。
  • 受影响客户端 Chrome 打开 https://yourdomain.com 无错误。
  • https://crt.sh/?q=yourdomain.com 签发后几分钟内能看到新证书序列号。
  • Chrome DevTools → Security 标签证书有效、无 CT 警告。
  • 干净 Chrome 无痕窗口在非企业网测试成功。

长期预防

  • 永远用已知 embed SCT 的 CA:Let’s Encrypt / ZeroSSL / DigiCert / Sectigo / GlobalSign——目前都行。
  • 内部 CA 别交叉签到公共 root,除非你打算从公共 root 签发。
  • cert-spottercrt.sh 监听、或商业服务(Hardenize / Censys)建 CT 监控,发现 SCT 缺失和异常签发。
  • Chrome 大版本发布后做一次校验,自己的证书没有在已取消 log 上。
  • 文档化签发流程包括 SCT 送达方式,未知工程师一年后能排查。

常见坑

  • 以为”证书链通就有效”——Chrome 的 CT 检查独立于链验证。
  • CA logging 坏了反复重新签同一张——拿到的还是没 SCT 的同款。
  • 试图在用户侧 Chrome 关 CT——只对企业受管 Chrome 通过策略生效、不对普通用户生效。
  • 忘了 Chrome 的 CT 强制对 ECDSA 和 RSA 同等适用、算法无关。
  • 换 CA 后没验证 SCT——新 CA 可能用 TLS 扩展送、被你 server 静默丢了。

FAQ

Q:为什么 Firefox 能开、Chrome 拒绝?

Firefox 目前对一般公开站点不强制 CT(撰写时)。Chrome 和 Safari(用自己的 CT log 列表)强制。证书按 RFC 标准”有效”、但过不了 Chrome 更严格的策略。

Q:能让 Chrome 跳过我域名的 CT 检查吗?

只在受管 Chrome 通过企业策略(CertificateTransparencyEnforcementDisabledForUrls)能做,不对普通用户生效。没有按用户的退出选项。

Q:CA 说他们写 CT log、但证书里没 SCT?

很可能 CA log 了证书、但用 TLS 扩展或 OCSP stapling 送 SCT、没 embed。查 web server 支持送达方式,或让 CA 给新签发切到 embedded。

Q:通配符证书也走 CT 吗?

一样。通配符跟普通证书一样签发流程、必须有合格 log 的 SCT。

参见 绑了自定义域名但 SSL 没发SSL 混合内容警告CAA 记录挡了证书签发证书续签自动化静默挂了

标签: #排查 #SSL #certificate-transparency #chrome #排查