PHP通过IP提取SSL证书失败,stream_socket_client无法创建连接原因咨询
问题分析与解决方案
这个问题我之前碰到过,核心原因是**SNI(Server Name Indication)**的配置差异,我来给你拆解清楚:
为什么用IP连接会失败?
现在很多服务器都是共享IP部署的——一个IP对应好几个域名。当发起HTTPS连接时,服务器需要知道你要访问的具体域名,才能返回对应的SSL证书,这个机制就是SNI。
当你用域名www.adidas.ru连接时,PHP的stream_socket_client会自动在SSL握手阶段发送SNI信息,服务器能匹配到正确的证书,握手自然成功。但直接用IP2.21.90.7连接时,PHP默认不会发送SNI信息,服务器根本不知道该返回哪个证书,于是直接拒绝了连接,这就是你看到的“无法创建连接且无错误提示”的原因。
至于Chrome能访问,是因为浏览器会自动处理SNI:哪怕你输入的是IP,浏览器在握手时还是会尝试发送对应的域名信息(或者服务器在无SNI请求时返回了默认证书),所以能正常获取到证书。
修复后的代码
要解决这个问题,你需要手动在SSL上下文里启用SNI,并指定目标域名。修改后的代码如下:
$addr = '2.21.90.7'; $targetDomain = 'www.adidas.com'; // 必须指定证书对应的域名,用于SNI请求 $sslContext = stream_context_create([ "ssl" => [ "capture_peer_cert" => true, "SNI_enabled" => true, // 开启SNI支持 "SNI_server_name" => $targetDomain, // 告诉服务器我们要访问的域名 // 因为IP和证书域名不匹配,暂时关闭证书验证(仅用于提取证书场景) "verify_peer" => false, "verify_peer_name" => false ] ]); $connection = stream_socket_client( "ssl://{$addr}:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $sslContext ); // 先检查连接是否成功 if ($connection) { var_dump($sslContext); var_dump($connection); var_dump($errno); var_dump($errstr); $certParams = stream_context_get_params($connection); var_dump($certParams); $certInfo = openssl_x509_parse($certParams['options']['ssl']['peer_certificate']); var_dump($certInfo); // 记得关闭连接 fclose($connection); } else { echo "连接失败:错误码{$errno},错误信息{$errstr}"; }
关键修改说明
- 启用SNI:通过
SNI_enabled和SNI_server_name配置,让PHP在握手时告诉服务器我们要访问的域名,服务器就能返回对应的证书了。 - 关闭证书验证:因为你用IP连接,证书里的域名是
www.adidas.com,和IP不匹配,默认的证书验证会失败。如果你的需求只是提取证书信息,不需要验证证书有效性的话,就可以关闭这两个选项。 - 增加连接判断:避免连接失败时后续代码报错,让逻辑更健壮。
这样修改后,你就能成功连接到服务器并提取到目标SSL证书的信息了。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

