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
  • Star 39 You must be signed in to star a gist
  • Fork 20 You must be signed in to fork a gist
  • 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 <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);
}
}
@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