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 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 marcelmcz commented Feb 21, 2014

This saved me too! Thanks!

@RideLikeTheWind

This comment has been minimized.

Copy link

@RideLikeTheWind 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 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 zuzuleinen commented Nov 26, 2014

Thanks a lot!

@radomirmazon

This comment has been minimized.

Copy link

@radomirmazon 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 kieuduygn commented May 19, 2015

Thanks very much

@sergio-bobillier

This comment has been minimized.

Copy link

@sergio-bobillier 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 tblancog commented Oct 14, 2015

Nice, but how do i use this class?

@fiatux

This comment has been minimized.

Copy link

@fiatux 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 moeinrahimi commented Apr 17, 2017

hi, how should i pass username and password

@sinansh

This comment has been minimized.

Copy link

@sinansh sinansh commented Jun 7, 2017

how to implement?

Please share sample code?

@mirzaawais

This comment has been minimized.

Copy link

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

@DaruinHerrera

This comment has been minimized.

Copy link

@DaruinHerrera DaruinHerrera commented Nov 25, 2020

Yes, you would like to sign with an ssl certificate, how would the process be

@Turin86

This comment has been minimized.

Copy link
Owner Author

@Turin86 Turin86 commented Nov 26, 2020

Yes, you would like to sign with an ssl certificate, how would the process be

Call the WSSoapClient contructor (does not override the SoapClient constructor) with the appropiate parameters.
https://www.php.net/manual/en/soapclient.soapclient.php#120888

@DaruinHerrera

This comment has been minimized.

Copy link

@DaruinHerrera DaruinHerrera commented Nov 26, 2020

Sí, le gustaría firmar con un certificado ssl, ¿cómo sería el proceso?

Llame al constructor WSSoapClient (no anula el constructor SoapClient) con los parámetros apropiados.
https://www.php.net/manual/en/soapclient.soapclient.php#120888

You will have an example of consumption and use this in the header that I get from the SOAPUI

`soapenv:Header
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-B89D7F960777898D89160631426090014">
wsse:Username########</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">#####</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">sBRMmGjIObO6tX0KjOScFA==</wsse:Nonce>
wsu:Created2020-11-25T16:24:20.900Z</wsu:Created>
</wsse:UsernameToken>
<wsu:Timestamp wsu:Id="TS-B89D7F960777898D89160631426090013">
wsu:Created2020-11-25T16:24:20.899Z</wsu:Created>
wsu:Expires2020-11-25T16:25:20.899Z</wsu:Expires>
</wsu:Timestamp>
<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1" wsu:Id="X509-B89D7F960777898D8916063142608618">########CERTIFICA######################/s
</wsse:BinarySecurityToken>
<ds:Signature Id="SIG-B89D7F960777898D89160631426089212" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
ds:SignedInfo<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="soapenv v1" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#id-B89D7F960777898D89160631426086111">
ds:Transforms<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="v1" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
ds:DigestValueURDCHKG444RS#/SHsbPH+oRVOS53qjc=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
ds:SignatureValueybjaTDbfjaxY8lCePbN43aIlJ4AINQeGomIkHSbCoYxH+JUQxeIhUSwo+3VbYG9BCQAX/U6bgyup
00KSASncniw3qWWSAE9z1d/73zwgAz88ykIuMSxVEC70qc5FoxLXPvQZad15SQjOkyjfDFG83ZTu
YrNLX11bBQfYjhHhfMQFn+####AAAdlJ2fcRpzPBsSo5VzZtKiNrkbV
1hI/##$$$fFSASASq8tK6RJxSgzCZmmcDz
3JZA652vHyyT4Sws/ChtSaY1oOS3v0a6LkQJ1w==
</ds:SignatureValue>
<ds:KeyInfo Id="KI-B89D7F960777898D8916063142608619">
<wsse:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1" wsu:Id="STR-B89D7F960777898D89160631426086110" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd">
<wsse:Reference URI="#X509-B89D7F960777898D8916063142608618" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>

</soapenv:Header>`

@andymorenoing

This comment has been minimized.

Copy link

@andymorenoing andymorenoing commented Jan 8, 2021

Sí, le gustaría firmar con un certificado ssl, ¿cómo sería el proceso?

Llame al constructor WSSoapClient (no anula el constructor SoapClient) con los parámetros apropiados.
https://www.php.net/manual/en/soapclient.soapclient.php#120888

Tendrás un ejemplo de consumo y lo usarás en el encabezado que obtengo de la SOAPUI

`soapenv: Header
<wsse: Security xmlns: wsse =" http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd "xmlns: wsu =" http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd ">
<wsse: UsernameToken wsu: Id =" UsernameToken-B89D7F960777898D89160631426090014 ">
wsse: Nombre de usuario ######## </ wsse: Nombre de usuario>
<wsse: Tipo de contraseña = " http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile -1.0 # PasswordText "> ##### </ wsse: Password>
<wsse: Nonce EncodingType =" http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap- message-security-1.0 # Base64Binary "> sBRMmGjIObO6tX0KjOScFA == </ wsse: Nonce>
wsu: Created2020-11-25T16: 24: 20.900Z </ wsu: Created>
</ wsse: UsernameToken>
<wsu: Timestamp wsu: Id = "TS-B89D7F960777898D89160631426090013">
wsu: Created2020-11-25T16: 24: 20.899Z </ wsu: Creado>
wsu: Expires2020-11-25T16: 25: 20.899Z </ wsu: Expires>
</ wsu: Timestamp>
<wsse: BinarySecurityToken EncodingType = " http://docs.oasis-open.org/wss /2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary "ValueType =" http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509- token-profile-1.0 # X509PKIPathv1 "wsu: Id =" X509-B89D7F960777898D8916063142608618 "> ######## CERTIFICA ###################### / s
</ wsse: BinarySecurityToken>
<ds:ID de firma = "SIG-B89D7F960777898D89160631426089212" xmlns: ds = "http://www.w3.org/2000/09/xmldsig# ">
ds: SignedInfo <ds: CanonicalizationMethod Algorithm =" http://www.w3.org/2001/10/xml-exc-c14n# ">
<ec: InclusiveNamespaces PrefixList = "soapenv v1" xmlns: ec = " http://www.w3.org/2001/10/xml-exc-c14n#" />
</ ds: CanonicalizationMethod> <ds: SignatureMethod Algorithm = " http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<ds: Reference URI = "# id-B89D7F960777898D89160631426086111">
ds: Transforms <ds: Transform Algorithm = " http: // www.w3.org/2001/10/xml-exc-c14n # ">
<ec: InclusiveNamespaces PrefixList =" v1 "xmlns: ec =" http://www.w3.org/2001/10/xml-exc- c14n # "/>
</ ds:Transformar> </ ds: Transformaciones> <ds: DigestMethod Algorithm = " http://www.w3.org/2000/09/xmldsig#sha1" />
DS: DigestValueURDCHKG444RS # / SHsbPH + oRVOS53qjc = </ ds: DigestValue>
</ ds: Reference>
</ ds: SignedInfo>
ds: SignatureValueybjaTDbfjaxY8lCePbN43aIlJ4AINQeGomIkHSbCoYxH + + JUQxeIhUSwo 3VbYG9BCQAX / U6bgyup
00KSASncniw3qWWSAE9z1d / 73zwgAz88ykIuMSxVEC70qc5FoxLXPvQZad15SQjOkyjfDFG83ZTu
YrNLX11bBQfYjhHhfMQFn + #### AAAdlJ2fcRpzPBsSo5VzZtKiNrkbV
1ALT / ## $ $$ fFSASASq8tK6RJxSgzCZmmcDz
3JZA652vHyyT4Sws / ChtSaY1oOS3v0a6LkQJ1w ==
</ ds: SignatureValue>
<ds: KeyInfo Id = "KI-B89D7F960777898D8916063142608619">
<wsse: SecurityTokenReference wsse11: TokenType =" http://docs.oasis-open.org/wss/2004 /01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1 "wsu: Id ="STR-B89D7F960777898D89160631426086110 "xmlns: wsse11 ="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd ">
<wsse: Reference URI =" # X509-B89D7F960777898D8916063142608618 "ValueType =" http: //docs.oasis- open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1 "/>
</ wsse: SecurityTokenReference>
</ ds: KeyInfo>
</ ds: Signature>
</ wsse: Seguridad>

</ soapenv: Encabezado> `

you got an example with the certificate too?

@nbgithub123

This comment has been minimized.

Copy link

@nbgithub123 nbgithub123 commented Feb 1, 2021

Hi anyone knows how to implement ws security with a certificate X509 to a soap request instead? need help . thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment