Viewed   139 times

I'm having hard time consuming a secure WCF Web Service from a PHP site. My knowledge in PHP are limited, I found various examples on the Web but didn't succeeded making them working yet.

I have a Silverlight application that also consume this WebService and it works fine. But when I run the PHP site, I get this error :

MessageSecurityException: Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security.

I also tried to change the customBinding to a basicHttpBinding. When I do that, the Custom Username validator is not called anymore. But, using basicHttpBinding and removing credentials validation on my WCF Service "works" (Besides the fact that it is unsecured). So the problem seems to related to the security message.

Could someone give me a working example or tutorial that could help me making this PHP page works?

Here is my PHP code :

    $options = array( 
            'soap_version'  => SOAP_1_1, 
            'exceptions'    => true, 
            'trace'         => 1, 
            'cache_wsdl'    => WSDL_CACHE_NONE,
            'Username'      => 'MyUserName', 
            'password'      => 'MyPassword');

$client = new SoapClient('https://UrlToService/Service.svc?wsdl', $options); 

try
{
    $phpresponse = $client->Get(); 

    print $phpresponse->GetResult->Version;
    echo "</b><BR/><BR/>";
}
catch(Exception $e) 
{ 
    echo "<h2>Exception Error!</h2></b>"; 
    echo $e->getMessage(); 
    echo "<BR/><BR/>";
}

WCF Configuration

<behavior name="sslBehavior">
   <serviceMetadata httpsGetEnabled="true" />
   <serviceDebug includeExceptionDetailInFaults="true" />
   <serviceCredentials>
      <userNameAuthentication userNamePasswordValidationMode="Custom"   customUserNamePasswordValidatorType="MyNamespace.ServiceUserNameValidator, MyNamespace" />
   </serviceCredentials>
   <serviceSecurityAudit
    auditLogLocation="Application"
    serviceAuthorizationAuditLevel="Failure"
    messageAuthenticationAuditLevel="Failure"
    suppressAuditFailure="true" />
</behavior>

<customBinding>
    <binding name="sslCustomBinding">
      <security authenticationMode="UserNameOverTransport" includeTimestamp="true" allowInsecureTransport="true">
        <localServiceSettings maxClockSkew="00:10:00" />
        <localClientSettings maxClockSkew="00:10:00" />
        <secureConversationBootstrap />
      </security>
      <textMessageEncoding messageVersion="Soap11" />
      <httpsTransport />
    </binding>
  </customBinding>

<service behaviorConfiguration="sslBehavior" name="MyNamespace.Services.Service">
    <endpoint address="" binding="customBinding" 
              bindingConfiguration="sslCustomBinding" contract="MyNamespace.ServiceContracts.IService" />
    <host>
      <baseAddresses>
        <add baseAddress="https://UrlToService/Services/" />
      </baseAddresses>
    </host>
  </service>

 Answers

2

I solved the problem. I had to extends the "SoapHeader" class in PHP to make it compliant with the WS-Security standard.

Here is the solution :

PHP Header class

class WsseAuthHeader extends SoapHeader 
{
    private $wss_ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
    function __construct($user, $pass, $ns = null) 
    {    
        if ($ns) 
        {        
            $this->wss_ns = $ns;    
        }    

        $auth = new stdClass();    

        $auth->Username = new SoapVar($user, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);     
        $auth->Password = new SoapVar($pass, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);    
        $username_token = new stdClass();    
        $username_token->UsernameToken = new SoapVar($auth, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns);     
        $security_sv = new SoapVar(        
                                new SoapVar($username_token, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns),        
                                SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'Security', $this->wss_ns);    

        parent::__construct($this->wss_ns, 'Security', $security_sv, true);
    }
}

PHP Client call

$options = array( 
            'soap_version'    => SOAP_1_1, 
            'exceptions'      => true, 
            'trace'           => 1, 
            'wdsl_local_copy' => true
            );


$username = "MyUser";
$password = "MyPassword";

$wsse_header = new WsseAuthHeader($username, $password);    

$client = new SoapClient('https://UrlToService/Service.svc?wsdl', $options); 
$client->__setSoapHeaders(array($wsse_header));

try
{
    $phpresponse = $client->Get(); 

    print $phpresponse->GetResult->Version;
    echo "</b><BR/><BR/>";
}
catch(Exception $e) 
{ 
    echo "<h2>Exception Error!</h2></b>"; 
    echo $e->getMessage(); 
}

Hope it will helps someone else!

Thanks to Chris : Connecting to WS-Security protected Web Service with PHP

Friday, October 28, 2022
2

Our problem has been resolved by fixing the location of soap.wsdl_cache_dir in php.ini.

Our websites were hosted and developed on Windows machines, so the default directory of '/tmp' didn't work. Changing this to C:WindowsTemp has meant although the initial connection is still slow, all subsequent requests are fast.

We will now look into using a more common solution of a warm-up script.

Wednesday, December 14, 2022
3

Well, no. (2) old-style ASP.NET webservices is on its way out - it's old, no longer being developed - it's been replaced by WCF.

So this leaves options 1 (straight WCF) and 3 (ADO.NET Data Services - renamed WCF Data Services recently).

Both use WCF as their basic technology - so learning and knowing about WCF is a must in both cases.

With straight WCF (Option 1) you have more options - you can self-host, host your service in IIS, use different protocols and bindings and so on. But with choice comes complexity - you need to learn and know all that stuff - at least to some degree. Your client needs to be able to talk SOAP to you - just about any language (.NET of course, Java, Ruby, PHP - you name it) can talk SOAP in one way or another.

If you're mostly interested in exposing data from databases onto outside clients, I think WCF Data Services is indeed quite a good choice. It's based on REST, so you can hit your WCF Data Service with a browser and just see what happens. It's quite powerful, and even offer a LINQ client side support - you formulate a LINQ query and this gets translated into your appropriate REST call to your data service.

With WCF Data Services, your client needs nothing but a HTTP stack - even the iPhone has that :-) But with a .NET client, things are of course nicer and more comfortable and more efficient.

I'd say check out the WCF Data Service first and see if that satisfies your needs - and if not, dig deeper into WCF. Also check out WCF Data Service at a glance for an intro.

UPDATE:

Marc, do I understand you correctly that WCF on the server fulfils this requirement? And ADO.NET (WCF) Data services too?

Absolutely. WCF (plain or with Data Services) on the server side does NOT dictate the client in any way, shape or form. You can hook up an iPhone to a WCF Data Service, if you really feel like it :-) WCF has been designed from the ground up to be very interoperable - actually, it's the one platform out there that implements the most WS-* industry standards for cross-platform communications.

Sunday, November 6, 2022
 
mikywan
 
5

It took a while but I got it done! A lot of blog posts were helpful but not end-to-end, so in the spirit of paying it forward, here's my blog post to walk through the process.

Wednesday, September 21, 2022
 
5

Try using a CXF proxy (service and client). In this case, you will get the raw XML into the flow. Of course you can deserialize it to java if you need to, for instance via XML-To-Object transformer (XStream).

http://www.mulesoft.org/documentation/display/MULE3USER/Proxying+Web+Services+with+CXF

From there, there are several ways to do transform the payload in the middle. Either go through java (as mentioned above) and transform java layer objects or write an XSLT sheet that does the transformation.Or transform the XML using Java and/or the other tools Mule provides (scripting, xpath etc).

You might want to explain the aggregation case once more in details. Do you want to merge the responses for future use or have one web service call -> fan out -> aggregate -> response?

Thursday, September 22, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :