I'm developing a php client to interact with Nominet EPP server, and as I found in their web site:http://registrars.nominet.uk/namespace/uk/registration-and-domain-management/registrar-systems/epp/registration
I need to use a "Verisign Class 3 Public Primary Certification Authority" root certificate. I found one in here (Verisign is bought by Symantec): https://knowledge.symantec.com/support/mpki-for-ssl-support/index?page=content&id=SO5624&actp=LIST&viewlocale=en_US
When I use it in my php code:
//$context = stream_context_create(array($this->protocol => $options));
$context = stream_context_create();
stream_context_set_option($context, $this->protocol, 'local_cert', __DIR__ . '/../../certificates/'.$this->certificate_path);
$errno = false;
$errstr = false;
$this->socket = stream_socket_client($this->protocol.'://'.$this->hostname.':'.$this->port, $errno, $errstr, 5 , STREAM_CLIENT_CONNECT, $context);
Everything is ok: path of certificate, port, hostname ...
And I get:
"stream_socket_client(): Unable to set private key file ..."
I know I need a private key, but in Symentec website they don't give a private key.
Do anyone have any idea ?
Thanks so much,
Hassan,
You are not using the right context option. This option is to define your certificate. What you want is use the certificate you found to authenticate the peer server, to make sure you are really talking to the right one.
As stated by this:
In order to verify the identity of the secure server you will need the
'Verisign Class 3 Public Primary Certification Authority' root
certificate available free from www.verisign.com (the certificate is
also distributed with most web browsers).
On PHP Doc:
local_cert
string Path to local certificate file on filesystem. It
must be a PEM encoded file which contains your certificate and private
key.
But what you want is this:
cafile
Location of Certificate Authority file on local filesystem which
should be used with the verify_peer context option to authenticate the
identity of the remote peer.
Related
I need to establish a TLS connection between my backend and a web service API. I'm using the HttpClientInterface class within the Symfony framework. I would really like to understand what I'm doing here.
This is the situation:
I recieved a .p12 file containing my certificate and its private key, these have been extracted with openssl to .pem files. In the PHP: SSL context options, I have set local_cert = /mypath/mycert.pem, local_pk = /mypath/mykey.pem and passphrase = 'pwtoextractfiles'. Are all these options neccessary? Whats happening here really?
I also got a server certificate. I set cafile = /mypath/servercert.pem. However the issuer of the server cert tells me I'm supposed to add the issuer of the server cert as trusted root and add the issuer in my trust store. How is this done in PHP? Have I done this correctly by setting the cafile option?
I'm running the next script from my local host and the production server, and Im getting different outputs. Anyone knows why am I getting that false from my localhost?
<?php
$host = 'ssl://mail.companyname.org';
$port = 993;
$error = 0;
$errorString = "";
var_dump(fsockopen($host, $port, $error, $errorString, 30));
var_dump($errorString);
var_dump($error);
Local host output:
bool(false)
Production server output:
resource(4) of type (stream)
UPDATE: after the comments/answer I have modified the code and now Im getting this output on my local host:
PHP Warning: fsockopen(): SSL operation failed with code 1. OpenSSL
Error messages: error:1416F086:SSL
routines:tls_process_server_certificate:certificate verify failed in
/tmp/test.php on line 7 PHP Warning: fsockopen(): Failed to enable
crypto in /tmp/test.php on line 7 PHP Warning: fsockopen(): unable to
connect to ssl://mail.twmdata.org:993 (Unknown error) in /tmp/test.php
on line 7 bool(false) string(0) "" int(0)
it seems this is problem with server certificate :
first you can check if your server certificate and its chains are valid by this:
https://www.sslshopper.com/ssl-checker.htm
if somethings were wrong in ssl-checker?
you can try to correct SSL certificate configs in companyname.org
if you succeed and error was persists ?
you have to add Certificate files manually.
if you have a self-signed certificate:
you have to add Certificate files manually.
if you dont have certificate nor you dont care about man-in-the-middle attack,
you can still use SSL without Certificate.
turn off php fsock Certificate check (not recommended)
its recommended to have a certificate at least a self-signed. if you have a self-signed try 1 solution.
I have found the Problem
You have exposed your Domain name in your PHP Warning Log, so i have checked your domain SSL.
after i check your company`s domain certificate using this tool:
https://www.sslshopper.com/ssl-checker.html#hostname=twmdata.org
it had 2 errors with your certificates:
This certificate has expired (0 days ago). Renew now.
None of the common names in the certificate match the name that was entered (twmdata.org). You may receive an error when accessing this site in a web browser.
so it seems you have to renew your certificate first
Update:
i have found this answer maybe helpful
https://stackoverflow.com/a/40962061/9287628
it suggested to use
stream_context_create(['ssl' => [
'ciphers' => 'RC4-MD5'
]])
as #ChrisHaas suggested connecting with stream_context_create and stream_socket_client brings you a lot of option if you want to dictate the cert directory or you want to turn off certificate check.
Per the documentation for fsockopen
The function stream_socket_client() is similar but provides a richer set of options, including non-blocking connection and the ability to provide a stream context.
Basically, fsockopen is very low-level but without many options, or, arguably, "sane defaults".
Instead, you can switch to stream_socket_client which will allow you to specify a context as the last parameter, and that object has many options, including a dedicated one with over a dozen options specific to SSL. The object created from this function is compatible with fwrite and other functions, so it should do everything you are hoping for.
$context = stream_context_create([/*Options here*/]);
$connection = stream_socket_client($host, $errno, $errorString, 30, null, $context);
Now, what options should you use?
The worst option that might work is probably verify_peer. I say "worst" because you are throwing away the verifiability part of SSL/TLS and only using it for encryption, and doing this will make you susceptible to MitM attacks. However, there's a place and time for this, so you could try it if the other options are too complicated.
$context = stream_context_create(['ssl' => ['verify_peer' => false]]);
$connection = stream_socket_client($host, $errno, $errorString, 30, null, $context);
Instead, I'd recommend using either cafile or capath which do the same thing except the former is for a file while the latter is for a directory.
$context = stream_context_create(['ssl' => ['verify_peer' => true, 'cafile' => '/path/to/file']]);
$connection = stream_socket_client($host, $errno, $errorString, 30, null, $context);
What certs should you use? We use this library to pull in recent CA files on a periodic basis, very convenient. There's a little bit of setup that's per-project but once you get it it goes pretty fast. See this for pulling in a CA file at a well-known location.
One other last option is local_cert which you can use with a PEM file that holds the certificate and private key from the server, if you have access to that.
EDIT
The cert on mail.twmdata.org:993 is different than the web server's cert that other people are talking about, which is generally a best practice. You can inspect that cert using:
openssl s_client -connect mail.twmdata.org:993 -servername mail.twmdata.org
If you do that, you'll see that the server has a self-signed cert which you can get around by setting the verify_peer option to false.
Remove the # symbol. You are hiding error messages that might tell you what the problem is. You should also set a variable in the errorno argument to fsockopen() and echo it for debugging.
My guess would be that you haven't installed PHP with SSL support on your local server. See here.
Companyname.org might also block requests from your local server that are allowed from the production server.
I'm working with cUrl and PHP to make a request to a server (for paypal access)
Paypal developer website does never mention that an SSL certificate is required to use PayPal access API, however the code that I use to request the token is the following:
$options = array(
CURLOPT_URL => $url,
CURLOPT_POST => 1,
CURLOPT_VERBOSE => 1,
CURLOPT_POSTFIELDS => $postvals,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSLVERSION => 3
);
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
echo curl_error($ch);
This echo outputs the following error:
SSL certificate problem: unable to get local issuer certificate
My questions are:
1) do I need SSL to use paypal access if I need only to get the user email?
2) if I do not need SSL why this error occours?
PS: the endpoint is the following: https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/tokenservice
The correct solution is to fix your PHP setup.. setting CURLOPT_SSL_VERIFYPEER to false is a quick hack, but it's wrong as you disable the certificate validation by it's certificate authority. This exposes you to a man-in-the-middle attack.
It's easy to fix (php 5.3.7 or higher) -
Download a list file with an up-to-date certificate authorities, and add this setting to your php.ini
curl.cainfo=<path-to>cacert.pem
Restart your web server, and it'll work !
You may disable SSL verification (which is enabled by default as of cURL 7.10), by adding this:
CURLOPT_SSL_VERIFYPEER, false
to your $options, however the proper way is to keep validation enabled.
SECURITY NOTICE
If remote site uses certificate issued by known CA but validation still fails, then most likely certificate is incorrectly set up on the remote server (lack of intermediate certificates etc.). Alternatively your system got no idea about used Certificate Authority that signed target's certificate. In such case yo should use php.ini's curl.cainfo (documentation) to point to valid PEM file with all supported CAs - that would make your setup properly validate issuer chain.
Please be aware that by setting CURLOPT_SSL_VERIFYPEER to false you are NOT solving the issue! You are working it around. This is all about security so it's fine to do that for a while, but deploying that on production is not wise, politely speaking, as you will become open to Man In The Middle Attack. You have been warned.
I had the same exact problem
Can't connect to PayPal to validate IPN message: SSL certificate: unable to get local issuer certificate
I used the code samples generated on paypal's github found here (I used PHP): https://github.com/paypal/ipn-code-samples
I downloaded both certs and tried testing both from curl: http://curl.haxx.se/docs/caextract.html
After about 2 hours of testing (using paypal's ipn simulator) and googling, found that paypal ipn cannot be tested on localhost, so i pushed the code live and tried testing, but still got the same error (even with permissions set to 777).
When I set CURLOPT_SSL_VERIFYPEER, false, it worked but this would defeat the purpose of having an ssl certificate.
After snooping around on my server's files, I found a curl-ca-bundle.crt file in my PHP folder. I decided to hardcode the CURLOPT_CAINFO in my paypal ipn script to that path. It finally worked!
I noticed this older .crt file included some certificates that weren't on the latest .crt file from the curl website. It was a bunch of certificates from verisign class 1, verisign class 2, verisign class 3 and verisign class 4.
Here's the complete list of the certificate names I added to curl's .crt file:
Verisign Class 1 Public Primary Certification Authority
Verisign Class 1 Public Primary Certification Authority - G2
Verisign Class 1 Public Primary Certification Authority - G3
Verisign Class 2 Public Primary Certification Authority - G2
Verisign Class 2 Public Primary Certification Authority - G3
Verisign Class 3 Public Primary Certification Authority
Verisign Class 4 Public Primary Certification Authority - G2
This may have something to do with what #Andomar was saying - paypal's verisign certificate is not included in the default (by default I mean curl's default) list of safe certificates.
I didn't have the time to debug and figure out exactly which certificate is needed so I just included all of them.
For anyone who experiences this problem in the future, I would suggest to get the latest certs from curl and add one by one the certificates in the list above until the error is gone.
Here's a link for some of those verisign certificates (you may need to google for the others not listed): www.symantec.com/page.jsp?id=roots
Note*: To view paypal's current certificates you can run this command in terminal:
openssl s_client -connect paypal.com:443 -showcerts
If anyone has further insight to this issue, please comment as I spent hours to figure all of the above out.
SSL certificate problem: unable to get local issuer certificate
Means that cUrl doesn't trust Verisign, the certificate authority that vouches for PayPal. As Marc B comments, cUrl no longer ships with trust for any certificate authority.
You can bypass the certificate chain validation with the option:
CURLOPT_SSL_VERIFYPEER => 0
To read how to configure cUrl so that it trusts Verisign, read the cUrl documentation.
I am currently using a tcp socket to exchange data between a server and a client using socket streams in PHP. I want each client to have a unique certificate (which I will generate for them) to access my server so that only people I know will be able to use it.
I have searched and found only how to use certificate on the server side, which will enable me to make a SSL socket connection, but what I actually need is to use a client server certificate to identify the client that is connecting to my server. Is this possible using PHP ?
I'd imagine the client to connect like this:
$clientCert = './clientCert.pem';
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $clientCert);
$server = stream_socket_client('ssl://IP', $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);
But how does the server handle (identify, etc) this client side certificate ?
Hope I was clear with my question.
Thanks in advance,
Rafael
You should use stream_context_set_option to set verify_peer to true; this will require the other side to verify that the client's certificate is trusted. The verification is done by checking that the certificate is signed by a trusted authority, so you need to specify where the authority's public key can be found through either the cafile or capath option.
Note that the above holds true both on the server side (to allow you to authenticate your clients) and on the client side -- the server is also not authenticated unless you explicitly make it so.
I'm testing an API that uses curl_exec php function and a CA certificate but something is going wrong and I'm a little lost.
I have configured SSL on my apache VirtualHost and looks ok ( opening https:://[myVHost]... works ).
However the API curl call give me back this message:
SSL peer certificate or SSH remote key was not OK
I'm not very experienced with SSL so I have few ideas about the cause of that.
UPDATE:
This is the code I'm using in my cURL request, I have commented 2 lines and changes their value (look at 'TODO' line ) and in this way it is working, however this is just a work arround ...
$opts[CURLOPT_URL] = $url;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_CONNECTTIMEOUT] = 50;
$opts[CURLOPT_TIMEOUT] = 100;
$headers = array(
'Accept: application/json',
"User-Agent: APIXXX-PHP-Client");
$opts[CURLOPT_HTTPHEADER] = $headers;
$opts[CURLOPT_USERPWD] = $env->getApiKey() . ':';
if (certificatePresent()) {
// $opts[CURLOPT_SSL_VERIFYPEER] = true;
// $opts[CURLOPT_SSL_VERIFYHOST] = 2;
// TODO: SET IT BACK
$opts[CURLOPT_SSL_VERIFYPEER] = 0;
$opts[CURLOPT_SSL_VERIFYHOST] = 0;
$opts[CURLOPT_CAINFO] = $path
}
curl_setopt_array($curl, $opts);
$response = curl_exec($curl);
You are probably using self-signed SSL certifiacate, which will not pass when the CURLOPT_SSL_VERIFYPEER options is set.
There are two solutions:
Set up valid SSL certificate.
Disable SSL verification in Curl. (add --insecure option)
If you disable verification, you can't be sure if you are really communicating with your host.
So it depends on level of security you need.
Beside CURLOPT_SSL_VERIFYPEER there are two other settings which might be changed to false/0:
CURLOPT_SSL_VERIFYHOST
CURLOPT_SSL_VERIFYSTATUS
Beware that you should fix your SSL certificates & settings instead of disable security!
Although I am answering an old post, I think it will help the new viewers-
You can check the problem by adding
$opts[CURLOPT_VERBOSE] = 1
For self signed certificate your client may connect with the server using IP address, because the host name is not available in DNS cache. In that case the COMMON NAME(CN) of your server certificate needs to match with the Server IP (put IP address as common name when generating the server certificate). When you do it correctly, you can see this message:
common name: 192.168.0.1 (matched)
Here 192.168.0.1 is an example.
You're right to want to enable SSL_VERIFYPEER if you are worried about man-in-the-middle attacks.
Is your $path set to point to the certificate (or certificate bundle) provided by the API owner? Is that certificate readable by the web server user? If so, have you verified that the certificate(s) is the same as when you visit the https address manually in a browser and inspect the certificate?
If you can't get it to work, and the API you are connecting to has a SSL certificate that works in your normal browser without warnings, you should be able to set $path to your CA root bundle on your server.
You can build a valid SSL certificate and ensure that it is stored in the trusted folder.
Valid SSL certificate can be created by including the following command in the developer command prompt of VS2012. (This can be obtained by typing developer in the start)
The following command creates a self-signed certificate that can be used to test a web application that uses Secure Sockets Layer (SSL) on a web server whose URL is www.example.com. The OID defined by the -eku option identifies that certificate as an SSL server certificate. The certificate is stored in the my store and is available at the machine (rather than user) level. The certificate's private key is exportable, and the certificate is valid from May 10, 2010 through December 22, 2011.
Makecert -r -pe -n CN="www.example.com" -b 05/10/2010 -e 12/22/2011 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localmachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12
For more on how to create the SSL certificate
Now make sure that this certificate is trusted, this can be done by typing CERTMGR in the cmd..
now the cert created is in the PERSONAL folder.. copy it and paste it to the TRUSTED PEOPLE FOLDER.
This should do the trick. Let me know if that doesn't work.
I had the same issue. I follow the instruction here: http://siteber.com/download-failed-ssl-peer-certificate-or-ssh-remote-key-was-not-ok/ and it fixed mine.
basically I went to /etc/resolv.conf
and Replace any
OpenDNS server:
208.67.222.222
208.67.220.220
With
Google’s public DNS servers:
nameserver 8.8.8.8
nameserver 8.8.4.4
This error can also occur if you update packages on a linux server that has a self-signed certificate.
Solution:
Stop your existing Apache/nginx server.
Run certbot (if you are using lets encrypt)
Restart your Apache/nginx server.
Note: If you're using Springboot, add System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,TLSv1.3"); to your application.properties file
Voila!
I spent almost all day for this error, and problem was in using ipv6, and called api server does not support ipv6.
Solution:
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_VERSION_IPV4);