Web services has made the communication between heterogeneous environments (say PHP with .NET  or Java) a reality. It has defines standards for communicate not only with texts but also with binaries. And more importantly you can keep these communication confidential using encrypted messages according to your requirement. In this post, we will look at how we can implement such a system with PHP in one side.

In web services we can send/receive binary messages in two basic forms.

  1. Setting the binary inside the SOAP message. – Binary should be converted to base64 to make sure the SOAP body contains only texts. Since base64 converted data span longer than the binary data, we call this form as non-optimized way of sending binaries.
  2. Setting the binary outside the SOAP message – Binary would be sent as a MIME part in the message. And some element inside SOAP body keeps a reference to the binary using the MIME id. MTOM is a standard for referencing the MIME from inside the SOAP body. Since the binary is encoded, this will keep the message optimum with the binaries.

In WSF/PHP you can use any of these methods as you prefer. Lets forget about the encryption for now. We will check how we can send binaries in both of the above mentioned forms.

// first the request xml. Note tht xop:Include element that is referring the attachment with the id "myid1".
$reqPayloadString = <<<XML
<ns1:upload xmlns:ns1="http://wso2.org/wsfphp/samples/mtom">
               <ns1:fileName>test.jpg</ns1:fileName>
               <ns1:image xmlmime:contentType="image/jpeg" xmlns:xmlmime="http://www.w3.org/2004/06/xmlmime">
                  <xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:myid1"></xop:Include>
               </ns1:image>
</ns1:upload>
XML;

try {
    $f = file_get_contents("my_binary_file.jpg");

    // here in the attachments option we define the binaries
    // corresponding to the id defined in the above XML
    $reqMessage = new WSMessage($reqPayloadString,
                                array("to" => "http://localhost/simple_upload_service.php",
                                      "action" => "http://wso2.org/upload",
                                      "attachments" => array("myid1" => $f)));

    // creating the WSClient
    // here the option useMTOM will decide whether the
    // attachment is set MTOM or base64
    $client = new WSClient(array("useWSA" => TRUE,
                                 "useMTOM" => TRUE));

    // sending the message and retrieving the response
    $resMessage = $client->request($reqMessage);

    printf("Response = %s \\n", $resMessage->str);

} catch (Exception $e) {

    if ($e instanceof WSFault) {
        printf("Soap Fault: %s\\n", $e->Reason);
    } else {
        printf("Message = %s\\n",$e->getMessage());
    }
}

As mentioned in the inline comment we can choose the preferred form of sending binary using the “useMTOM” option. if it is true, the binary is set as a MTOM, (referencing from the body) or if it is set false, the binary will be set as a base64 binary within the SOAP body.
To encrypt the message you only need to write few additional lines. First you define your policy that you need to encrypt this message using a WSPolicy object. Then the security token including the service public key and your private key. You need to give these two option as a constructor argument in WSClient. Here is that little additional code you need to write to add the encryption.

    // loading the keys
    $rec_cert = ws_get_cert_from_file("receiving_server.cert");
    $pvt_key = ws_get_key_from_file("my_private_key.pem");

    // here we defines the policies and create WSPolicy object
    $sec_array = array("encrypt" => TRUE,
                       "algorithmSuite" => "Basic256Rsa15",
                       "securityTokenReference" => "IssuerSerial");

    $policy = new WSPolicy(array("security" => $sec_array));

    // defining Security Tokens
    $sec_token = new WSSecurityToken(array("privateKey" => $pvt_key,
                                           "receiverCertificate" => $rec_cert));

    // modifing WSClient with adding WSPolicy and WSSecurityToken object
    $client = new WSClient(array("useWSA" => TRUE,
                                 "useMTOM" => TRUE,
                                 "policy" => $policy,
                                 "securityToken" => $sec_token));

You can implement the receiving side of the message similar to the sending side that we just described above. The most important thing is it doesn’t need to be written in PHP. It can be a Java code or .NET code.If you already have web services that use encrypted binary messaging, the above php code can be use out of the box to communicate with it.

November 19th, 2008WS-SecurityPolicy With PHP

WS-SecurityPolicy specification defines standards for defining security policies for your web service. WSF/PHP allows you to declare your security policies according to these standards.

You can take one of following approaches to associate policies to your web service or client.

  • PHP Array to represent your policies
  • Policy file compliant with WS-Security Policy.
  • Declaring policies inline with the WSDL.

Declaring Policies with a PHP Array

