Skip to content

Instantly share code, notes, and snippets.

@Turin86
Forked from johnkary/WSSoapClient.php
Last active April 24, 2023 19:37
Show Gist options
  • Save Turin86/5569152 to your computer and use it in GitHub Desktop.
Save Turin86/5569152 to your computer and use it in GitHub Desktop.
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 <[email protected]>
* @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);
}
}
@fiatux
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

@sinansh
Copy link

sinansh commented Jun 7, 2017

how to implement?

Please share sample code?

@mirzaawais
Copy link

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

@DaruinHerrera
Copy link

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

@Turin86
Copy link
Author

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
Copy link

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
Copy link

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
Copy link

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

@jgarciafinanciat
Copy link

Hello, can you please help me what should it be called?
I have PHP 8.0.2

@jgarciafinanciat
Copy link

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

Hello, can you please help me what should it be called?
I have PHP 8.0.2

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