无法使用PHP SOAP扩展使用WS-Security连接到SSL Web服务-证书,复杂的WSDL [英] Can't connect to SSL web service with WS-Security using PHP SOAP extension - certificate, complex WSDL

查看:97
本文介绍了无法使用PHP SOAP扩展使用WS-Security连接到SSL Web服务-证书,复杂的WSDL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用PHP5 SOAP扩展,我无法使用客户端证书和WS-Security连接到具有https端点的Web服务,尽管我可以使用具有完全相同的wsdl和客户端证书的soapUI进行连接,并获得对请求的正常响应。没有HTTP身份验证,也没有代理。我收到的消息是无法连接到主机。 已经能够验证我没有找到主机服务器。 (以前我错误地说我在打服务器。)

Using the PHP5 SOAP extension I have been unable to connect to a web service having an https endpoint, with client certificate and using WS-Security, although I can connect using soapUI with the exact same wsdl and client certificate, and obtain the normal response to the request. There is no HTTP authentication and no proxy is involved. The message I get is 'Could not connect to host'. Have been able to verify that I am NOT hitting the host server. (Earlier I wrongly said that I was hitting the server.)

自签名客户端SSL证书是由openssl从。转换的.pem文件。 p12密钥库,该密钥库又由keytool从.jks密钥库转换而来,该.jks密钥库具有包含私有密钥和客户端证书的单个条目。

The self-signed client SSL certificate is a .pem file converted by openssl from a .p12 keystore which in turn was converted by keytool from a .jks keystore having a single entry consisting of private key and client certificate.

在soapUI中,我不需要提供服务器专用证书,我给它的仅有的两个文件是wdsl和pem。我确实必须提供pem及其密码才能连接。我推测尽管有错误消息,但我的问题实际上可能是XML请求而不是SSL连接本身形成的。

In soapUI I did not need to supply a server private certificate, the only two files I gave it were the wdsl and pem. I did have to supply the pem and its passphrase to be able to connect. I am speculating that despite the error message my problem might actually be in the formation of the XML request rather than the SSL connection itself.

我得到的wsdl具有嵌套的复杂类型。 php服务器位于带有IIS的Windows XP笔记本电脑上。

The wsdl I have been given has nested complex types. The php server is on my Windows XP laptop with IIS.

代码,数据值和WSDL提取如下所示。 (WSSoapClient类只是扩展了SoapClient,添加了带有mustUnderstand = true的WS-Security Username Token标头,并包含了soapUI调用都需要的随机数。)

The code, data values and WSDL extracts are shown below. (The WSSoapClient class simply extends SoapClient, adding a WS-Security Username Token header with mustUnderstand = true and including a nonce, both of which the soapUI call had required.)

非常感谢您的帮助。我是一个新手,怎么做!经过许多建议,经过许多天的大量Google搜寻,并阅读了Kevin McArthur撰写的Pro PHP。尝试使用类映射代替嵌套数组的尝试也失败了。

Would so much appreciate any help. I'm a newbie thrown in at the deep end, and how! Have done vast amounts of Googling on this over many days, following many suggestions and have read Pro PHP by Kevin McArthur. An attempt to use classmaps in place of nested arrays also fell flat.

class STEeService
{


public function invokeWebService(array $connection, $operation, array $request)
 {
  try
   {  
    $localCertificateFilespec = $connection['localCertificateFilespec'];
$localCertificatePassphrase = $connection['localCertificatePassphrase'];

$sslOptions = array(
   'ssl' => array(
     'local_cert' => $localCertificateFilespec,
     'passphrase' => $localCertificatePassphrase,
     'allow_self-signed' => true,
     'verify_peer' => false
             )
          );  
$sslContext = stream_context_create($sslOptions);

$clientArguments = array(
    'stream_context' => $sslContext,
    'local_cert' => $localCertificateFilespec,    
    'passphrase' => $localCertificatePassphrase,
    'trace' => true,
    'exceptions' => true,   
    'encoding' => 'UTF-8',
    'soap_version' => SOAP_1_1
   );

$oClient = new WSSoapClient($connection['wsdlFilespec'], $clientArguments); 
$oClient->__setUsernameToken($connection['username'], $connection['password']);        

   return $oClient->__soapCall($operation, $request);      
   }
   catch (exception $e)
   {
    throw new Exception("Exception in eServices " . $operation . " ," . $e->getMessage(), "\n");
   }

 }
}






$ connection如下:




$connection is as follows:

array(5) { ["username"]=> string(8) "DFU00050" 
["password"]=> string(10) "Fabricate1" 
["wsdlFilespec"]=> 
string (63) "c:/inetpub/wwwroot/DMZExternalService_Concrete_WSDL_Staging.xml" 
["localCertificateFilespec"]=> string(37) 
"c:/inetpub/wwwroot/ClientKeystore.pem"
["localCertificatePassphrase"]=> string(14) "password123456" }



