OpenSSL报permitted subtree violation:证书SAN与CA名称约束排查

HTTPS叶证书没过期,SAN也包含访问域名,OpenSSL却报 permitted subtree violation。别急着重装根证书:这可能是CA证书的名称约束拒绝了下级名称。下面区分"访问名能否匹配"和"这条链能否为这些名称背书",再用error 47、48和离线命令定位。

一、SAN匹配,不代表名称约束通过

主机名验证检查访问名是否匹配叶证书;nameConstraints限制CA下级证书的名称范围。前者通过,后者仍可失败。好比姓名对得上,签发机构的授权范围却不覆盖这张证件。

中间CA只允许example.test,叶证书却含api.example.test与api.other.test。只访问前者,多余DNS SAN仍可让路径被拒绝。

二、从error 47和48定位,不把depth当约束来源

本次OpenSSL 1.1.1k FIPS离线实验中,47对应permitted subtree violation,48对应excluded subtree violation。前者是名称未落在允许范围,后者是命中排除范围。错误日志还会给出subject与depth,需要完整保留。

depth 0是待验证叶证书,1是签发它的CA,以此类推。但名称约束失败时,depth可以指向被检查名称所在的证书,并不保证指向写着nameConstraints的CA。本例约束放在中间CA,两类失败都报depth 0;别因此只修改叶证书配置。

hostname mismatch走主机名分支;unable to get local issuer certificate先处理构链。错误可不止一条,不用最后一行代替诊断。

三、逐张检查最终证书,不只看CSR

bash 复制代码
openssl version
# 每个文件只放一张证书;先确认文件角色
for f in root.pem issuing.pem leaf.pem; do
  printf "\nfile=%s\n" "$f"
  openssl x509 -in "$f" -noout -subject -issuer \
    -serial -fingerprint -sha256
  openssl x509 -in "$f" -noout \
    -ext subjectAltName,nameConstraints,basicConstraints
done

重点读取每张CA的Name Constraints和叶证书全部SAN。x509只读首张证书,fullchain.pem也不例外;多级链先拆分并编号。

RFC 5280规定名称约束扩展只用于CA证书,并应标为critical。permittedSubtrees表示允许范围,excludedSubtrees表示排除范围,命中排除范围仍应拒绝。存在多个受约束CA时,要满足路径上适用的各级限制,不是让下级写一个更宽范围就覆盖上级。

四、固定信任材料,分开验证名称与路径

bash 复制代码
openssl verify -no-CApath -CAfile root.pem \
  -untrusted issuing.pem -purpose sslserver \
  -verify_hostname api.example.test -show_chain leaf.pem
rc=$?
printf "verify_exit=%s\n" "$rc"

命令只验本地文件,不连接网站。CAfile固定根,untrusted提供中间链,leaf.pem是目标。名称、用途、路径一起检查,OK且退出0才通过;show_chain成功时显示路径。

为定位可另做不带verify_hostname的对照,但它只是不执行这项访问名匹配,并不会解除CA名称约束。本次多SAN越界样本去掉该参数仍报47。不能把这种诊断对照写进生产验收脚本,宣称域名也已验证。

不要提升中间CA为信任锚或关闭验证来消除错误;这改变了信任边界,并非修复原路径。

五、配置片段表达什么,修复就改什么

ini 复制代码
[issuing_ca]
basicConstraints = critical,CA:TRUE,pathlen:0
keyUsage = critical,keyCertSign,cRLSign
subjectKeyIdentifier = hash
nameConstraints = critical,permitted;DNS:example.test,excluded;DNS:admin.example.test

这是CA扩展片段,不是完整生产CA配置:允许example.test及其DNS子域,排除admin.example.test及其子域。它不是DNS解析规则,也不是网站访问控制。pathlen:0描述中间CA层级,不能替代名称范围检查;两种约束要分别满足。

DNS、IP、URI、邮件地址是不同名称类型,不能把permitted;DNS当作"只允许所有类型身份属于这个域"的总开关。本文只测试DNS SAN,不把结果推广到IP范围、通配符或其他客户端实现;涉及前导点、国际化域名等边界时应另建准确样本验证。

如果多余SAN是申请错误,由授权CA按正确名称集合重新签发叶证书。若业务确需范围外名称,应选择政策允许的签发路径,或由CA管理员审批后调整并重签适用CA证书。只改CSR、配置文件或服务端server_name,不会改变已经签名的扩展。

六、用正反例确认,不只测一个能通的名字

本次用临时根、中间CA和serverAuth叶证书离线验证,未改系统信任、时钟或生产服务,材料已清理。不冒充浏览器或线上握手实测。

叶证书或条件 退出码与错误 结论
仅api.example.test 0;OK 允许范围内
仅api.other.test 2;47,depth 0 超出允许范围
api.example.test加api.other.test 2;47,depth 0 访问名匹配仍不足
仅admin.example.test 2;48,depth 0 允许范围内但被排除
合规证书,验证wrong.example.test 2;62,depth 0 主机名不匹配
bash 复制代码
# 在隔离夹具中比较四份证书的路径约束,不检查访问名
for f in good.pem outside.pem mixed.pem excluded.pem; do
  openssl verify -no-CApath -CAfile root.pem \
    -untrusted issuing.pem -purpose sslserver "$f"
  rc=$?
  printf "%s exit=%s\n" "$f" "$rc"
done

四份文件对应表中前四行,并非内置样本。循环不验访问名,需另用verify_hostname。总体退出码可能来自末尾printf;自动化应逐项判断rc。

七、上线前还差一次真实客户端验证

离线通过不说明服务已加载新证书。部署后从实际客户端读取线上指纹、SAN与链,以正确主机名完成握手和业务请求。不同TLS库支持可能不同,本机OK不能替所有客户端背书。

八、验收清单与官方依据

  • 记录错误原文、退出码、depth与证书指纹,定位名称及约束来源。
  • 检查全部DNS SAN和路径上适用的名称限制,而非只找一个匹配名。
  • 合规正例通过,越界及排除负例仍失败;主机名另作明确校验。
  • 签发修改经过授权,最终证书重新读取,线上版本另行验收。

依据:OpenSSL扩展配置、verify手册、RFC 5280名称约束。先分清"名称匹配"和"名称授权范围",再决定重签哪一层。

相关推荐
ShayneLee81 小时前
Nginx只开一个端口!能做什么?(一)
运维·nginx
万联WANFLOW1 小时前
Docker Hub 镜像拉取慢、timeout 的排查方法
运维·docker·云原生·容器·eureka
Julien20042 小时前
CGroups 资源控制组
linux·运维·服务器·ssh·学习方法
Vcaker2 小时前
Linux学习37-rook-ceph部署
linux·运维·学习
yt004yt2 小时前
VOCs 绿岛项目数字化设计要点,结合能碳管控思路
大数据·运维
阳光九叶草LXGZXJ2 小时前
Linux-学习-12-JDK安装
java·linux·运维·学习
wtblszn10 小时前
自动化包装生产线设备可视化管理方案
运维·自动化
心之语歌11 小时前
Tkinter 画布基本梳理
运维·服务器·python
Shulex12 小时前
面向跨境电商多渠道消息系统的技术架构:亚马逊站内信合规对接与自动化执行链路设计
运维·架构·自动化