Re: Help with soap server ws-* implementation

915 views
Skip to first unread message

Gianluca Poli

unread,
Jul 11, 2012, 4:43:27 AM7/11/12
to wse...@googlegroups.com
Hi Pranesh

i use only the username/pwd authentication in my implementation..both are sent into soap soap headers by client and server only needs to start a wsse instance and capture input, parse xml and get those username/password values here is the code i use:

client side:

    require_once ROOT_DIR . '/soap-wsse/mySoap.php';

    $sClient = mySoap::getInstance();
    $sClient->addUserToken(USER, md5(PWD)); //here i add the user token to be put into soap headers
    try {

        print($sClient->inserimento(ID, rawurlencode('evento')));
  
          echo "\n";
          echo "\nLast Request Headers: \n";
          echo "\n";
          var_dump($sClient->__getLastRequestHeaders());
          echo "\n";
          echo "Last Response Headers: \n";
          var_dump($sClient->__getLastResponseHeaders());
          echo "\n";
          echo "Last Response:\n";
          var_dump($sClient->__getLastResponse());
          echo "\n";
          print "<pre>\n";
          print "Request :".htmlspecialchars($sClient->__getLastRequest()) ."\n";
          print "Response:".htmlspecialchars($sClient->__getLastResponse())."\n";
          print "</pre>";

    } catch (SoapFault $fault) {
        echo "\nSOAP ERROR: " . htmlspecialchars(rawurldecode($fault));
        echo "\n";
        echo "\nLast Request Headers: \n";
        echo "\n";
        var_dump($sClient->__getLastRequestHeaders());
        echo "\n";
        echo "\n";
        echo "\nLast Request: \n";
        echo "\n";
        var_dump($sClient->__getLastRequest());
        echo "\n";
        echo "Last Response Headers:\n";
        var_dump($sClient->__getLastResponseHeaders());
        echo "\n";
        echo "LastResponse\n";
        var_dump($sClient->__getLastResponse());
    }

SERVER SIDE:

        Ini_set('soap.wsdl_cache_enabled', '0');

        $this->server = new SoapServer('PATH-TO-FILE.WSDL');
        $this->soap = new DOMDocument();
        $this->soap->load('php://input'); //loads the xml stream from client
        $this->user = $this->soap->getElementsByTagName('Username')->item(0)->nodeValue; //use the DOM parser to read the username field sent by client
        $this->passwd = $this->soap->getElementsByTagName('Password')->item(0)->nodeValue; //use the DOM parser to read the password field sent by client
        

        $s = new WSSESoapServer($this->soap);
        
        

            try {
                if ($s->process()) {   //that call processes the wsdl function called by client

                    $this->server->setObject($this);
                    $result = $s->saveXML();
                    $this->server->handle($result);
                    exit;
                }
            } catch (Exception $e) {
                echo "server exception: " . $e;
            }
            $server->fault(8, "Invalid Signature");
        

Hope this code helps u out, it may contain some syntax error due to copy/paste...

Cya

Gianluca



On Tuesday, July 10, 2012 8:35:51 PM UTC+2, Pranesh Vadhirajan wrote:
Hello All,

I am trying to enhance my implementation of a php soap server to include ws-* security.  I am looking to find some code snippets that show how to validate the signature on certain soap elements with keys from a user against my keystore.  The current implementation of the soap server is running using SSL and authenticates a user's request based on a key provided by the user.  However, I would like to enhance this by also requiring users to sign the following elements : Message ID, timestamp and service request (the body of the message, I believe).  I have been trying to find code snippets that would show me how to validate user signatures for the above elements against my keystore based on their public key information.  So far all I've been find are examples on how to sign and make web service requests as a client.  The code snippets that I've found,  which use xmlseclibs.php and soap-server-wsse.php don't seem to have any server side validation of keys against a keystore, if I have understood them correctly. 

Thanks,
Pranesh 

Pranesh Vadhirajan

unread,
Jul 11, 2012, 10:19:25 AM7/11/12
to wse...@googlegroups.com
Thanks Gianluca for the reply.  My current server side implementation looks very similar to your posted code snippet.  What I'm looking to do is to modify "soap-server-wsse.php" so that when I call the "process()" function, it would verify that certain elements (namely Message ID, timestamp and message body) are signed based on public keys that I have in my keystore.  I have not been able to find such code snippets anywhere.  

Pranesh Vadhirajan

unread,
Jul 11, 2012, 10:53:47 AM7/11/12
to wse...@googlegroups.com
I think I may have some concepts mixed up here also.  In order to verify authenticity of a signature, the server has to decrypt the signature using a public key.  However, this public key doesn't have to be generated by me directly from a keystore, correct?  I.e. how does signature verification work in SOAP?  Is the public key sent along the SOAP message and  the server side simply validates the authenticity by decrypting the signed elements using the public key in the message?  

If I require my clients to sign "message id", how should this be specified in the web service contract?  I.e how should this be described in the wsdl (if this is where it should be done) ?  I would really appreciate it if someone explained the concept here.

