Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
WS-Security for PHP SoapClient
<?php
/**
* This class can add WSSecurity authentication support to SOAP clients
* implemented with the PHP 5 SOAP extension.
*
* It extends the PHP 5 SOAP client support to add the necessary XML tags to
* the SOAP client requests in order to authenticate on behalf of a given
* user with a given password.
*
* This class was tested with Axis, WSS4J servers and CXF.
*
* @author Roger Veciana - http://www.phpclasses.org/browse/author/233806.html
* @author John Kary <johnkary@gmail.com>
* @author Alberto Martínez - https://gist.github.com/Turin86/5569152
* @see http://stackoverflow.com/questions/2987907/how-to-implement-ws-security-1-1-in-php5
*/
class WSSoapClient extends SoapClient
{
private $OASIS = 'http://docs.oasis-open.org/wss/2004/01';
/**
* WS-Security Username
* @var string
*/
private $username;
/**
* WS-Security Password
* @var string
*/
private $password;
/**
* WS-Security PasswordType
* @var string
*/
private $passwordType;
/**
* Set WS-Security credentials
*
* @param string $username
* @param string $password
* @param string $passwordType
*/
public function __setUsernameToken($username, $password, $passwordType)
{
$this->username = $username;
$this->password = $password;
$this->passwordType = $passwordType;
}
/**
* Overwrites the original method adding the security header.
* As you can see, if you want to add more headers, the method needs to be modified.
*/
public function __call($function_name, $arguments)
{
$this->__setSoapHeaders($this->generateWSSecurityHeader());
return parent::__call($function_name, $arguments);
}
/**
* Generate password digest.
*
* Using the password directly may work also, but it's not secure to transmit it without encryption.
* And anyway, at least with axis+wss4j, the nonce and timestamp are mandatory anyway.
*
* @return string base64 encoded password digest
*/
private function generatePasswordDigest()
{
$this->nonce = mt_rand();
$this->timestamp = gmdate('Y-m-d\TH:i:s\Z');
$packedNonce = pack('H*', $this->nonce);
$packedTimestamp = pack('a*', $this->timestamp);
$packedPassword = pack('a*', $this->password);
$hash = sha1($packedNonce . $packedTimestamp . $packedPassword);
$packedHash = pack('H*', $hash);
return base64_encode($packedHash);
}
/**
* Generates WS-Security headers
*
* @return SoapHeader
*/
private function generateWSSecurityHeader()
{
if ($this->passwordType === 'PasswordDigest')
{
$password = $this->generatePasswordDigest();
$nonce = sha1($this->nonce);
}
elseif ($this->passwordType === 'PasswordText')
{
$password = $this->password;
$nonce = sha1(mt_rand());
}
else
{
return '';
}
$xml = '
<wsse:Security SOAP-ENV:mustUnderstand="1" xmlns:wsse="' . $this->OASIS . '/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>' . $this->username . '</wsse:Username>
<wsse:Password Type="' . $this->OASIS . '/oasis-200401-wss-username-token-profile-1.0#' . $this->passwordType . '">' . $password . '</wsse:Password>
<wsse:Nonce EncodingType="' . $this->OASIS . '/oasis-200401-wss-soap-message-security-1.0#Base64Binary">' . $nonce . '</wsse:Nonce>';
if ($this->passwordType === 'PasswordDigest')
{
$xml .= "\n\t" . '<wsu:Created xmlns:wsu="' . $this->OASIS . '/oasis-200401-wss-wssecurity-utility-1.0.xsd">' . $this->timestamp . '</wsu:Created>';
}
$xml .= '
</wsse:UsernameToken>
</wsse:Security>';
return new SoapHeader(
$this->OASIS . '/oasis-200401-wss-wssecurity-secext-1.0.xsd',
'Security',
new SoapVar($xml, XSD_ANYXML),
true);
}
}
@donatj

This comment has been minimized.

Copy link

commented Nov 6, 2013

Man this saved the day, I tried the original and it wasn't working for my needs but your fork saved me. Thanks.

@marcelmcz

This comment has been minimized.

Copy link

commented Feb 21, 2014

This saved me too! Thanks!

@RideLikeTheWind

This comment has been minimized.

Copy link

commented Jun 25, 2014

Thank you! This has absolutely saved my hours of work with another framework that was truly outdated. Cheers!

@francescomadeo

This comment has been minimized.

Copy link

commented Jul 4, 2014

excuse me but for mutual authentication with digital signatures as you can do?

@zuzuleinen

This comment has been minimized.

Copy link

commented Nov 26, 2014

Thanks a lot!

@radomirmazon

This comment has been minimized.

Copy link

commented Apr 7, 2015

The nonce is represent by base64, but I don't see any decode ? You use it from $nonce. I expected something like this: base64($nonce). Does it work ?

@kieuduygn

This comment has been minimized.

Copy link

commented May 19, 2015

Thanks very much

@sergio-bobillier

This comment has been minimized.

Copy link

commented May 20, 2015

Thanks a lot. I don't know why some people keeps using this outdated and inconvenient standard. They should move to RESTful Webservices >.<

@tblancog

This comment has been minimized.

Copy link

commented Oct 14, 2015

Nice, but how do i use this class?

@fiatux

This comment has been minimized.

Copy link

commented Jun 18, 2016

I still get the following error, any ideas? @Turin86 @johnkary

MustUnderstand headers:[{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood

@moeinrahimi

This comment has been minimized.

Copy link

commented Apr 17, 2017

hi, how should i pass username and password

@sinansh

This comment has been minimized.

Copy link

commented Jun 7, 2017

how to implement?

Please share sample code?

@mirzaawais

This comment has been minimized.

Copy link

commented Jul 3, 2017

How to see last request? I have put __getLastRequest in your class and return like return parent::__getLastRequest(); but it returns null

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.