PHP在证书CN与主机名不匹配但主机名在SAN中时能否完成SSL握手?
PHP连接Tomcat SAN证书异常排查与修复方案
核心问题背景
原有正常运行的PHP程序在Tomcat更换SAN证书后出现连接失败,已确认访问域名在SAN列表内、手动指定cafile仍无法连接,PHP版本为7.3。
分步排查与修复方案
第一步:先验证服务端证书配置有效性
不要默认证书配置正确,直接通过OpenSSL命令拉取服务端实际返回的证书做校验:
重点检查3个点:openssl s_client -connect 你的访问域名:8443 </dev/null 2>/dev/null | openssl x509 -noout -text | grep -A 10 "Subject Alternative Name"- 输出的SAN列表中,访问域名必须是DNS类型的条目,用IP访问时必须配置对应IP类型的SAN条目,类型不匹配时校验直接失败
- 通配符SAN仅支持匹配一级子域名,例如
*.example.com无法匹配a.b.example.com - 拉取到的证书链必须完整,包含叶子证书、中间CA证书、根证书,缺少中间证书时哪怕cafile指定正确也会握手失败
第二步:修正SSL上下文与SoapClient参数
你当前代码有两个高频踩坑点:一是本地加载WSDL时,SoapClient不会自动提取location里的域名作为SSL校验主机名,会默认用本地WSDL路径做匹配,必然失败;二是缺少SNI、手动指定peer_name的关键配置。
修正后的代码示例:<?php $host = "server_name"; $username = "******"; $password = "******"; $context = stream_context_create([ 'ssl' => [ 'verify_peer' => true, // 生产环境请勿关闭校验,避免中间人攻击 'verify_peer_name' => true, 'allow_self_signed' => true, // 仅自签证书保留该配置,正规CA签发证书请删除此行 'cafile' => 'C:\path\to\your\fullchain.cer', // 必须传入包含完整证书链的文件,不能只传叶子证书 'SNI_enabled' => true, // Tomcat多证书托管场景必须开启,否则会拿到默认证书而非目标域名证书 'peer_name' => $host, // 手动强制指定SSL校验的主机名,解决本地WSDL导致的校验名错误 ] ]); $client = new SoapClient("C:\inetpub\wwwroot\PhoneSetup\axl\AXLAPI.wsdl", [ 'trace'=>true, 'exceptions'=>true, 'location'=>"https://".$host.":8443/axl", 'login'=>$username, 'password'=>$password, 'stream_context'=>$context, 'cache_wsdl' => WSDL_CACHE_NONE, // 关闭WSDL缓存,避免旧缓存导致配置不生效 ] ); try { $response = $client->getPhone(["name"=>"SEP28AFFDA53393"]); var_dump($response); } catch (SoapFault $e) { // 打印OpenSSL底层错误,直接定位失败原因 while ($opensslErr = openssl_error_string()) { var_dump("OpenSSL错误: ".$opensslErr); } var_dump("SOAP错误: ".$e->getMessage()); } ?>第三步:排查环境兼容与权限问题
运行如下代码检查PHP的OpenSSL环境配置:
重点确认:<?php var_dump(openssl_get_cert_locations()); var_dump(OPENSSL_VERSION_TEXT); ?>- PHP绑定的OpenSSL版本不低于1.0.2,低于该版本对SAN扩展的识别存在已知bug,无法正确匹配多SAN条目、国际化域名的SAN配置
- 你配置的cafile路径格式正确,且IIS运行PHP的账号(通常是IUSR、IIS_IUSRS用户组)对该证书文件有读取权限,权限不足时cafile会静默加载失败,退回到系统默认CA路径校验
第四步:分层验证排除问题
先跳过SoapClient,直接用HTTP流测试SSL连接是否正常,排除SoapClient本身的参数干扰:
如果该请求可以正常返回结果,说明SSL配置本身没问题,故障点就在SoapClient的参数覆盖或WSDL缓存上。<?php // 传入你配置好的ssl上下文 $result = file_get_contents("https://".$host.":8443/axl", false, $context); while ($err = openssl_error_string()) { var_dump($err); } var_dump($result); ?>
常见错误对应原因
- 报错
certificate subject name does not match target host name:peer_name未正确指定,或SAN条目确实不匹配、类型错误 - 报错
unable to get local issuer certificate:证书链不完整、cafile路径错误/权限不足 - 报错
no suitable signature algorithm:证书使用的签名算法(如Ed25519)过新,当前PHP绑定的OpenSSL版本不支持
内容的提问来源于stack exchange,提问作者Chad
相关产品推荐
相关产品推荐