Thanks,
Pranesh

Gianluca Poli

unread,
Jul 11, 2012, 11:10:08 AM7/11/12
to wse...@googlegroups.com
Oh, well, i forgot to post some of my modified mySoap class that is used by the client :P

so in mySoap.php  i added this class that implements the singleton pattern

CLIENT SIDE:

public static function getInstance() {
        if (self::$instance == null) {
            $c = __CLASS__;
            self::$instance = new $c($wsdl, array('trace'=>1,"exceptions" => 1,'cache_wsdl' => WSDL_CACHE_NONE));
        }

        return self::$instance;
    } //so yes, u have to tell the mySoap class to retrieve his wsdl file... 

the certificate/ private key part follows in same file..  here's the code:

function __doRequest($request, $location, $saction, $version, $one_way = 0) {
        $this->CERT_FILE = ROOT_DIR . '/PATH-TO-SERVER-CERTIFICATE.pem'; 
        $this->PRIVATE_KEY = ROOT_DIR . '/PATH-TO-SERVER-PRIVATE_KEY.key.org';
        $doc = new DOMDocument('1.0');
        $doc->loadXML($request);
        
        $objWSSE = new WSSESoap($doc);
        
        /* Sign all headers to include signing the WS-Addressing headers */
        $objWSSE->signAllHeaders = TRUE;

        $objWSSE->addTimestamp();
        $objWSSE->addUserToken($this->_username, $this->_password, $this->_digest);

        /* create new XMLSec Key using RSA SHA-1 and type is private key */
        $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private'));

        /* load the private key from file - last arg is bool if key in file (TRUE) or is string (FALSE) */
        $objKey->loadKey($this->PRIVATE_KEY, TRUE);

        /* Sign the message - also signs appropraite WS-Security items */
        $objWSSE->signSoapDoc($objKey);

        /* Add certificate (BinarySecurityToken) to the message and attach pointer to Signature */
        $token = $objWSSE->addBinaryToken(file_get_contents($this->CERT_FILE));
        $objWSSE->attachTokentoSig($token);
        
        $request = $objWSSE->saveXML();
        //print "Request :\n".htmlspecialchars(rawurldecode($request)) ."\n"; 
        
        return parent::__doRequest($request, $location, $saction, $version);
    }
}

so i use the server private key to encrypt all messages, but now u let me think about it i should use server's public key to encrypt that is a non reversible function and server should use his private key to decrypt the public-key crypted message... i am prolly doing it wrong.. 

Pranesh Vadhirajan

unread,
Jul 11, 2012, 2:21:00 PM7/11/12
to wse...@googlegroups.com
In your client implementation, in the doRequest function you are signing elements with your private key and the server side decrypts with the public key.  I am trying to find out how the server gets the public key.  Is the public key sent with the soap message itself?  If so, can anyone (not just the server), decrypt the signature by just intercepting the public key within the soap message? If so, then how are secret authentication parameters (username, password, etc) encrypted?  Obviously only the server should have the private key to decrypt, while the client can encrypt this with the public key.  

I am really trying to understand how the encryption/decryption process works relative to SOAP messages and web service security so that I can get a better handle on how to extend the soap-server-wsse.php for my server side implementation.  

Rob Richards

unread,
Jul 11, 2012, 3:17:52 PM7/11/12
to wse...@googlegroups.com, Pranesh Vadhirajan
I think you are confusing signatures and encrypted data. In most cases it's fine to send the public key used for the signature along with the call because all that is doing is verifying that the owner of the key is the one who signed the message and that it hasnt been tampered. The public key can be verified in a number of ways, i.e. a signed keys from a certified autority, issued by server organization, fingerprint matches, etc...

In many live scenarios, I have seen the organization owning the server issuing the client keys to be used by the client.

In regards to encrypting data, the client would use the public key of the server. This way only the holder of the private key (the server) can decrypt the data. The public key can be obtained in a number of ways. It could be given to the client to use with their application, it can be derived from the SSL connection, etc..

Rob

Pranesh Vadhirajan

unread,
Jul 11, 2012, 3:47:37 PM7/11/12
to wse...@googlegroups.com, Pranesh Vadhirajan
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

Pranesh Vadhirajan

unread,
Jul 17, 2012, 1:13:33 PM7/17/12
to wse...@googlegroups.com
When sending a signed request to the server, I noticed that the server doesn't actually go into the while loop in the following code:
Has anybody else noticed this?  Is there a way to check timestamp and signature that someone can post a code snippet?  

When I send an unsiged document, the function returns because of this statement: 

if (empty($this->secNode)) {
            return;
        }

Therefore, I know that when a document is signed, this->secNode is not empty because the function returns TRUE.  However, it seems this->secNode->firstChild seems to not exist.  Can some explain if this is true and if so, is this not the correct thing to check when validating a signature?  It seems that the function above cannot really validate a signature if I'm correct.  I would request that someone who has figured out a way to validate the signature, to please post a code snippet.

Thanks,
Pranesh
Reply all
Reply to author
Forward
0 new messages