This is a WSF/PHP specific API to declare policies for a web service. You don’t need to learn WS-Security Policy to write policies with this approach. You can set whether you want to use encryption, signing or usernameToken in a PHP array and create a WSPolicy object using it.

// here is the security array to declare your policies in simple manner
$sec_array = array("encrypt" => TRUE,
 "algorithmSuite" => "Basic256Rsa15",
 "securityTokenReference" => "IssuerSerial");

// creating WSPolicy instance using the policy array
$policy = new WSPolicy(array("security"=> $sec_array));

You can use this policy object to create a service along with a WSSecurityToken which contain the user parameters like the server private key and the client certificate.

$sec_token = new WSSecurityToken(array(
 "privateKey" => $server_pvt_key,
 "receiverCertificate" => $client_pub_key));

$svr = new WSService(array("actions" => $actions,
 "operations" => $operations,
 "policy" => $policy,
 "securityToken" => $sec_token)); // here is the policy object you just created

$svr->reply();

You can invoke this service just by writing a simple web service client. There also you need to provide the policies declared in the service, so the client can build his request to validate with server policies. You will be using a similar WSPolicy object to set these policies at the client side too, as show in the below code segment.

 $sec_token = new WSSecurityToken(array(
   "privateKey" => $pvt_key,
   "receiverCertificate" => $rec_cert));

 $client = new WSClient(array("useWSA" => TRUE,
    "policy" => $policy, /* the policy object */
    "securityToken" => $sec_token));

 $resMessage = $client->request($reqMessage);

Declaring Policies with a Policy File

You can set your policies in the server or client side using a policy file compliant with WS-Security Policy specification. You have to take this approach if your policy requirements are too complicated, like you want to sign only some parts of the message or you want to encrypt some soap headers.

Similar to the above method, here too you will use the WSPolicy object to set your policies. But unlike the above where you give the policies as a PHP array , here you can just give the policy file as an argument to the WSPolicy constructor.

// creating the WSPolicy instance from a policy file
$policy_xml = file_get_contents("policy.xml");
$policy = new WSPolicy($policy_xml);

Here is an example of a complete policy file written according to the WS-Security Policy standards. And you can find a quick guide on WS-Security Policy from the article Understanding WS-Security Policy Language written by Nandana, a key leader of Apache Rampart project.

Declaring Policies inline in a WSDL

We use WSDL to describe our web services. WSDL has the information about the service endpoint, the transport protocols (e.g. http), messaging protocols (e.g. SOAP) and the message schemas and many others about the service. You can attach your policies inside a WSDL.

Here is an example of a WSDL with inline policies. The difference in this approach is you can set your policies separately for each messages or each operations or each endpoints of your service. The following segment of a WSDL shows how you refer to different policies which are declared in the early part of the WSDL.

     <wsdl:binding name="CalendarSOAP12Binding" type="ns1:CalendarPortType">
       <!-- Endpoint policies are declared here.
          these are common to all messages transferring
          through this protocols (i.e. SOAP12, http)-->
        <wsp:PolicyReference URI="#transport_binding_policy"/>
        <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <wsdl:operation name="login">
           <soap12:operation soapAction="urn:login" style="document"/>
           <wsdl:input>
              <!-- policy specific to the 'login' operation -->
              <wsp:PolicyReference URI="#username_token_policy"/>
              <soap12:body use="literal"/>
           </wsdl:input>
           <wsdl:output>
              <soap12:body use="literal"/>
           </wsdl:output>
         </wsdl:operation>
         <wsdl:operation name="register">
            <!-- no specific policies are set for the 'register' operation
            <soap12:operation soapAction="urn:register" style="document"/>
            <wsdl:input>
              <soap12:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
               <soap12:body use="literal"/>
            </wsdl:output>
         </wsdl:operation>
           ....
       </wsdl:binding>

This is the binding section of a WSDL where we bind messaging protocol and transport protocols with a service endpoint. Here we have “login” and “register” operations. Note that we are referring to “transport_binding_policy” from the parent level of each operation elements. That means these policies are common to all the operation in that binding. And inside the “login” operation we are referring to “username_token_policy”, so in order to invoke this operation, you have to send username token headers. And “register” doesn’t require any operation specific policies allowing users to register without any prior authentications.

You can select any of the above mentioned approach to define policies of your web service or to invoke a web service that support WS-Policy. If your policy requirements are simple, it will be easy to use the array based approach. If your policy requirements are complex or you have a good understanding of WS-Policy and WS-Security Policy you can rely on the policy file based approach or defining policy inline with WSDL. And the former 2 methods will give you a nice separation of the logic code and security configurations. The selection is yours:)

