You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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}";
}

关键修改说明

  1. 启用SNI:通过SNI_enabled和SNI_server_name配置,让PHP在握手时告诉服务器我们要访问的域名,服务器就能返回对应的证书了。
  2. 关闭证书验证:因为你用IP连接,证书里的域名是www.adidas.com,和IP不匹配,默认的证书验证会失败。如果你的需求只是提取证书信息,不需要验证证书有效性的话,就可以关闭这两个选项。
  3. 增加连接判断:避免连接失败时后续代码报错,让逻辑更健壮。

这样修改后,你就能成功连接到服务器并提取到目标SSL证书的信息了。

内容的提问来源于stack exchange,提问作者Michael

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:56:24