Signing SOAP Headers In PHP Web Services

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.

This entry was posted in php, security, Tutorial/Guide, wsf/php, wso2 and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *