PHP cURL SSL CERT - php

I have a situation while trying to do a GET method with curl to get xml from distant web api.
$url = 'xxxx.com'
$key = '/KEY/PRIVATE.KEY'
$perm = '/KEY/CERT.PEM'
$options = array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSLCERT => $perm ,
CURLOPT_SSLKEY => $key,
CURLOPT_HEADER => false,
);
$curl = curl_init();
curl_setopt_array($curl , $options);
$resp = curl_exec($curl);
curl_close($curl);
I have to link SSL cert and pem, but $resp is always returned as false.
This is what my cURL request should be in command prompt :
curl -k -v -X GET "[-URL-]" --cert ./KEY/CERT.pem --key ./KEY/PRIVATE.KEY
Any help would be strongly appreciated...
EDIT
So, I used two methods to try to debug :
With stderr I have:
string ''.' unknown as intern or extern command, program file or executable file
' (length=117)
And with die(curl_error($curl)) I get this :
unable to use client certificate (no key found or wrong pass phrase?)
So, I actually make my URL with two string ($url = a . b), but this is supposed to work, or am I crazy?
For the ssl error, I don't get why I have this issue since this works totally fine on command prompt...
EDIT 2
So, I found out how to solve in part my issue : I had to use realpath().
But now I have a new issue :
SSL certificate problem, verify that the CA cert is OK. Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
So, I found some responses on stackoverflow that say to update cacert.pem file.
I did it, added CURLOPT_CAINFO => $cainfo,
in $options and put curl.cainfo = "PATH_TO/cacert.pem" in both of my php.ini (apache + php => WAMP). (PATH_TO already replaced by my true path).
Anyway, after all thoses changes, I still have the same issue.
I have to work under php 5.3, would it be the cause?
EDIT 3
Finnally found my answer. I should have look for curl options more carefully...
In command prompt -k allows an insecure connexion.
So, in php, I just have to set CURLOPT_VERIFYPEER to false.
FYI, I based myself on the doc that the distant web api gave to me .
Regards,

Related

cURL: CURLOPT_CAPATH contains correct cert but doesn't work

The following script works on PHP 5.6.23:
$options = [
CURLOPT_POST => 1,
CURLOPT_URL => 'https://uat.dwolla.com/oauth/rest/offsitegateway/checkouts',
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_POSTFIELDS => json_encode(['name'=>'value']),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CAINFO => '/path/to/certs/GoDaddyRootCertificateAuthority-G2.crt',
];
$ch = curl_init();
curl_setopt_array($ch, $options);
if( ! $result = curl_exec($ch)) $err = curl_error($ch);
else $err = null;
curl_close($ch);
if($err) echo $err;
else print_r(json_decode($result,true));
I get the expected response from Dwolla's payment API. To make my script more dynamic, I tried to change it to refer to the directory that hosts the certs I want cURL to trust. So I changed the last option (CURLOPT_CAINFO) to:
CURLOPT_CAPATH => '/path/to/certs'
This breaks the script however and no connection is made; the error is:
SSL certificate problem: unable to get local issuer certificate
I know the directory is correct and the cert file is valid since the original script refers to the cert in that same directory. I expected cURL to scan the files in the directory and find the cert it needs but this isn't happening. Why is this?
It does not work if you just point CApath to some directory and put the certificates into this directory. To make the finding of the correct CA certificate efficient the files in this directory need to have names derived from the subject of the certificate. For example you'll might find the following in /etc/ssl/certs:
ff783690.0 -> UTN_USERFirst_Hardware_Root_CA.pem
ff588423.0 -> ComSign_CA.pem
...
Here the filenames are based on the hashes of the certificate's subject and point to the real certificate. For information on how to create the necessary filename see How to calculate the hash value used by CA file names
.
See also the man page for openssl verify:
-CApath directory
A directory of trusted certificates. The certificates should have names of the form: hash.0 or have symbolic links to them of this form ("hash" is the hashed certificate subject name: see the -hash option of the x509 utility). Under Unix the c_rehash script will automatically create symbolic links to a directory of certificates.

PHP Curl works with native library but not with ZF2

I'm connecting to a remote server using TLS1.1 on PHP 5.3.
When using Zend Framework 2, I get an error:
$client = new Client('https://www.example.com/');
$curlAdapter = new Client\Adapter\Curl();
$curlAdapter->setCurlOption(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1);
$client->setAdapter($curlAdapter);
$client->send();
Result: Error in cURL request: SSL connect error
Adding this resolves the issue, but is obviously less secure
$curlAdapter->setCurlOption(CURLOPT_SSL_VERIFYHOST, 2);
$curlAdapter->setCurlOption(CURLOPT_SSL_VERIFYPEER,false);
Result: It works
Making the request using native PHP commands works fine:
$c = curl_init('https://www.example.com/');
$options = array(
CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_1,
);
curl_setopt_array ($c ,$options );
curl_exec($c);
Returns the contents of the page.
So PHP works, but ZF2 doesn't unless VerifyPeer = false. What's the issue?
It is probably because you are missing one parameter:
CURLOPT_CAINFO => '/etc/ssl/certs/ca-bundle.pem' // replace with your cert.
It is also possible that you are using different php configurations (web / cli) that point to different places with the ssl certs. Some details are also available here: Security consequences of disabling CURLOPT_SSL_VERIFYPEER (libcurl/openssl)

Steps for debugging php Curl on windows

I had a lot of trouble figuring out why my php Curl API worked fine on a Mac using MAMP, but would not work under windows.
I asked fot debugging tips or useful information for finding curl configuration issues under windows.
The accepted answer contains a list of the steps that helped me get curl working on windows 7 32 bits.
If Curl still doesn't work, you can use file_get_contents to make POST requests. It works on all hostingers, all OS and on local.
$url = 'WhateverUrlYouWant';
$postdata = http_build_query(
array(
'id' => '202',
'form' => 'animal',
.....
)
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents($url,false, $context);
echo $result
Here is a list of steps for debugging Curl:
Check in phpinfo module that Curl IS enabled.
Verify extensions dir path is propperly set in php.ini and that extension=php_curl.dll is uncommented.
Check that Environment Variables are propperly set as per: http://php.net/manual/en/faq.installation.php#faq.installation.addtopath
Run deplister.exe ext\php_curl.dll to verify all dependencies are correctly satisfied.
If all of the above is working then check the output of CURLOPT_VERBOSE. As per #hp95 suggestion in the following thread: No Response getting from Curl Request to https server
If you are reaching a site that uses SSL check the following post: HTTPS and SSL3_GET_SERVER_CERTIFICATE:certificate verify failed, CA is OK
And fix it like these:
https://snippets.webaware.com.au/howto/stop-turning-off-curlopt_ssl_verifypeer-and-fix-your-php-config/
reverse
curl_close($curl);
print_r($response);
** this is a broken protocol **
to
print_r($response);
curl_close($curl);
** print your results BEFORE closing, which destroys (empties) the $response **

SOAP-ERROR: Parsing WSDL: Couldn't load from - but works on WAMP

This works fine on my WAMP server, but doesn't work on the linux master server!?
try{
$client = new SoapClient('http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl', ['trace' => true]);
$result = $client->checkVat([
'countryCode' => 'DK',
'vatNumber' => '47458714'
]);
print_r($result);
}
catch(Exception $e){
echo $e->getMessage();
}
What am I missing here?! :(
SOAP is enabled
Error
SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl' : failed to load external entity "http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl"/taxation_customs/vies/checkVatService.wsdl"
Call the URL from PHP
Calling the URL from PHP returns error
$wsdl = file_get_contents('http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl');
echo $wsdl;
Error
Warning: file_get_contents(http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl): failed to open stream: HTTP request failed! HTTP/1.0 503 Service Unavailable
Call the URL from command line
Calling the URL from the linux command line HTTP 200 is returned with a XML response
curl http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
For some versions of php, the SoapClient does not send http user agent information. What php versions do you have on the server vs your local WAMP?
Try to set the user agent explicitly, using a context stream as follows:
try {
$opts = array(
'http' => array(
'user_agent' => 'PHPSoapClient'
)
);
$context = stream_context_create($opts);
$wsdlUrl = 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl';
$soapClientOptions = array(
'stream_context' => $context,
'cache_wsdl' => WSDL_CACHE_NONE
);
$client = new SoapClient($wsdlUrl, $soapClientOptions);
$checkVatParameters = array(
'countryCode' => 'DK',
'vatNumber' => '47458714'
);
$result = $client->checkVat($checkVatParameters);
print_r($result);
}
catch(Exception $e) {
echo $e->getMessage();
}
Edit
It actually seems to be some issues with the web service you are using. The combination of HTTP over IPv6, and missing HTTP User Agent string, seems to give the web service problems.
To verify this, try the following on your linux host:
curl -A '' -6 http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
this IPv6 request fails.
curl -A 'cURL User Agent' -6 http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
this IPv6 request succeeds.
curl -A '' -4 http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
curl -A 'cURL User Agent' -4 http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
both these IPv4 request succeeds.
Interesting case :) I guess your linux host resolves ec.europa.eu to its IPv6 address, and that your version of SoapClient did not add a user agent string by default.
Security issue: This answer disables security features and should not be used in production!
Try this. I hope it helps
$options = [
'cache_wsdl' => WSDL_CACHE_NONE,
'trace' => 1,
'stream_context' => stream_context_create(
[
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
]
]
)
];
$client = new SoapClient($url, $options);
This issue can be caused by the libxml entity loader having been disabled.
Try running libxml_disable_entity_loader(false); before instantiating SoapClient.
It may be helpful for someone, although there is no precise answer to this question.
My soap url has a non-standard port(9087 for example), and firewall blocked that request and I took each time this error:
ERROR - 2017-12-19 20:44:11 --> Fatal Error - SOAP-ERROR: Parsing
WSDL: Couldn't load from 'http://soalurl.test:9087/orawsv?wsdl' :
failed to load external entity "http://soalurl.test:9087/orawsv?wsdl"
I allowed port in firewall and solved the error!
Try changing
$client = new SoapClient('http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl', ['trace' => true]);
to
$client = new SoapClient('http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl', ['trace' => true, 'cache_wsdl' => WSDL_CACHE_MEMORY]);
Also (whether that works or not), check to make sure that /tmp is writeable by your web server and that it isn't full.
Try enabling openssl extension in your php.ini if it is disabled.
This way I could access the web service without need of any extra arguments, i.e.,
$client = new SoapClient(url);
None of the above works for me, so after a lot of research, I ended up pre-downloading the wsdl file, saving it locally, and passing that file as the first parameter to SoapClient.
Worth mentioning is that file_get_contents($serviceUrl) returned empty response for me, while the url opened fine in my browser. That is probably why SoapClient also could not load the wsdl document. So I ended up downloading it with the php curl library. Here is an example
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $serviceUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$wsdl = curl_exec($ch);
curl_close($ch);
$wsdlFile = '/tmp/service.wsdl';
file_put_contents($wsdlFile, $wsdl);
$client = new SoapClient($wsdlFile);
You can of course implement your own caching policy for the wsdl file, so it won't be downloaded on each request.
503 means the functions are working and you're getting a response from the remote server denying you. If you ever tried to cURL google results the same thing happens, because they can detect the user-agent used by file_get_contents and cURL and as a result block those user agents. It's also possible that the server you're accessing from also has it's IP address blackballed for such practices.
Mainly three common reasons why the commands wouldn't work just like the browser in a remote situation.
1) The default USER-AGENT has been blocked.
2) Your server's IP block has been blocked.
3) Remote host has a proxy detection.
After hours of analysis reading tons of logs and internet, finally found problem.
If you use docker and php 7.4 (my case) you probably get error because default security level in OpenSSL it too high for wsdl cert. Even if you disable verify and allow self-signed in SoapClient options.
You need lower seclevel in /etc/ssl/openssl.cnf from DEFAULT#SECLEVEL=2 to
DEFAULT#SECLEVEL=1
Or just add into Dockerfile
RUN sed -i "s|DEFAULT#SECLEVEL=2|DEFAULT#SECLEVEL=1|g" /etc/ssl/openssl.cnf
Source: https://github.com/dotnet/runtime/issues/30667#issuecomment-566482876
You can verify it by run on container
curl -A 'cURL User Agent' -4 https://ewus.nfz.gov.pl/ws-broker-server-ewus/services/Auth?wsdl
Before that change I got error:
SSL routines:tls_process_ske_dhe:dh key too small
It was solved for me this way:
Every company from which you provide "Host" has a firewall.
This error occurs when your source IP is not defined in that firewall.
Contact the server administrator to add the IP.
Or the target IP must be defined in the server firewall whitelist.
I use the AdWords API, and sometimes I have the same problem.
My solution is to add
ini_set('default_socket_timeout', 900);
on the file
vendor\googleads\googleads-php-lib\src\Google\AdsApi\AdsSoapClient.php line 65
and in the
vendor\googleads-php-lib\src\Google\AdsApi\Adwords\Reporting\v201702\ReportDownloader.php line 126
ini_set('default_socket_timeout', 900);
$requestOptions['stream_context']['http']['timeout'] = "900";
Google package overwrite the default php.ini parameter.
Sometimes, the page could connect to 'https://adwords.google.com/ap
i/adwords/mcm/v201702/ManagedCustomerService?wsdl and sometimes no.
If the page connects once, The WSDL cache will contain the same page, and the program will be ok until the code refreshes the cache...
Adding ?wsdl at the end and calling the method:
$client->__setLocation('url?wsdl');
helped to me.
I might have read all questions about this for two days. None of the answers worked for me.
In my case I was lacking cURL module for PHP.
Be aware that, just because you can use cURL on terminal, it does not mean that you have PHP cURL module and it is active.
There was no error showing about it. Not even on /var/log/apache2/error.log
How to install module:
(replace version number for the apropiated one)
sudo apt install php7.2-curl
sudo service apache2 reload
I had the same problem
From local machines everything work (wamp + php5.5.38 or vagrant + php 7.4), but from prod linux server I had error
SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl' : failed to load external entity "http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl"
From redirect path plugin in chrome I discovered permanent redirect to https, but change url to https doesnt help.
Status Code URL IP Page Type Redirect Type Redirect URL
301 http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl 147.67.210.30 server_redirect permanent https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
200 https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl 147.67.210.30 normal none none
After few attempts of different code solutions helped my our server provider. He discovered problem in IP forwarding with ipv6.
http://ec.europa.eu/
Pinging ec.europa.eu [147.67.34.30] with 32 bytes of data:
Request timed out.
Request timed out.
Request timed out.
Recommendation was user stream_context_create with socket and bind to 0:0. this forces ipv4
// https://www.php.net/manual/en/context.socket.php
$streamContextOptions = [
'socket' => [
'bindto' => '0:0'
],
];
$streamContext = stream_context_create($streamContextOptions);
$soapOptions = [
'stream_context' => $streamContext
];
$service = new SoapClient('https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl', $soapOptions);
I had similar error because I accidently removed attribute
[ServiceContract] from my contract, yet the service host was still opening successfully. Blunders happen
May try to see if the endpoint supports https as well
Below solution worked for me.
1- Go to php.ini in ubuntu with apache is /etc/php/7.4/apache2
( note: you should use your php version replace by 7.4 )
2- Remove ; from this line ;extension=openssl to make in uncommented.
3- Restart your web server sudo service apache2 restart

