Thanks for clarifying that Rob. I used your xmlseclibs.php and soap-server-wsse.php classes to verify signature from an incoming soap request, but I was not clear on how the verification happened, since I didn't actually configure the keys to be used for the verification myself. Now, I understand that the key must have been sent along the soap message itself and that the server side code (using the above mentioned .php files) must have just decrypted the signature with the public key from the request and "verified" that the signature has not been tampered.
I would like to require the users of my web service to sign certain elements of the soap message. Is there a way to specify this in the web service contract (to me this is the wsdl), so that the users know that their requests will be rejected if these elements are not signed? For instance I would like to require the users to sign the timestamp element so that I could verify its authenticity before processing the message. I'm a bit of a newbie when it comes to web services and especially web service security so I could really use some sample code and maybe a detailed explanation if that's not too inconvenient for you. Specifically how could I modify/extend your soap-server-wsse.php code so that I can verify that specific elements (timestamp for example) are signed?
I used the following modification to your soap-server-wsse.php code but it doesn't seem to be checking for signatures on the timestamp element. I've attached only the modified parts:
public function processTimestamp($refNode)
{
$Created = '';
$query = '//wswsu:Created';
$nodeset = $this->SOAPXPath->query($query);
if ($encmeth = $nodeset->item(0)) {
$Created = $encmeth->textContent;
}
if (empty($Created)) { // reject submission without timestamp
throw new Exception("Creation timestamp required.");
return FALSE;
}
// Get Expires time. When not given then never expires.
$Expires = '';
$query = '//wswsu:Expires';
$nodeset = $this->SOAPXPath->query($query);
if ($encmeth = $nodeset->item(0)) {
$Expires = $encmeth->textContent;
}
if (empty($Expires)) { // Never expires
return TRUE;
}
if (time() > strtotime($Expires)) { // Timestamp expired?
throw new Exception("Timestamp expired.");
}
return TRUE;
}
public function process() {
if (empty($this->secNode)) {
return;
}
$sigValid = FALSE;
$tsValid = FALSE;
$node = $this->secNode->firstChild;
while ($node) {
$nextNode = $node->nextSibling;
switch ($node->localName) {
case "Signature":
if ($this->processSignature($node)) {
if ($node->parentNode) {
$node->parentNode->removeChild($node);
$sigValid = TRUE;
}
}
else {
/* throw fault */
return FALSE;
}
break;
case "Timestamp": // Timestamp processing
if ($this->processTimestamp($node)) {
if ($node->parentNode) {
$node->parentNode->removeChild($node);
$tsValid = TRUE;
}
}
else {
/* throw fault */
return FALSE;
}
break;
}
$node = $nextNode;
}
$this->secNode->parentNode->removeChild($this->secNode);
$this->secNode = NULL;
if (!sigValid && !tsValid) {
return FALSE;
}
return TRUE;
}
the code for my soap server is as follows:
function my_webservice() {
$soap = new DOMDocument();
$soap->load('php://input');
$server = new SoapServer('my.wsdl', array('cache_wsdl' => WSDL_CACHE_NONE));
$s = new WSSESoapServer($soap);
$server->setClass('myService');
try {
if ($s->process()) {
$server->handle($s->saveXML());
exit;
}
}
catch (Exception $e) {
echo 'Message: ' . $e->getMessage();
}
$server->fault(8, "Invalid Signature");
}
I'm using soapUI to generate my web service request, and the code above rejects messages that don't have a signature element signed, but don't reject messages without timestamp element signed. Could you please provide some insights on what is missing in code/configuration.
Thanks for your time,
Pranesh