$ clientArgument如下:



$clientArguments is as follows:

array(7) { ["stream_context"]=> resource(8) of type (stream-context) 
["local_cert"]=> string(37) "c:/inetpub/wwwroot/ClientKeystore.pem" 
["passphrase"]=> string(14) "password123456" 
["trace"]=> bool(true) ["exceptions"]=> bool(true) ["encoding"]=> string(5) "UTF-8" 
["soap_version"]=> int(1) }



$操作如下:



$operation is as follows:

'getConsignmentDetails'



$ request为



$request is as follows:

array(1) { [0]=> array(2) { ["header"]=> array(2) { 
["source"]=> string(9) "customerA" ["accountNo"]=> string(8) "10072906" } 
["consignmentId"]=> string(11) "GKQ00000085" } }

注意嵌套的额外级别,一个包装请求的数组本身就是一个数组。尽管我不知道原因,但在帖子中建议这样做,但这似乎有助于避免其他异常。

Note how there is an extra level of nesting, an array wrapping the request which is itself an array. This was suggested in a post although I don't see the reason, but it seems to help avoid other exceptions.

    object(SoapFault)#6 (9) { ["message":protected]=> 
string(25) "Could not connect to host" ["string":"Exception":private]=> string(0) "" 
    ["code":protected]=> int(0) ["file":protected]=> string(43) "C:\Inetpub\wwwroot\eServices\WSSecurity.php" 
    ["line":protected]=> int(85) ["trace":"Exception":private]=> array(5) { [0]=> array(6) { 
    ["file"]=> string(43) "C:\Inetpub\wwwroot\eServices\WSSecurity.php" ["line"]=> int(85) ["function"]=> string(11) "__doRequest" 
    ["class"]=> string(10) "SoapClient" ["type"]=> string(2) "->" ["args"]=> array(4) { 
    [0]=> string(1240) " DFU00050 Fabricate1 E0ByMUA= 2010-10-28T13:13:52Z customerA10072906GKQ00000085 " 
    [1]=> string(127) "https://services.startrackexpress.com.au:7560/DMZExternalService/InterfaceServices/ExternalOps.serviceagent/OperationsEndpoint1" 
    [2]=> string(104) "/DMZExternalService/InterfaceServices/ExternalOps.serviceagent/OperationsEndpoint1/getConsignmentDetails" [3]=> int(1) } } 
    [1]=> array(4) { ["function"]=> string(11) "__doRequest" ["class"]=> string(39) "startrackexpress\eservices\WSSoapClient" 
    ["type"]=> string(2) "->" ["args"]=> array(5) { [0]=> string(1240) " DFU00050 Fabricate1 E0ByMUA= 2010-10-28T13:13:52Z customerA10072906GKQ00000085 " 
    [1]=> string(127) "https://services.startrackexpress.com.au:7560/DMZExternalService/InterfaceServices/ExternalOps.serviceagent/OperationsEndpoint1" 
    [2]=> string(104) "/DMZExternalService/InterfaceServices/ExternalOps.serviceagent/OperationsEndpoint1/getConsignmentDetails" [3]=> int(1) [4]=> int(0) } }
    [2]=> array(6) { ["file"]=> string(43) "C:\Inetpub\wwwroot\eServices\WSSecurity.php" ["line"]=> int(70) ["function"]=> string(10) "__soapCall" 
    ["class"]=> string(10) "SoapClient" ["type"]=> string(2) "->" ["args"]=> array(4) { [0]=> string(21) "getConsignmentDetails" [1]=> array(1) { 
    [0]=> array(2) { ["header"]=> array(2) { ["source"]=> string(9) "customerA" ["accountNo"]=> string(8) "10072906" } 
    ["consignmentId"]=> string(11) "GKQ00000085" } } [2]=> NULL [3]=> object(SoapHeader)#5 (4) { 
    ["namespace"]=> string(81) "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" ["name"]=> string(8) "Security" 
    ["data"]=> object(SoapVar)#4 (2) { ["enc_type"]=> int(147) ["enc_value"]=> string(594) " DFU00050 Fabricate1 E0ByMUA= 2010-10-28T13:13:52Z " } 
    ["mustUnderstand"]=> bool(true) } } } [3]=> array(6) { ["file"]=> string(42) "C:\Inetpub\wwwroot\eServices\eServices.php" 
    ["line"]=> int(87) ["function"]=> string(10) "__soapCall" ["class"]=> string(39) "startrackexpress\eservices\WSSoapClient" 
    ["type"]=> string(2) "->" ["args"]=> array(2) { [0]=> string(21) "getConsignmentDetails" [1]=> array(1) { [0]=> array(2) { 
    ["header"]=> array(2) { ["source"]=> string(9) "customerA" ["accountNo"]=> string(8) "10072906" } ["consignmentId"]=> string(11) "GKQ00000085" } } } } 
    [4]=> array(6) { ["file"]=> string(58) "C:\Inetpub\wwwroot\eServices\EnquireConsignmentDetails.php" ["line"]=> int(44) 
    ["function"]=> string(16) "invokeWebService" ["class"]=> string(38) "startrackexpress\eservices\STEeService" ["type"]=> string(2) "->" 
    ["args"]=> array(3) { [0]=> array(5) { ["username"]=> string(10) "DFU00050 " ["password"]=> string(12) "Fabricate1 " 
    ["wsdlFilespec"]=> string(63) "c:/inetpub/wwwroot/DMZExternalService_Concrete_WSDL_Staging.xml" 
    ["localCertificateFilespec"]=> string(37) "c:/inetpub/wwwroot/ClientKeystore.pem" ["localCertificatePassphrase"]=> string(14) "password123456" } 
    [1]=> string(21) "getConsignmentDetails" [2]=> array(1) { [0]=> array(2) { ["header"]=> array(2) { ["source"]=> string(9) "customerA" 
    ["accountNo"]=> string(8) "10072906" } ["consignmentId"]=> string(11) "GKQ00000085" } } } } } 
    ["previous":"Exception":private]=> NULL ["faultstring"]=> string(25) "Could not connect to host" ["faultcode"]=> string(4) "HTTP" }






一些WSDL摘录(TIBCO BusinessWorks):




Here are some WSDL extracts (TIBCO BusinessWorks):

            <xsd:complexType name="TransactionHeaderType">
            <xsd:sequence>
                <xsd:element name="source" type="xsd:string"/>
                <xsd:element name="accountNo" type="xsd:integer"/>
                <xsd:element name="userId" type="xsd:string" minOccurs="0"/>
                <xsd:element name="transactionId" type="xsd:string" minOccurs="0"/>
                <xsd:element name="transactionDatetime" type="xsd:dateTime" minOccurs="0"/>
            </xsd:sequence>
        </xsd:complexType>







       <xsd:element name="getConsignmentDetailRequest">
            <xsd:complexType>
                <xsd:sequence>
                    <xsd:element name="header" type="prim:TransactionHeaderType"/>
                    <xsd:element name="consignmentId" type="prim:ID" maxOccurs="unbounded"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>
        <xsd:element name="getConsignmentDetailResponse">
            <xsd:complexType>
                <xsd:sequence>
                    <xsd:element name="consignment" type="freight:consignmentType" minOccurs="0" maxOccurs="unbounded"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>







        <xsd:element name="getConsignmentDetailRequest">
            <xsd:complexType>
                <xsd:sequence>
                    <xsd:element name="header" type="prim:TransactionHeaderType"/>
                    <xsd:element name="consignmentId" type="prim:ID" maxOccurs="unbounded"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>
        <xsd:element name="getConsignmentDetailResponse">
            <xsd:complexType>
                <xsd:sequence>
                    <xsd:element name="consignment" type="freight:consignmentType" minOccurs="0" maxOccurs="unbounded"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>







    <wsdl:operation name="getConsignmentDetails">
        <wsdl:input message="tns:getConsignmentDetailsRequest"/>
        <wsdl:output message="tns:getConsignmentDetailsResponse"/>
        <wsdl:fault name="fault1" message="tns:fault"/>
    </wsdl:operation>







<wsdl:service name="ExternalOps">
    <wsdl:port name="OperationsEndpoint1" binding="tns:OperationsEndpoint1Binding">
        <soap:address location="https://services.startrackexpress.com.au:7560/DMZExternalService/InterfaceServices/ExternalOps.serviceagent/OperationsEndpoint1"/>
    </wsdl:port>
</wsdl:service>






这里与WSSoapClient类相关:




And here in case it's relevant is the WSSoapClient class:

    <?PHP
namespace startrackexpress\eservices;
use SoapClient, SoapVar, SoapHeader;

class WSSoapClient extends SoapClient
{
 private $username;
 private $password;

/*Generates a WS-Security header*/
 private function wssecurity_header()
 {
  $timestamp = gmdate('Y-m-d\TH:i:s\Z');
  $nonce = mt_rand(); 
  $passdigest = base64_encode(pack('H*', sha1(pack('H*', $nonce).pack('a*', $timestamp).pack('a*', $this->password))));

  $auth = '
<wsse:Security SOAP-ENV:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
    <wsse:Username>' . $this->username . '</wsse:Username>
    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">' . 
 $this->password . '</wsse:Password>
    <wsse:Nonce>' . base64_encode(pack('H*', $nonce)).'</wsse:Nonce>
    <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">' . $timestamp . '</wsu:Created>
   </wsse:UsernameToken>
</wsse:Security>
';
  $authvalues = new SoapVar($auth, XSD_ANYXML); 
  $header = new SoapHeader("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security",$authvalues, true);

  return $header;
 }

