I am trying to send signed soap requests but seem to be missing something.
I tried the wso2 library but that won't work on ubuntu 14.04 with php7.0
I then tried this one : https://github.com/LinioIT/wse-php but keep getting
ERR_025: Verification failure: No signature in the WS-Security message for the configured soap actor/role ""! in SoapClient->__call()
I tested this in soapUI and got it working but can't translate it to PHP
Here's my code:
<?php
$wsdl = "/***/GeefOnderneming.wsdl";
$cafile = "/var/www/src/cert/CA_cat_inv.pem";
$location = "https://***.be/GeefOndernemingDienst-02.00";
$uri = "http://***.be";
$local_cert = "/var/www/src/cert/cert_priv_pub.pem";
$soap = new SoapWsController($wsdl, [
'local_cert' => $local_cert,
'cafile' => $cafile,
'location' => $location,
'uri' => $uri,
'connection_timeout' => 10,
]);
$theResponse = $soap->geefOnderneming(
$payload
);
SoapWsController is extended from the default SoapClient:
I am not sure wether the define's are correct, I've tried a lot of options
namespace Drupal\vlaio_dossiers\Controller;
use DOMDocument;
use SoapClient;
use XMLSecurity\WSSESoap;
use XMLSecurity\XMLSecurityKey;
define('PRIVATE_KEY', '/***/key_ecc_private.pem');
define('SERVICE_CERT', '/***/cert_priv_pub.pem');
define('CERT_FILE', '/***/certificate.pem');
class SoapWsController extends SoapClient {
public function __doRequest($request, $location, $saction, $version,$one_way = NULL)
{
$doc = new DOMDocument('1.0');
$doc->loadXML($request);
$objWSSE = new WSSESoap($doc);
/* add Timestamp with no expiration timestamp */
$objWSSE->addTimestamp();
/* create new XMLSec Key using AES256_CBC and type is private key */
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private'));
/* load the private key from file - last arg is bool if key in file (true) or is string (false) */
$objKey->loadKey(PRIVATE_KEY, true);
/* Sign the message - also signs appropiate WS-Security items */
$options = array("insertBefore" => false);
$objWSSE->signSoapDoc($objKey, $options);
/* Add certificate (BinarySecurityToken) to the message */
$token = $objWSSE->addBinaryToken(file_get_contents(CERT_FILE));
/* Attach pointer to Signature */
$objWSSE->attachTokentoSig($token);
$objKey = new XMLSecurityKey(XMLSecurityKey::AES256_CBC);
$objKey->generateSessionKey();
$siteKey = new XMLSecurityKey(XMLSecurityKey::RSA_OAEP_MGF1P, array('type' => 'public'));
$siteKey->loadKey(SERVICE_CERT, true, true);
$options = array("KeyInfo" => array("X509SubjectKeyIdentifier" => true));
$objWSSE->encryptSoapDoc($siteKey, $objKey, $options);
$retVal = parent::__doRequest($objWSSE->saveXML(), $location, $saction, $version);
$doc = new DOMDocument();
$doc->loadXML($retVal);
$options = array("keys" => array("private" => array("key" => PRIVATE_KEY, "isFile" => true, "isCert" => false)));
$objWSSE->decryptSoapDoc($doc, $options);
return $doc->saveXML();
}
}
I have a similar problem as yours.
I finally solve it by using https://github.com/robrichards/xmlseclibs.
Please ensure that you are using the latest version of xmlseclibs.
Related
I am trying to add WSSE Security headers to a SOAP XML message that is created from PHP's SoapServer::handle(). This should be done using SoapServer::addSoapHeaders(new SoapHeaders(...)), but I am unsure how to set specific WSSE security headers to the response by using robrichards/wse-php package.
Firstly the SoapServer is created. Then the incoming request gets handled, which return some stdClass with data that the handle() function presumably automatically converts to a XML SOAP envelope. This is wrapped in Laravel's Illuminate\Http\Response object and returned.
ini_set('soap.wsdl_cache_enabled', 0);
ini_set('soap.wsdl_cache_ttl', 0);
ini_set('default_socket_timeout', 80);
header("Connection: close");
$soap = $this->createSoapServer();
ob_start();
// Response automatically becomes a XML, because of soap->handle() from PHP's SoapServer.
$response = new Response($soap->handle($xml_request), 200);
$response->header('Content-Type', 'text/xml');
return $response;
Within the createSoapServer() function I create a SoapServer and want to add WSSE Security headers to the SoapServer using addSoapHeaders(). The headers I need to add are all present withing an empty soap envelope in the headers_xml variable $headers_xml = $objWSSE->saveXML();.
I don't know how to get these headers separately.
I wish to know how to add these headers to the XML response created by the SoapServer. I should be able to add them using addSoapHeaders(), though I do not know how.
private function createSoapServer($soap_settings = []) {
$soap_settings = $this->assembleSoapSettings($soap_settings);
$wsdl_path = $soap_settings['wsdl_path'];
// Set soap's own options
$soap_settings['soap_options'] = array_merge([
WSDL_CACHE_NONE,
SOAP_SINGLE_ELEMENT_ARRAYS,
'trace' => !$this->isProduction,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'use' => SOAP_LITERAL,
'connection_timeout' => 80,
'soap_version' => SOAP_1_2,
], $soap_settings['soap_options']);
$soap = new SoapServer($wsdl_path,
array_merge([
'location' => $soap_settings['soap_location'],
'local_cert' => $soap_settings['ssl_cert_path'],
'passphrase' => $soap_settings['ssl_cert_password'],
], $soap_settings['soap_options'])
);
// Sets the server php class where the incoming request gets handled.
$soap->setClass($service_server);
// Retrieve empty XML envenlope to set headers in.
// NOTICE: This is not the correct approach,
$request = file_get_contents(app_path('Connect/Register/empty_soap.xml'));
$options = $this->soapclient_options;
$dom = new DOMDocument('1.0');
$dom->loadXML($request);
$objWSA = new WSASoap($dom, WSASoap::WSANS_2005);
Log::channel('soap-response')->info("Hit 3");
/** Add Addressing */
$objWSA->addFrom($options['wsa_addressing_from']);
$objWSA->addTo($options['wsa_addressing_to']);
$objWSA->addAction($options['wsaAction']);
/** Set needed soap header settings */
$objWSA->addMessageID();
$dom = $objWSA->getDoc();
/* Sign all headers to include signing the WS-Addressing headers */
$objWSSE = new WSSESoap($dom);
$objWSSE->signAllHeaders = true;
$objWSSE->addTimestamp();
/* create new XMLSec Key using RSA SHA256 and type is private key */
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'private'));
/* load the private key from file*/
if (isset($options['ssl_private_key_passphrase'])) {
$objKey->passphrase = $options['ssl_private_key_passphrase'];
}
$objKey->loadKey($options['ssl_private_key_path'], true);
/* Sign the message - also signs appropraite WS-Security items */
$objWSSE->signSoapDoc($objKey,
[
'algorithm' => XMLSecurityDSig::SHA256,
'insertBefore' => false,
]
);
/* Add certificate (BinarySecurityToken) to the message and attach pointer to Signature */
$token = $objWSSE->addBinaryToken(file_get_contents($options['ssl_cert_path']));
$objWSSE->attachTokentoSig($token);
/** NOTICE: Problem here! How to get correct type of headers to put into 'addSoapHeaders' of PHP's SoapServer */
$headers_xml = $objWSSE->saveXML();
$soap->addSoapHeaders(new SoapHeader("ns", $headers_xml, "value"));
return $soap;
}
(Please tell if this question is badly formatted or missing information, as this is my first time writing.)
I need to build php classes from a WSDL that is behind basic auth.
It has tons of namespaces so it looks burdensome to do this by hand.
I have tried a few tools but looks like the auth session isn't presistent.
$options = array(
'login' => $username,
'password' => $password,
);
$client = new SoapClient($wsdl, $options);
Yes, it works! I tried in a solution that I was building and it connects to my customer WS which is with HTTP Basic Auth.
HTTP Auth works with SOAP Client, however you cannot access password protected WSDL files
See https://bugs.php.net/bug.php?id=27777
Using built in SOAP client, you should have something like this:
$options = array(
'login' => $username,
'password' => $password,
);
$client = new SoapClient($wsdl, $options);
I solved this by using the lib nusoap. See if it helps
$params = array(
"param" => "value"
);
$soap_client = new nusoap_client($wsdl_url, true);
$soap_client->setCredentials(USER_SERVICE, PASS_SERVICE, 'basic');
$soap_client->soap_defencoding = 'UTF-8'; //Fix encode erro, if you need
$soap_return = $soap_client->call("method_name", $params);
How about this solution:
Download the WSDL and save into a local file
Create SoapClient with the local file
Something like this (in a simplified version) :
class MySoap {
private $WSDL = 'https://secure-wsdl.url?wsdl';
private $USERNAME = 'dummy';
private $PASSWORD = 'dummy';
private $soapclient;
private function localWSDL()
{
$local_file_name = 'local.wsdl';
$local_file_path = 'path/to/file/'.$local_file_name;
// Cache local file for 1 day
if (filemtime($local_file_path) < time() - 86400) {
// Modify URL to format http://[username]:[password]#[wsdl_url]
$WSDL_URL = preg_replace('/^https:\/\//', "https://{$this->USERNAME}:{$this->PASSWORD}#", $this->WSDL);
$wsdl_content = file_get_contents($WSDL_URL);
if ($wsdl_content === FALSE) {
throw new Exception("Download error");
}
if (file_put_contents($local_file_path, $wsdl_content) === false) {
throw new Exception("Write error");
}
}
return $local_file_path;
}
private function getSoap()
{
if ( ! $this->soapclient )
{
$this->soapclient = new SoapClient($this->localWSDL(), array(
'login' => $this->USERNAME,
'password' => $this->PASSWORD,
));
}
return $this->soapclient;
}
public function callWs() {
$this->getSoap()->wsMethod();
}
}
It works for me :)
This is Simple example to authenticate webservice using soapClient
$apiauth =array('UserName'=>'abcusername','Password'=>'xyzpassword','UserCode'=>'1991');
$wsdl = 'http://sitename.com/service.asmx?WSDL';
$header = new SoapHeader('http://tempuri.org/', 'AuthHeader', $apiauth);
$soap = new SoapClient($wsdl);
$soap->__setSoapHeaders($header);
$data = $soap->methodname($header);
This code internally parse header as follow
<soap:Header>
<AuthHeader xmlns="http://tempuri.org/">
<UserName>abcusername</UserName>
<Password>xyzpassword</Password>
<UserCode>1991</UserCode>
</AuthHeader>
</soap:Header>
i’ve been trying to resolve this issue, but from what i understand, soap client connections to ssl+httpauth web services are more pain. I’ve googled and from what i understand, with my problem being solved, you can use the example below to solve your problem too(by using HttpAuth infos in both url and soapClient setup).
$username="test";
$password="test";
$url = "https://".urlencode($username).":".urlencode($password)."#example.com/service.asmx?WSDL";
$context = stream_context_create([
'ssl' => [
// set some SSL/TLS specific options
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
]]);
$client = new SoapClient($url, [
'location' => $url,
'uri' => $url,
'stream_context' => $context,
'login' => $username,
'password' => $password
]);
$params=array(
'operation'=>’arguments',
'and’=>'other bull',
'goes’=>'here'
);
$response = $client->__soapCall('OperationName', array($params));
Context / What I want :
I'm facing an issue while calling a Webservice with SOAP. Here's an image the relevant part of the WS I want to call :
(I voluntarily hide the namespace part, not relevant here)
I want to send data through 'Demande_de_mot_de_passe' function and catch result from this request.
In the code below, this request is correct (the name of the function is good), I guess the problem is the formatting of the data I want to send. The call of the function is made with this part :
$client->Demande_de_mot_de_passe($soapVar);
What I've tried :
Here's the relevant part of the code I've tried ( I voluntarily change values of data but nothing else. There is no typo error with the brackets, it close the function and the class I didn't put here to keep the relevant part) :
$client = new \SoapClient('URL_OF_THE_WS?WSDL', array(
'trace' => 1,
'encoding' => 'UTF-8',
'soap_version' => SOAP_1_1,
'classmap' => array('Demande_de_mot_de_passe_Input' => 'Demande_de_mot_de_passe_Input')
));
$donnesUtilisateur = new Demande_de_mot_de_passe_Input;
$donnesUtilisateur->Code_societe = '000';
$donnesUtilisateur->Ident_type = 'A';
$donnesUtilisateur->Ident_code = 'xxxxxx';
$donnesUtilisateur->Dat_demande = '00000000';
$donnesUtilisateur->Adr_mail = 'xxxxxx';
$donnesUtilisateur->Adr_cpos = 'xxxxxx';
$donnesUtilisateur->Nom = 'xxxxxx';
$donnesUtilisateur->Prenom = 'xxxxxx';
$donnesUtilisateur->Dat_naiss = '00000000';
$namespace = 'URL_OF_NAMESPACE';
$soapVar = new \SoapVar($donnesUtilisateur, SOAP_ENC_OBJECT,'Demande_de_mot_de_passe_Input', $namespace);
$result = $client->Demande_de_mot_de_passe($soapVar);
print_r($result);
}
}
class Demande_de_mot_de_passe_Input {
public $Code_societe;
public $Ident_type;
public $Ident_code;
public $Dat_demande;
public $Adr_cpos;
public $Adr_mail;
public $Nom;
public $Prenom;
public $Dat_naiss;
}
I've also tried with passing array of casting an object with the array like this (without success) :
$donnesUtilisateur = [
'Code_societe' => '000',
'Ident_type' => 'A',
'Ident_code' => 'xxxxxx',
'Dat_demande' => '00000000',
'Adr_cpos' => 'xxxxxx',
'Adr_mail' => 'xxxxxx',
'Nom' => 'xxxxxx',
'Prenom' => 'xxxxxx',
'Dat_naiss' => '00000000',
];
and :
$donnesUtilisateur = (object) [
'Code_societe' => '000',
'Ident_type' => 'A',
'Ident_code' => 'xxxxxx',
'Dat_demande' => '00000000',
'Adr_cpos' => 'xxxxxx',
'Adr_mail' => 'xxxxxx',
'Nom' => 'xxxxxx',
'Prenom' => 'xxxxxx',
'Dat_naiss' => '00000000',
];
Error I get :
SoapFault: Did not receive a 'Demande_de_mot_de_passe_Input' object. in SoapClient->__call()
If I unterstand clearly, the formatting of data sent is not correct but when I try other way to send it, it still reporting the same error.
Docs I've read about without success :
http://www.fvue.nl/wiki/Php:_Soap:_How_to_add_attribute_to_SoapVar
http://grokbase.com/t/php/php-soap/066jkmcz2h/passing-objects-to-soap-server-complextype-classmap
EDIT
Here's a capture of the WS 'Demande_de_mot_de_passe' function call in SoapUI :
(Sorry for the long post, I hope it is clear enough, don't forget to ask about precisions if needed, thanks in advance for your help :) )
At your WSDL's type, there's a sequence named Demande_de_mot_de_passe which use a element named Demande_de_mot_de_passeRequest and not Demande_de_mot_de_passe_Input.
Your print from SoapUI describe the message request, but if it's document style, Demande_de_mot_de_passe is a type. On the other hand if it's RPC is the method name.
Starting if it's RPC you can do as showed below. You should use as native object as you can (SOAP will work better with they). A stdObject will be good enough:
$request = new stdClass();
$demande_de_mot_de_passeRequest->Code_societe = '000';
$demande_de_mot_de_passeRequest->Ident_type = 'A';
$demande_de_mot_de_passeRequest->Ident_code = 'xxxxxx';
$demande_de_mot_de_passeRequest->Dat_demande = '00000000';
$demande_de_mot_de_passeRequest->Adr_mail = 'xxxxxx';
$demande_de_mot_de_passeRequest->Adr_cpos = 'xxxxxx';
$demande_de_mot_de_passeRequest->Nom = 'xxxxxx';
$demande_de_mot_de_passeRequest->Prenom = 'xxxxxx';
$demande_de_mot_de_passeRequest->Dat_naiss = '00000000';
$request->Demande_de_mot_de_passeRequest = $demande_de_mot_de_passeRequest;
$response = $client->Demande_de_mot_de_passe($request);
If your SOAP binding is document, you just have to add a new upper level named Demande_de_mot_de_passe
/** The variable $demande_de_mot_de_passeRequest is created as above **/
$demande_de_mot_de_passe = new stdClass();
$demande_de_mot_de_passe->Demande_de_mot_de_passeRequest = $demande_de_mot_de_passeRequest;
$request->Demande_de_mot_de_passe = $demande_de_mot_de_passe;
$response = $client->Demande_de_mot_de_passe($request);
Your WSDL doesn't need a list/collections (it's not your case), so you don't need to create/parse variables with SoapVar. There's others examples that you can read about (one is mine, but it's in portuguese) and other is about the BOGUS node:
http://forum.imasters.com.br/topic/535213-enviar-xml-via-soap/?p=2137411
http://www.fischco.org/blog/2011/3/26/php-soapserver-objects-arrays-and-encoding.html
i am getting this error when trying to make a soap call.
The SOAP action specified on the message, '', does not match the HTTP SOAP Action.
When i call $service->SearchTouristItems($sti); (this function is further below) i get the above error and i have no idea why.
The below is the code i am using.
// i used http://www.urdalen.no/wsdl2php/ to create TCS2Service which extends SoapClient
$service = new TCS2Service() ;
$sd = new ServiceDescriptor;
$sd->UniqueIdentifier = 'xxxxxxxxx-xxxxx-xxxx-xxxxx-xxxxxx';
$stic = new SearchTouristItemCriteria;
$stic->SearchString = array ('dublin') ;
$sti = new SearchTouristItems;
$sti->searchTouristItemCriteria = $sd;
$sti->serviceDescriptor = $stic;
$result = $service->SearchTouristItems($sti);
echo "<pre>";
print_r($result);
echo "</pre>";
SearchTouristItems looks like this
/**
*
*
* #param SearchTouristItems $parameters
* #return SearchTouristItemsResponse
*/
public function SearchTouristItems(SearchTouristItems $parameters) {
return $this->__soapCall('SearchTouristItems', array($parameters), array(
'uri' => 'http://tempuri.org/',
'soapaction' => ''
)
);
}
this is the initilization of the client
public function TCS2Service($wsdl = "http://www.example.com/services/TCS2Service.svc", $options = array( 'soap_version' => SOAP_1_2,
'exceptions' => true,
'trace' => 1,
'cache_wsdl' => WSDL_CACHE_NONE,)) {
foreach(self::$classmap as $key => $value) {
if(!isset($options['classmap'][$key])) {
$options['classmap'][$key] = $value;
}
}
parent::__construct($wsdl, $options);
}
Not sure though but what is the value of 'soapaction' => '' in your code is replaced with the provided parameter. I do not have that experience calling web services with PHP so just gave it a thought.
I think your ws-addressing is not turned on. Please turn on the ws-
addressing and check again.
What I would do :
check if the SOAP action is well defined in the WSDL: look for address location="
try another WSDL to php converter
send the WSDL url so I can try it by my side
I need to build php classes from a WSDL that is behind basic auth.
It has tons of namespaces so it looks burdensome to do this by hand.
I have tried a few tools but looks like the auth session isn't presistent.
$options = array(
'login' => $username,
'password' => $password,
);
$client = new SoapClient($wsdl, $options);
Yes, it works! I tried in a solution that I was building and it connects to my customer WS which is with HTTP Basic Auth.
HTTP Auth works with SOAP Client, however you cannot access password protected WSDL files
See https://bugs.php.net/bug.php?id=27777
Using built in SOAP client, you should have something like this:
$options = array(
'login' => $username,
'password' => $password,
);
$client = new SoapClient($wsdl, $options);
I solved this by using the lib nusoap. See if it helps
$params = array(
"param" => "value"
);
$soap_client = new nusoap_client($wsdl_url, true);
$soap_client->setCredentials(USER_SERVICE, PASS_SERVICE, 'basic');
$soap_client->soap_defencoding = 'UTF-8'; //Fix encode erro, if you need
$soap_return = $soap_client->call("method_name", $params);
How about this solution:
Download the WSDL and save into a local file
Create SoapClient with the local file
Something like this (in a simplified version) :
class MySoap {
private $WSDL = 'https://secure-wsdl.url?wsdl';
private $USERNAME = 'dummy';
private $PASSWORD = 'dummy';
private $soapclient;
private function localWSDL()
{
$local_file_name = 'local.wsdl';
$local_file_path = 'path/to/file/'.$local_file_name;
// Cache local file for 1 day
if (filemtime($local_file_path) < time() - 86400) {
// Modify URL to format http://[username]:[password]#[wsdl_url]
$WSDL_URL = preg_replace('/^https:\/\//', "https://{$this->USERNAME}:{$this->PASSWORD}#", $this->WSDL);
$wsdl_content = file_get_contents($WSDL_URL);
if ($wsdl_content === FALSE) {
throw new Exception("Download error");
}
if (file_put_contents($local_file_path, $wsdl_content) === false) {
throw new Exception("Write error");
}
}
return $local_file_path;
}
private function getSoap()
{
if ( ! $this->soapclient )
{
$this->soapclient = new SoapClient($this->localWSDL(), array(
'login' => $this->USERNAME,
'password' => $this->PASSWORD,
));
}
return $this->soapclient;
}
public function callWs() {
$this->getSoap()->wsMethod();
}
}
It works for me :)
This is Simple example to authenticate webservice using soapClient
$apiauth =array('UserName'=>'abcusername','Password'=>'xyzpassword','UserCode'=>'1991');
$wsdl = 'http://sitename.com/service.asmx?WSDL';
$header = new SoapHeader('http://tempuri.org/', 'AuthHeader', $apiauth);
$soap = new SoapClient($wsdl);
$soap->__setSoapHeaders($header);
$data = $soap->methodname($header);
This code internally parse header as follow
<soap:Header>
<AuthHeader xmlns="http://tempuri.org/">
<UserName>abcusername</UserName>
<Password>xyzpassword</Password>
<UserCode>1991</UserCode>
</AuthHeader>
</soap:Header>
i’ve been trying to resolve this issue, but from what i understand, soap client connections to ssl+httpauth web services are more pain. I’ve googled and from what i understand, with my problem being solved, you can use the example below to solve your problem too(by using HttpAuth infos in both url and soapClient setup).
$username="test";
$password="test";
$url = "https://".urlencode($username).":".urlencode($password)."#example.com/service.asmx?WSDL";
$context = stream_context_create([
'ssl' => [
// set some SSL/TLS specific options
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true,
]]);
$client = new SoapClient($url, [
'location' => $url,
'uri' => $url,
'stream_context' => $context,
'login' => $username,
'password' => $password
]);
$params=array(
'operation'=>’arguments',
'and’=>'other bull',
'goes’=>'here'
);
$response = $client->__soapCall('OperationName', array($params));