【问题标题】:Two SoapClient requests for two different thirdparty WSDL services, one works, the other doesn't两个不同的第三方 WSDL 服务的两个 SoapClient 请求,一个有效,另一个无效
【发布时间】:2026-01-19 09:50:01
【问题描述】:

在这两种情况下,相应的 WSDL 都会加载到 Firefox 中并显示“安全连接”。 PHP 版本是 5.6.22,这意味着默认情况下 PHP 将验证连接是否安全(与 PHP 5.5.x 和之前的http://php.net/manual/en/migration56.openssl.php 的行为相反)。这个想法是精确地执行安全连接,这两种情况都是为了保证安全,隐式进行适当的验证。


需要这个案例才能工作(案例 A):

$wsdl = 'https://palena.sii.cl/DTEWS/CrSeed.jws?WSDL';
$entity_loader_status_old = libxml_disable_entity_loader(false);
$SoapClient = new SoapClient($wsdl);
$seed = $SoapClient -> getSeed();
libxml_disable_entity_loader($entity_loader_status_old);
var_dump($seed);

错误:

Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Couldn't load from 'https://palena.sii.cl/DTEWS/CrSeed.jws?WSDL' : failed to load external entity "https://palena.sii.cl/DTEWS/CrSeed.jws?WSDL"
 in /path/to/script.php:3
Stack trace:
#0 /path/to/script.php(3): SoapClient->SoapClient('https://palena....')
#1 {main}
  thrown in /path/to/script.php on line 3

此示例有效(案例 B):

$wsdl = 'https://www.lb.lt/webservices/ExchangeRates/ExchangeRates.asmx?WSDL';
$entity_loader_status_old = libxml_disable_entity_loader(false);
$SoapClient = new SoapClient($wsdl);
$exchangeRate = $SoapClient -> getExchangeRate();
libxml_disable_entity_loader($entity_loader_status_old);
var_dump($exchangeRate);

转储:

object(stdClass)#2 (1) { ["getExchangeRateResult"]=> string(2) "-1" }

注意:此案例为辅助,仅供演示。


案例 A 工作了几个月(总是 PHP 5.6.x),就在两天前它停止工作并抛出错误,但没有更改任何代码。失败日期接近 web 服务证书的 Valid-From 日期(2016 年 12 月 12 日至 2017 年 12 月 14 日),看起来服务器刚刚更新了它的证书(在细节中看到了日期通过在浏览器中加载 WSDL 地址来获取证书),这很可能与问题有关。

显然,要获得客户端的信任,续订的证书需要特定的中间证书,该中间证书传播性差,因此必须找到中间证书并将其添加到受信任的中间证书包中。为此,找到了中间证书,并使用它的下载地址,在客户端执行了接下来的三行:

wget http://symantec.tbs-certificats.com/Symantec_Class_3_EV_SSL_CA_G3.crt
cp Symantec_Class_3_EV_SSL_CA_G3.crt
/etc/pki/ca-trust/source/anchors update-ca-trust

在某种程度上它似乎奏效了,因为现在错误不是从第 3 行(SoapClient)产生,而是从第 4 行(getSeed)产生:

Fatal error: Uncaught SoapFault exception: [HTTP] Could not connect to host
 in /path/to/script.php:4
Stack trace:
#0 [internal function]: SoapClient->__doRequest('<?xml version="...', 'https://palena....', '', 1, 0)
#1 /path/to/script.php(4): SoapClient->__call('getSeed', Array)
#2 /path/to/script.php(4): SoapClient->getSeed()
#3 {main}
  thrown in /path/to/script.php on line 4

案例A在禁用安全连接验证时有效(但这不是解决方案,因为它破坏了安全性):

$wsdl = 'https://palena.sii.cl/DTEWS/CrSeed.jws?WSDL';
$entity_loader_status_old = libxml_disable_entity_loader(false);
$SoapClient = new SoapClient(
    $wsdl
    , [
        'stream_context' => stream_context_create([
            'ssl' => [
                'verify_peer' => false,
            ],
        ]),
    ]
);
$seed = $SoapClient -> getSeed();
libxml_disable_entity_loader($entity_loader_status_old);
var_dump($seed);

转储:

string(219) "00436612495400"

为什么案例 A 不适用于安全验证以及如何解决?

【问题讨论】:

标签: php web-services security wsdl tls1.2


【解决方案1】:

对我来说,案例 B 也适用:

它会返回这个:

<?xml version="1.0" encoding="UTF-8"?>
<SII:RESPUESTA xmlns:SII="http://www.sii.cl/XMLSchema">
<SII:RESP_BODY>
<SEMILLA>004361002032</SEMILLA></SII:RESP_BODY>
<SII:RESP_HDR><ESTADO>00</ESTADO></SII:RESP_HDR>
</SII:RESPUESTA>

我正在使用 php 版本 5.5.9

【讨论】:

  • 是否有证书验证?
  • 我刚刚运行了你的代码,它给了我上面的结果:
  • 我为每种情况提供了两个代码,不同之处只是一个具有明确的证书验证。现在我尝试禁用证书验证并且它有效,我认为您测试了在 php 中不验证证书的代码早于 5.6 (php.net/manual/en/migration56.openssl.php)。我认为如果您尝试使用明确验证证书的代码,那么您将收到错误,因为您使用的是 PHP 5.5。
最近更新 更多