Earlier I wrote a blog about how to make your wordpress blog a web service using the WSF/PHP Data Services library. I will expand that post to demonstrate the use of WS-Security features with WSF/PHP.

This time it is a Tag Search service for my wordpress blog. Check the ‘Tag Search’ Data Services Demo from http://ws.dimuthu.org/. The only difference is here you are authenticated before accessing the service using the username tokens as specified in WS-Security.

Just look at the WSSecurity constructor in the Data Service Demo Code. You can observe 4 new parameters passed in to it. (In addition to the “config” and “operations” options)

  • policy – This is where you specify the policy governed by the service.  Here you can either use the WS-Policy compliant policy file or just a simple PHP array that contain the required security token informations.
    $sec_array = array("useUsernameToken" => TRUE);
    $policy = new WSPolicy(array("security"=>$sec_array));
  • securityToken: You specify the user parameters like how you handle the authentication and the encoding type in this option.
    $security_token = new WSSecurityToken(array("passwordCallback" => "password_callback_function",
                                           "passwordType" => "Digest"));
    
    /* callback function
     * @param string $username username of the client request
     * @return string $password password for that username
     */
    function password_callback_function($username) {
        // In the real word I should authenticate users from database.
        // for this demo I have a simple if-else block
    
        if($username == "visitor") {
            return "visitor123";
        }
    
        return "notavistor";
    }

    Note that here you specify a callback function to the security token parameter. Inside this function you retrieve the password for the user (mostly from the database) and return. WSF/PHP will authenticate the user from these information.

  • useWSA : You need to set this option in order to generate the WS-Addressing parameters (like action) for your WSDL. WS-Addressing is required to run web services with WS-Security in WSF/PHP.
  • actions: You should provide a map of action to service operations in order to get the WS Addressing information generated with your WSDL.
    $actions = array("http://ws.dimuthu.org/blog/getPosts" => "getPosts");

    Just have a look at how these information are rendered in the generated WSDL, http://ws.dimuthu.org/blog/WordpressTagSearchService.php?wsdl. (Note the wsaw:action attribute in the messages inside the portType element.

After you deploy the service, it is very easy to generate a client with the WSDL. If you write clients in PHP you can use the wsdl2php tool shipping with WSF/PHP.  The code for my demo client can be found in http://ws.dimuthu.org/source.php?src=tag.search.client. (There I have hard coded the username and password just for the demo purpose)

With WSF/PHP 1.3.2 you can use following basic features in WS-Security.

Feature Purpose Array based Security Policy Options ($sec_policies) Security Token Options ($sec_token_options)
UsernameToken Authentication array(“useUsernameToken” => TRUE) array(“user” => “your_username”,
“password” => “your_password”,
“passwordType” => “Digest”); //Digest/Plain
Timestamp Avoid Interception,Replay Attack (use with signing) array(“includeTimeStamp” => TRUE); array(“ttl” => 100)
Signing Non-Repudiation, Verify Server/Clients identity array(“sign” => TRUE,
“algorithmSuite” => “Basic256Rsa15″,
“securityTokenReference” => “KeyIdentifier”)
array(“privateKey” => $pvt_key,
certificate” => $cert)
Encryption privacy array(“encrypt” => TRUE,
“algorithmSuite” => “Basic256Rsa15″,
“securityTokenReference” => “IssuerSerial”);
array(“privateKey” => $pvt_key,
“receiverCertificate” => $pub_key))

You can build the WSPolicy and WSSecurityToken with an any mix of above features. For some scenarios you may only need timestamp with signing where as some other critical scenarios you want signing, encryption, username token and timestamp.

Here is how you build the WSSPolicy and WSSecurityToken classes using the above mentioned $sec_policies and $sec_token_options.

$policy = new WSPolicy(array("security"=> $sec_policies));

$sec_token = new WSSecurityToken($sec_token_options);

$svr = new WSService(array("policy" => $policy,
                           "securityToken" => $sec_token,
                            "actions" => $your_actions,
                           "operations" => $your_operations));

$svr->reply();

Similarly you can use the WSPolicy and WSSecurity with WSClient for the client side security. See the samples WS-Security demos and WS-Security sources.

This blog is about some of the security features shipped with WSF/PHP 1.3.2. With the next release of WSF/PHP you will have more features related to WS-Security like WS-SecureConversations, WS-Trust and use of KeyStores for encryption and signing.


© 2007 Dimuthu’s Blog | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress