Non-Repudiation and Integrity are two main security issues addressed by signing a message. If you are writing a web service or a service consumer in PHP you can use the WSF/PHP toolkit to sign messages.

Here is how you can sign a SOAP request message.

// loading the keys
$my_cert = ws_get_cert_from_file("client_certificate.cert");
$my_key = ws_get_key_from_file("client_private_key.pem");
$rec_cert = ws_get_cert_from_file("server_certificate.cert");

// preparing the policy array
$sec_array = array("sign"=>TRUE,
                   "algorithmSuite" => "Basic256Rsa15",
                   "securityTokenReference" => "IssuerSerial");

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

// preparing the security_token
$sec_token = new WSSecurityToken(array("privateKey" => $my_key,
                                       "certificate" => $my_cert,
                                       "receiverCertificate" => $rec_cert));

// create the client using the policy and sec token + option ws-addressing
$client = new WSClient(array("useWSA" => TRUE,
                              "policy" => $policy,
                              "securityToken" => $sec_token));

// invoke the request				
$resMessage = $client->request($reqMessage);

Although I’ve not shown in the above code, you should always wrap the requesting code inside a try/catch block specially when there are security tokens in your message. Because whenever security test is failed the server will send a SOAP fault, and the client is expected to handle that.

Similarity you can write a service that accept only signing messages with the same policy and security tokens. But this time your security token contains the private key and the certificate of the server. And for service you create WSService instance (similarity to  WSClient) and feed these options.

// loading the keys and create the security token
$cert = ws_get_cert_from_file("server_certificate.cert");
$pvt_key = ws_get_key_from_file("server_private_key.pem")

$sec_token = new WSSecurityToken(array("privateKey" => $pvt_key,
                                       "certificate" => $cert));

// policy is declared similar to earlier example

$sec_array = array("sign" => TRUE,
                    "algorithmSuite" => "Basic256Rsa15",
                    "securityTokenReference" => "KeyIdentifier");

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

// create a WSService instance and feed the options
$svr = new WSService(array("operations" => $operations,
                           "actions" => $actions,
                           "policy" => $policy,
                           "securityToken" => $sec_token));

$svr->reply();

With this code you will only be able to sign the payload of a SOAP request. Payload is what stays inside the soap body part which contain the actual data of your business. So in most of the case, what you want is integrity of your business data, so this will satisfy your need.

But there can be situation you want to sign SOAP headers which are outside the SOAP body. SOAP headers contain meta data of the message. For an example when you sign a message, the SOAP headers could carry the signature information and the certificates.

One example of the use of signing SOAP headers is replay detection. (I’ve written about replay detection on a early blog). in SOAP, replays can be detected using ‘message id’s which are transferred through the WS-addressing SOAP headers. But interceptors can change the message ids and send it as a unique message which will eventually cause to fail the replay detection. But if you sign the ‘message id’ (i.e. WS-Addressing headers) the intruders will not be able to do the trick since they can not recreate the signature for the changed content.

Anyway you can not set your requirement to sign the soap headers, just using a PHP array as it has done in the above examples. Rather you have to declare it using a policy file compliant to WS-SecurityPolicy specification. I.e instead of using a PHP array to create a WSPolicy instance like this,

$sec_array = array("sign"=>TRUE,
                    "algorithmSuite" => "Basic256Rsa15",
                    "securityTokenReference" => "IssuerSerial");

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

You can create the WSPolicy instance directly using the policy file,

$policy_xml = file_get_contents("policy.xml");
$policy = new WSPolicy($policy_xml);

And inside the policy file, you can declare your requirements, policies. Here is how I say I want to sign all my WS-Addressing headers.

<sp:SignedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
   <sp:Header Namespace="http://www.w3.org/2005/08/addressing"/>
</sp:SignedParts>

Note that here the namespace “http://www.w3.org/2005/08/addressing” is the namespace used in the Addressing headers. So I instruct to sign headers containing that namespace in order to sign the ‘message id’ inside the WS-Addressing headers. You can visit a comlete policy file with the declarations of signed part from here.

When you are developing a Web Service, you have to think about the security aspects of your service seriously. When it comes to security in web services you have two basic choices.

  1. Transport level security - Just SOAP over HTTPS
  2. Message level security - WS-Security

See my previous blog comparing Transport level and Message level security.

If you are satisfied with the security provided by using just ‘SOAP over HTTPS’, you can get the work done by configuring your server (Apache or IIS) to enable ssl. See http://www.onlamp.com/pub/a/onlamp/2008/03/04/step-by-step-configuring-ssl-under-apache.html for an step by step guide for configure SSL in your Apache server.

If you want message level security for your application, just use WS-Security. With WSF/PHP it is even easier to implement than SOAP over HTTPS method, because you can provide the certificates programatically in PHP and no need to do further configuration.

WSF/PHP provides you two classes in line with WSService to implement an API to provide WS-Security.

  1. WSPolicy -Let you provide rules that the engine need to follow in securing the message. E.g.
    $policy = new WSPolicy(array("security"=> array("encrypt" => TRUE,
                        "algorithmSuite" => "Basic256Rsa15",
                        "securityTokenReference" => "IssuerSerial")));

    In fact you can load policies from an xml which adheres to the WS-SecurityPolicy specification.

  2. WSSecurityToken - Keeps the security tokens like certificates, keys, username, passwords which would be used when applying the rules specified in the policy. E.g.
    $sec_token = new WSSecurityToken(array("privateKey" => $pvt_key,
                                           "receiverCertificate" => $pub_key));

You can see the WS-Security in action on live from http://labs.wso2.org/wsf/php/samples/security/ and security demo ource codes.