 // Sets a username and passphrase
 public function __setUsernameToken($username,$password)
 {
  $this->username=$username;
  $this->password=$password;
 }

 // Overwrites the original method, adding the security header
 public function __soapCall($function_name, $arguments, $options=null, $input_headers=null, $output_headers=null)
 {
  try
  {
    $result = parent::__soapCall($function_name, $arguments, $options, $this->wssecurity_header());
    return $result;
  }
  catch (exception $e)
  {
   throw new Exception("Exception in __soapCall, " . $e->getMessage(), "\n");
  }
 }
}
?>



更新:



请求XML将

Update:

The request XML would have been as follows:

<?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://startrackexpress/Common/Primitives/v1" xmlns:ns2="http://startrackexpress/Common/actions/externals/Consignment/v1" xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <SOAP-ENV:Header> <wsse:Security SOAP-ENV:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> 
<wsse:UsernameToken> <wsse:Username>DFU00050</wsse:Username> 
    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">Fabricate1</wsse:Password> 
    <wsse:Nonce>M4FIeGA=</wsse:Nonce> 
    <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2010-10-29T14:05:27Z</wsu:Created> 
    </wsse:UsernameToken> 
    </wsse:Security> </SOAP-ENV:Header>
    <SOAP-ENV:Body><ns2:getConsignmentDetailRequest>
    <ns2:header><ns1:source>customerA</ns1:source><ns1:accountNo>10072906</ns1:accountNo></ns2:header>
    <ns2:consignmentId>GKQ00000085</ns2:consignmentId>
    </ns2:getConsignmentDetailRequest></SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

这是通过WSSoapClient中的以下代码获得的:

This was obtained with the following code in WSSoapClient:

public function __doRequest($request, $location, $action, $version)         {
    echo "<p> " . htmlspecialchars($request) . " </p>" ;    
    return parent::__doRequest($request, $location, $action, $version);
}


推荐答案

星轨API。这是我编写的通过CURL代替的课程。

For anyone else struggling with the startrack API. Here is class I wrote to go via CURL instead.

Instructions: 

Add the attached file to:
Client/Executables 

Change line 28 from 

class WSSoapClient extends SoapClient

To:

require('SoapClientCurl.class.php');
class WSSoapClient extends SoapClientCurl

<?php

/**
 * Override to overcome problems with Startrack Self Signed SSL Certificates on
 * certain server configurations.
 *
 * The important options here that aren't available in the SoapClient options are
 * CURLOPT_SSLVERSION       - Forces the SSl Version to 3
 * CURLOPT_SSL_VERIFYHOST   - Tells ssl not to care that the Startrack SSL certificate is for a different domain
 * CURLOPT_SSL_VERIFYPEER   - Tells ssl not to care that the Startrack SSL certificate is from a bogus CA (I think)
 *
 */
class SoapClientCurl extends SoapClient
{
    /**
     *
     * @param string $request       - The XML SOAP request.
     * @param string $location      - The URL to request.
     * @param string $action        - The SOAP action.
     * @param int $version          - The SOAP version.
     * @param boolean $one_way      - If one_way is set to 1, this method returns nothing. Use this where a response is not expected.
     * @throws SoapFault
     * @return string|void
     */
    public function __doRequest($request, $location, $action, $version, $one_way = 0)
    {
        $handle = curl_init();

        curl_setopt($handle, CURLOPT_URL, $location);
        curl_setopt($handle, CURLOPT_HTTPHEADER, array(
                'Content-type: text/xml;charset="utf-8"',
                'Accept: text/xml',
                'Cache-Control: no-cache',
                'Pragma: no-cache',
                'SOAPAction: '.$action,
                'Content-length: '.strlen($request))
        );

        curl_setopt($handle, CURLOPT_RETURNTRANSFER,    true);
        curl_setopt($handle, CURLOPT_POSTFIELDS,        $request);
        curl_setopt($handle, CURLOPT_SSLVERSION,        3);
        curl_setopt($handle, CURLOPT_PORT,              443);
        curl_setopt($handle, CURLOPT_POST,              true );
        curl_setopt($handle, CURLOPT_SSL_VERIFYHOST,    false);
        curl_setopt($handle, CURLOPT_SSL_VERIFYPEER,    false);

        $response = curl_exec($handle);

        if(empty($response))
        {
            throw new SoapFault('CURL error: '.curl_error($handle), curl_errno($handle));
        }

        curl_close($handle);

        if(1 !== $one_way)
        {
            return $response;
        }
    }
}

这篇关于无法使用PHP SOAP扩展使用WS-Security连接到SSL Web服务-证书,复杂的WSDL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