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

donatj 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

marcelmcz commented Feb 21, 2014

This saved me too! Thanks!

@RideLikeTheWind

This comment has been minimized.

Copy link

RideLikeTheWind 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

francescomadeo 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

zuzuleinen commented Nov 26, 2014

Thanks a lot!

@radomirmazon

This comment has been minimized.

Copy link

radomirmazon 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

kieuduygn commented May 19, 2015

Thanks very much

@sergio-bobillier

This comment has been minimized.

Copy link

sergio-bobillier 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

tblancog commented Oct 14, 2015

Nice, but how do i use this class?

@fiatux

This comment has been minimized.

Copy link

fiatux 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

moeinrahimi commented Apr 17, 2017

hi, how should i pass username and password

@sinansh

This comment has been minimized.

Copy link

sinansh commented Jun 7, 2017

how to implement?

Please share sample code?

@mirzaawais

This comment has been minimized.

Copy link

mirzaawais 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.