How to get Twitter PHP class working on IIS8 (curl issue?)

I decided to combine my website with my twitter account. In order to use it properly, I wanted to post a tweet. I decided to go with the very simple class TwitterAPIExchange from here: https://github.com/J7mbo/twitter-api-php
I tested it on my Linux server and it all works well. But I wanted to use this tool from my Windows server. This is my set up: Windows 2012, IIS 8, PHP.
Here my test code
// Setup
$settings = array( ... );
$url = 'https://api.twitter.com/1.1/statuses/update.json';
// Post the data
$requestMethod = 'POST';
$postfields = array('status' => 'Hello World');
$twitter = new TwitterAPIExchange($settings);
$json = $twitter->buildOauth($url, $requestMethod)->setPostfields($postfields)->performRequest();
var_dump($json);
On the windows machine, it returns
bool(false)
all the time! What does that mean? Is it an issue with curl and IIS? I read several articles online about it but none helped me so far. Any input is very appreciated!
Curl gave me the following error:
curl iis SSL certificate problem, verify that the CA cert is OK
As I could not make it work with a certificate on IIS, I folllowed a hint given on a forum (http://board.phpbuilder.com/showthread.php?10253357-cURL-and-SSL-issues) which told me to add the following curl options:
curl_setopt( $ch , CURLOPT_SSL_VERIFYPEER , false );
curl_setopt( $ch , CURLOPT_SSL_VERIFYHOST , false );
So in the end, I added this to the TwitterAPIExchange class where curl was used.
You likely have to download a certificate package and point the script to it. The following should work:
Save this file to the same directory as the PHP files: http://curl.haxx.se/ca/cacert.pem
Add CURLOPT_CAINFO => 'cacert.pem' to the options section, which starts at line 192 of the current version of the code: https://github.com/J7mbo/twitter-api-php/blob/master/TwitterAPIExchange.php#L192
You could also use another valid certificate bundle and save it elsewhere, so long as you include the path to it in the code.

Categories