I want to verify a signature for a soap request on a soap server implemented in php.
The server code:
$Server = new SoapServer();
$d = new DOMDocument();
$d->load('php://input');
$s = new WSSESoapServer($d);
try {
if($s->process()) {
// Valid signature
$Server->handle($s->saveXML());
} else {
throw new Exception('Invalid signature');
}
} catch (Exception $e) {
echo "server exception: " . $e;
}
The error:
exception 'Exception' with message 'Error loading key to handle Signature' in /<path>/wse/src/WSSESoapServer.php:146
I have implemented a Client to sign SOAP requests using this library: https://github.com/robrichards/wse-php. There are no examples of how to implement the server...
How do I load the public key to check the signature?
[Edit]
I have now been able to load the supplied key using
$key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public'));
$key->loadKey(CERT, true);
I am no longer getting an error message when validating the signature:
$x = new XMLSecurityDSig();
$d = $x->locateSignature($soapDoc);
$valid = $x->verify($key);
However, $valid is always false. I have no idea if it's because the key is loaded wrong or is actually invalid. I can find very little information on implementing a SOAP server with PHP and no information on implementing a SOAP server that relies on checking a signed request.
CLARIFICATION
My client talks to a remote web service and gets an acknowledgement.
Then the remote server takes some time to process my request.
A remote client (that I have no control over) then makes a request to
my service.
The last step is where I have trouble verifying the signature
Well anyways, your first approach looks fine to me, my server has the same structure. Unfortunately, WSSESoapServer does not inherit from SoapServer, thus is not really an SoapServer, but rather a SoapSignatureValidator and should be called like that. It would be easily possible to correct that behaviour, that one would not need a separate SoapServer instance (should be transparent and automatically).
<?php
require 'soap-server-wsse.php';
try {
// get soap message
$xmlSoap = DOMDocument::load('php://input');
// validate signature
$validateSignature = new WSSESoapServer($xmlSoap);
if(!$validateSignature->process())
file_put_contents("log.txt", "soapserver: SIGNATURE VALIDATION ERROR - CONTINUING WITHOUT SIGNATURE\n", FILE_APPEND);
//throw new Exception('Invalid Signature'); # this would cancel the execution and not send an answer
$sServer = new SoapServer($wsdl);
// actually process the soap message and create & send answer
//$sServer->setClass() or setObject() or set setFunction()
$sServer->handle($validateSignature->saveXML());
} catch (Exception $fault) {
file_put_contents("log.txt", "soapserver soapfault: ".print_r($fault, true), FILE_APPEND);
}
?>
Related
Summary
I am trying to set up heartbeats with my Consumer so it can detect a dropped connection - if the broker is rebooted for example so it may reconnect to the failover.
I tested the code with the Consumer/Dispatcher running locally and the queue running in AWS and everything worked fine. However, when moving the code to AWS the Consumer sets up heartbeats with the server/broker, but the heartbeat is either never sent by the server/broker or never received by the client/Consumer. As a result the HeartbeatException is thrown as soon as the requested server heartbeat interval has passed.
I based my code on the examples in the stomp-php-examples github
Repo: https://github.com/stomp-php/stomp-php-examples
Heartbeat example: https://github.com/stomp-php/stomp-php-examples/blob/support/version-4/src/heartbeats_server.php
My next best guess to as why this isn't working is something related to the queue config as I'm using the default config provided by AWS (I think). I have googled and searched all over about config settings for heartbeats but haven't got very far as this is a new topic. Any help would be appreciated!
Setup
Amazon MQ (ActiveMQ 5.15.14)
stomp-php 5.0 (latest version as of today)
I'm happy to provide any more details of my setup.
Code
Consumer (stripped down version)
abstract class AmqConsumerAbstract
{
/** #var StatefulStomp */
protected $consumer;
const HEARTBEAT = 5000;
public function listen(): void
{
$observer = new ServerAliveObserver();
$this->client->getConnection()->getObservers()->addObserver($observer);
$this->client->setHeartbeat(0, self::HEARTBEAT); // heartbeats setup here
// Note: the heartbeat above is longer than the read-timeout below
$this->client->getConnection()->setReadTimeout(2, 0);
$this->client->connect();
$this->consumer = new StatefulStomp($this->client);
$this->consumer->subscribe(
$this->getQueueName(),
null,
'client'
);
if (!$observer->isEnabled()) {
// we never get here so I assume everything is working OK
echo 'The Server is not supporting heartbeats.');
exit(1);
} else {
echo sprintf('The Server should send us signals every %d ms.', $observer->getInterval() * 1000);
}
try {
while (true) {
$frame = $this->consumer->read(); // I assumed this line would read the heartbeat?
// there is then some logic that deals with payload and does
// $this->consumer->begin();
// $this->consumer->ack($frame);
// $this->consumer->commit();
if ($observer->isDelayed()) {
$this->echoLog('ServerAliveObserver: Server has been delayed.');
}
}
} catch (HeartbeatException $e) {
echo 'AMQ (STOMP) Error, the server failed to send us heartbeats within the defined interval: ' . $e->getMessage()
$this->reconnect();
} catch (ConnectionException $e) {
echo $e->getMessage();
$this->reconnect();
} catch (Exception $e) {
echo 'AMQ (STOMP) Queue error: ' . $e->getMessage();
exit(1);
}
}
}
I have reproduced on a single-instance broker with version 5.15.14.
I have tried switching on STOMP debugging for the broker, but it appears the uri attribute under <transportConnector> is not allowed. The config will save but pulls out the uri attribute.
<transportConnector> Elements and Their Attributes Permitted in Amazon MQ Configurations
Working with Spring XML configuration files (xsd files)
I am using SOAP in a php script to call a web service from a Linux Centos 6 server. In this week I have been getting could not connect to host error from soapCall method. Of course this exception does not happen all the time but I see this exception most of the time, for example if I call soapCall 5 times, this error happens 3 times. My code is as below and I have not changed it at all for some months but recently it gets this error most of the time.
$wsdl="http://x.x.x.x:x/gw/services/Service?wsdl";
//Set key as HTTP Header
$aHTTP['http']['header'] = "key:" .$key ."\r\n";
$context = stream_context_create($aHTTP);
try
{
$client = new SoapClient($wsdl,array("soap_version" => SOAP_1_2,'trace' => 1,"stream_context" => $context));
}
catch(Exception $e)
{
return "something";
}
//I make $parametrs
try
{
$res = $client->__soapCall("send",array($parametrs));
}
catch(Exception $e)
{
print_r($e->getMessage()); //Most of the time it prints could not connect to host
}
I have read most answers to related questions here but my problem has not been solved. Also I added
ini_set("soap.wsdl_cache_enabled", "0");
ini_set("soap.wsdl_cache_ttl","0");
but nothing changed. I searched a lot about the reason and I found something about SSL but I have not found exact solution.
It must be mentioned that I can see the wsdl URL in browser and I have it's ping.
I'm currently attempting to install a login button for LinkedIn onto my website. I'm trying to go step by step as the developers section shows. After I wrote the code to get a request token and checked it using print_r.
define("my consumer key");
define("my consumer secret");
$oauth = new OAuth(my consumer key, my consumer secret);
//The first item of business is getting a request token
$request_token_response = $oauth->getRequestToken('https://api.linkedin.com/uas/oauth/requestToken');
if($request_token_response === FALSE) {
throw new Exception("Failed fetching request token, response was:"
. $oauth->getLastResponse());
} else {
$request_token = $request_token_response;
}
print "Request Token:\n";
printf(" - oauth_token = %s\n", $request_token['oauth_token']);
printf(" - oauth_token_secret = %s\n", $request_token['oauth_token_secret']);
print "\n";
I got "Fatal error:Uncaught exception 'OAuthException'...Peer certificate cannot be authenticated with known CA certificates". The line that was called for the error is below
$request_token_response = $oauth->getRequestToken('https://api.linkedin.com/uas/oauth/requestToken');
I am not understanding what that error is trying to tell me so that I can fix the problem. I would appreciate and tips or guidance to help me better understand what this error message is trying to convey and how to fix it.
It seems that the library is trying to verify the SSL certificate and it cannot do so. You could disable ssl checks via the method OAuth::disableSSLChecks. I assume you are using the following pecl extension http://www.php.net/manual/en/class.oauth.php. If not the client library you are using should have a method to disable the SSL certificate verification.
....
...
$oauth = new OAuth(my consumer key, my consumer secret);
$oauth->disableSSLChecks();
..
...
Ideally you would do it right after instantiation
I have a problem with Updating and Deleting events using signed requests. Inserting events works just fine. But when Updating and Deleting I receive an "Unknown authorization header" 401 error.
For ALL three operations I first generate the client like this:
$client = new Zend_Gdata_HttpClient();
$client->setAuthSubPrivateKeyFile('certificates/gcalkey.pem', null, true);
$client->setAuthSubToken($session_token);
$gdataCal = new Zend_Gdata_Calendar($client);
To Update an event I use this:
$eventOld = $gdataCal->getCalendarEventEntry($eventUri);
$eventOld->title = $gdataCal->newTitle('NEW NAME');
try {
$eventOld->save();
} catch (Zend_Gdata_App_Exception $e) { print_r($e); exit; }
And it gives me the "Unknown authorization header" error. But the same code, using unsigned requests, works.
Where might be the problem? I tried modifying $eventUri to both https and http, but it seems it does not have any effect.
I've been getting this 401 error message too, creating the client and service the same way. It can retrieve a list of calendars, but fails when retrieving an event feed.
Has this worked for you before? It might not be officially supported yet.
I make use of a great class made by Jaisen: http://github.com/jmathai/twitter-async/tree/master. Recently twitter has been up and down and I am sure it will continue to be the same in the future so I am trying to cut down on my dependency on twitter being actually working.
The following is what I have in my header.php and it is right at the top and it generates the login URL for each user. If twitter is down, my site hangs for as long as it needs to and it throws an exception. So I have to catch these expceptions which I have sort of done.
I now want to just cancel the request to the API after a few seconds and just load the page and keep trying behind the scenes. How can I best do this?
<?php include './twitter/EpiCurl.php'; include './twitter/EpiOAuth.php'; include './twitter/EpiTwitter.php';
$consumer_key = 'mykey';
$consumer_secret = 'mysecret';
$twitterObj = new EpiTwitter($consumer_key, $consumer_secret);
try{
$twiturl = $twitterObj->getAuthenticateUrl();
$url = "window.open('".$twiturl."','Login', 'left=20,top=20,width=500,height=500,toolbar=0,resizable=1'); startLoad();";
}catch(EpiOAuthBadRequestException $e){
// bad request exception do something
$statusMessage = 'Oops an error has occurred: ' . $e->getMessage();
}catch(EpiOAuthUnauthorizedException $e){
// bad authorization..probably bad tokens, do something different
$statusMessage = 'Oops an error has occurred: ' . $e->getMessage();
}catch(EpiOAuthException $e){
// uh oh, unknown oauth exception
$statusMessage = 'Oops, an unknown authorisation error has occurred! The mojo team have been notified! Please try again.';
}
if(isset($statusMessage)){
}
?>
Any improvement of the above code will also be appreciated.
Thanks all
The library supports a value to be passed into the curl timeout.
$twitterObj->setTimeout($secs_request_timeout);
I just added support for passing in a connection timeout as well. Unable to run unit tests because I'm being rate limited. Will commit this once I can verify that it works.
$twitterObj->setTimeout($secs_request_timeout, $secs_connection_timeout);
Use curl_setopt(CURLOPT_CONNECTTIMEOUT, 1 /* 1 second timeout */); to cause CURL to give up if a connection isn't established in 1 second. I use this when connecting to the facebook API, 'cause they've been pretty unreliable in the past as well.