MAMP & PHP “SSL operation failed with code 1” - php

EDIT
I’m on the move at the moment, and only have SO on the Stack Exchange app on my iPhone, so there’s some weird formatting with the quotes in the code below - Apologies! I have real ones in the real code :)
I’ve been trying to figure this out for two days with some other questions on SO, but here goes...
Just trying to use file_get_contents() to capture the webpage of another file located on the same server and same domain, and include that. I’m using MAMP with a self signed cert for production (so that I can have the server force SSL etc) so there’s that, and I also have that certificate as “Always Trusted” locally on my Mac as it’s self-signed obviously.
So now I have the issue where I want one page to capture the contents of another.. I initially tried using cURL and it was failing without giving me any exceptions, and no information using curl_error() so I switched over to file_get_contents() where I get the exception SSL operation failed with code 1... ssl3_get_server_certificate:certificate verify failed
I assume this is in issue with OpenSSL not trusting the self-signed (but I thought it used the trusted CA’s of the underlying OS?) and I can’t get it to work using the following stream context:
stream_context_create([
"ssl" => [
"allow_self_signed" => true
]
]);
And if I set verify_peer and verify_peer_name to false, the request is made but HTTPS cookies aren’t sent which breaks the whole thing.
I’ve tried adding the actual text of the cert to the cacert.pem file under the OpenSSL directory in MAMP, and set that file in the openssl.cafile option in the php.ini file as stated in another answer, but alas no luck...
Any ideas? Your help would be much appreciated! Thank you! ☺️💛
EDIT 2
So I tried it again using cURL, and this time got cURL to give me some verbose output to a file, and this is what it gives me (note the line third from the bottom):
* Trying ::1...
* TCP_NODELAY set
* Connected to admin.voyagerisc (::1) port 8890 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:#STRENGTH
* successfully set certificate verify locations:
* CAfile: /Applications/MAMP/Library/OpenSSL/certs/cacert.pem CApath: none
* SSL certificate problem: unable to get local issuer certificate
* Curl_http_done: called premature == 1
* Closing connection 0
...and this is the file where I have added the raw certificate to the top.. ugh, STUCK!

TLDR; You need to become your own (mini) CA so you have a Root Cert that can be trusted
So after a few painstaking days, I have finally figured this out and I hope below will help those few MAMP / PHP users who will undoubtedly run into this issue!!
Basically, file_get_contents() and cURL operate above OpenSSL, and (as I have now learnt, I was an SSL noob!) OpenSSL, like every device basically has big list of all the main Certificate Authority's root certificates. So if any certificate has been signed by one of those root certificates in can be trusted in the eyes of OpenSSL.
Now you can go into that file that OpenSSL uses that has all the big root certs, and I thought it would be as easy as placing the raw self-signed certificate of the development site at the top of the list and everything would fall into place. However, for some reason OpenSSL wouldn't trust it if it's not a ROOT CERTIFICATE.
So long story short, here's the steps:
Follow this guide to becoming your own CA in literally under 5 minutes. (As a side note, this now means that you can trust just the newly created root certificate, and for every development site you want with SSL, self-sign with that root certificate and you won't need to re-trust etc)
Once you've trusted the root certificate as per the article, and pointed MAMP to the key and certificate for the specific site, in your IDE open up the .pem file for the ROOT CERTIFICATE that you created, NOT the specific one for your development site
For MAMP, the file OpenSSL uses as the core list of trusted Certificate Authorities is located in /Applications/MAMP/Library/OpenSSL/certs/ - Open the cacert.pem in your IDE as well
Now, under the comments and before the first real CA, copy and paste in the raw data of your Root Certificate. You can also add in before that long piece of data (and BEFORE the -----BEGIN CERTIFICATE----- line) the name you set for your Certificate Authority earlier in Step 1, and format it with the ='s as is with the other Certificate Authorities.
The usual: Save & restart MAMP
Now, if you're running a local development server with MAMP and have self-signed SSL enabled, you should now be able to make the SSL calls back to the server itself, and have the server trust.... itself? I guess? Weird when you think about it!
As a final note, if you had visited any other similar questions here on SO, you would've seen many answers prompt you to create either a stream context with get_file_contents() or set some options with your cURL resource that suggested turning "verify_peer" => false & "verify_peer_name" => false in a stream context, or the equivalent options for cURL. I cannot stress enough that the approach completely defeats the purpose of SSL and shouldn't even be used for local development, due to the possibility of you forgetting to turn it back on or some other obscure event that causes trust to remain off within production.
The method I described above is more tedious, yes, but it will get you through development until you have a real SSL cert in production signed by a true CA, at which point this won't even be an issue anymore.
Feel free to comment if this explanation is not clear / precise enough, I'm only still getting my head around it! Cheers :)

As Mike mentioned (see comments), in the current version of MAMP (6.4) there's an invalid certificate file added to /Applications/MAMP/Library/OpenSSL/certs/cacert.pem. Just open this file and access the link shown in the 301 redirect (https://curl.se/ca/cacert.pem), download it and replace the file.

Mark MAMP_PRO_Root_CA as trusted in Keychain Access
After a ton of hassle and problems related to this, and the situation getting even worse over time as Chrome updated, breaking my automated testing as well, I finally found the solution, as described in the MAMP docs on SSL.
We simply have to add the root certificate that MAMP uses to sign it's custom SSL certs to Keychain Access and set it to always trust:
Open Keychain Access on your Mac
Search for MAMP_PRO_Root_CA
Double click it and expand the Trust section
Set When using this certificate to Always Trust
Close the little window and enter your system password to save the change
After that, any SSL certs you create through the MAMP interface will work automatically, as far as I can tell! If you've been creating your own self-signed certs through the terminal, you might have to go back and update them, but it's easy and reliable, so I don't see why you wouldn't want to if you're already committed to the hot mess which is the MAMP Pro lifestyle 😅
Here's the UI where you create SSL certs in MAMP Pro:
Honestly, I can't tell if this solution was staring me in the face the whole time, or if it was recently enabled in the new versions of MAMP or Chrome. In the end it doesn't matter. Our long national nightmare is finally over 🙏🏻

Related

SSL Verify Server Certificate error when calling API on same localhost (tls_process_server_certificate:certificate verify failed)

My dilemma is that I have two domains running on localhost, domain_a and domain_b. They're both running nginx, apache, and php-fpm. domain_a is running CodeIgniter 3.0.0, and domain_b is running CodeIgniter 4. In another VM, I had domain_a in a Docker container, and was able to hit the API endpoints in domain_b without any issues. Development work made it a requirement to have them both be on the same server, as it's close to how it will be in other environments.
For specifics, we're using the PHP oAuth module, and it throws an error that "making the request failed (dunno why)", which is extremely helpful. After some digging, I found that I could hit other endpoints without issue (such as google.com and a known endpoint outside these domains). I attempted to use cURL in place of oAuth (just a simple test to hit the endpoint), and I consistently get the same error.
tls_process_server_certificate:certificate verify failed
The certs I use are all self-signed for both domains, and I'm able to reach both domains from within the browser without issue. If it matters, both domains have user certs when logging in, but the users aren't the same, as each domain has their own self-signed CA.
My current code is this:
$conn = new OAuth($consumer_key, $consumer_secret, $oauth_sig_method);
$conn->enableDebug();
/*
if (is_on_local()) {
$conn->setCAPath('path/to/cert.cert');
}*/
$conn->disableSSLChecks();
$token = $conn->getRequestToken($auth_url);
I left the commented out part in to show what I've tried - I've tried pointing that to the system cert, domain_a CA, and domain_b CA, none of which worked. It looks like (for some reason) $conn->disableSSLChecks() isn't working, but I'm not sure of that. The error thrown is in the call to getRequestToken().
My etc/hosts file:
127.0.0.1 domain_a.tld
127.0.0.1 domain_b.tld
The actual TLD isn't tld, but again, they work in the browser and it worked before when domain_a was in Docker.
I've already tweaked domain_a enough so CI 3 works with PHP 8, so I'm convinced the problem is talking from one domain to the other. I'm running RHEL 8, and I've already got SELinux set to Permissive (actually disabled, I think, for development). There's nothing in httpd, nginx, php-fpm, or firewall logs. The only indicator I have is what I get from CI 3 logs in domain_a:
Severity: Warning --> OAuth::getRequestToken(): SSL operation failed with code 1.
OpenSSL Error messages:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed in /path/to/file
Severity: Warning --> OAuth::getRequestToken(): Failed to enable crypto in /path/to/file
Severity: Warning --> OAuth::getRequestToken(domain_b/oauth/access): Failed to open stream: operation failed in /path/to/file
I feel like the answer is right there, I'm just not seeing it.
As usual, shortly after explaining my issue I found the fix.
Currently, I have the endpoint as https://domain_b.tld/oauth/access. After some tinkering, I got a different error about SSL version. That put me on the track to the correct answer:
http://domain_b.tld:port/oauth/access. I'm able to hit the endpoint now without issue. I've got a virtual hosts file that, even though both domains are on the same port, I had to specify it or the call fails.
If anyone else runs into this issue, check the base URL. I never would have thought about hitting http rather than https as a solution.

Woocommerce Webshop on bitnami stack (ec2): SSL operation failed with code 1. OpenSSL Error messages: error:14090086

I simply can't find a solution to this. I migrated a Wordpress site with a woocommerce shop and payment gateway "Payunity" to a new EC2 machine with a bitnami wordpress stack.
I generated a Let's Encrypt SSL certificate and the entire site works as expected.
Only problem I have is that for some reason on the woocommerce checkout page I suddenly get this error message:
SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:ssl3_get_server_certificate:certificate
verify failed
I googled extensively and tried figuring this out but no chance.
Any idea what I have to set on the server to have this go away? I tried modifying the php.ini with the capath and cafile like some threads pointed out but no luck.
Any ideas?
Update: I now moved to Cloudflare as DNS Manager and have the "Full (strict) setting so that the Cloudflare SSL is the one in use. However still the same error, so I figure this has nothing todo with the original Let's Encrypt or now Cloudflare SSL Certificate.
I believe this error message is caused by CURL. According to the CURL FAQ (https://github.com/curl/curl/blob/master/docs/FAQ) section 4.12 (where exactly this error message is mentioned), "it means that curl couldn't verify that the server's certificate was good. Curl verifies the certificate using the CA cert bundle that comes with the curl installation." (vsince CURL 7.10).
As your CURL version is quite old (released on Oct 7, 2015), I would assume that one of the CA/root certificates it is using is too old. I would recommend updating CURL separately (e.g. using this guide: http://pavelpolyakov.com/2014/11/17/updating-php-curl-on-ubuntu/, depending on your OS).
Furthermore, you can check the openssl.cafile option in php.ini that should point to an absolute path containing a more or less recent CA bundle (e.g. "C:\xampp7.3\apache\bin\curl-ca-bundle.crt" for my XAMPP installation). You can try to extract the bundle from the XAMPP .zip (https://www.apachefriends.org/download.html) and replace the path in your php.ini and then restart the server.
In addition, you can check your php.ini if extension=php_openssl.* (extension e.g. dll for Windows) is uncommented, i.e. activated.
Maybe (and this is why I asked what should be shown normally at this place) a script inside the Payunity plugin is trying to fetch something from an URL with a broken certificate or something similar.
EDIT: As pointed out by Sebastian B., you can check the error.log (in case of Apache) for failed file_get_contents() (or similar) calls because the actual URL of the "file" the site PHP tried to fetch is mentioned there.
EDIT: CURL Perl script to create a fresh ca-bundle.crt file based on Mozilla's chain: https://github.com/curl/curl/blob/master/lib/mk-ca-bundle.pl You can try this (or extract one from a fresh CURL installation) and set this as a path in php.ini. Or you can use this from the Nextcloud project (https://github.com/nextcloud/server/blob/master/resources/config/ca-bundle.crt) or another one (just for testing purposes, of course).

Where i can find the default system certificate used by PHP by deafult?

In php.ini i have specified to use the cafile of my domain.
This made email from PHP works and also i have no issue on loading my domain.
I have issue when i try to use composer as seems PHP is using the domain certificate who is not able to get validation.
If i remove the openssl ca.file from php.ini email from PHP stop to work but composer work fine.
Maybe i need find where is the system certificate used by PHP when i remove the openssl line than add the certificate content to mine domain certificate, this should solve the issue.
Any idea of where this certificate can be found?
Do you think this will solve the issue?
https://github.com/composer/composer/issues/7797#issuecomment-440585828
Thanks for the help.
Solved
https://github.com/composer/composer/issues/7797#issuecomment-440680491
In centos the position seems to be little bit different
How to add Certificate Authority in centos7?
I found it on etc/pki/ca-trust/extracted/openssl

SSL Certificate unable to get local issuer certificate php background script

How to solve certificate problem on php background script? The script works if I load it directly in the browser but it isn't working when calling it from system like so:
system ("php somescript.php");
It returned fatal error in console:
Fatal error: Uncaught GuzzleHttp\Exception\RequestException: cURL error 60: SSL certificate problem: unable to get local issuer certificate
What am I missing here?
This is a rather unhelpfully-worded error that the Openssl library logs when it can't or doesn't know how to access the CA certificates on the system, or when it encounters a server/client whose certificate is not signed by a CA it trusts.
Openssl is typically compiled with default CA directory and/or path with the options --configure-ca-path and --configure-ca-bundle.
Check that this is the case for your installation (it most likely is) and that the CA directories do exist and can be read from by your script.
If your script provides its own CA bundle as a replacement for the default ones, also check that the path is correct.
Finally, everything may be set up correctly, in which case the remote certificate's CA isn't trusted by Openssl (since it's not in the list of trusted CAs). In this case you simply have to handle the exception or fix the trust issue, either locally by adding the remote's CA to the bundle or remotely by changing the certificate to one that's trusted.

php LDAP bind on secure remote server Windows fail

I am trying to query a remote LDAP server in a secure connection in a Windows php local test environment. I think I must have the access granted correctly because I can use an LDAP Browser application and that connects to the remote server fine. Also, if I do ' telnet remoteserverurl.com 636' then a blank screen shows up in command prompt, so I am at least connecting. But in my following .php code I get an error on bind: "PHP Warning: ldap_bind(): Unable to bind to server: Can't contact LDAP server in line..."
The same code works in a Linux server. I think there is some kind of missing LDAP libraries in my local php environment for secure LDAP connection? Anyway, here is the code:
$ds=ldap_connect("ldaps://serveraddress.com", "636"); // remote server
//$ds=ldap_connect("ldap://localhost", 389); // works
//putenv('LDAPTLS_REQCERT=never');//doesn't help with secure ldap
//ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); //works for local LDAP server (Open LDAP)
$r=ldap_bind($ds, "cn=xxx,ou=proxy,o=xxx", "passwordxxxx");//throws error for remote
Any idea? Thanks!
Know this is older, but I recently ran into a similar issue when using wordpress 3.x & 4.x on windows 2008 & 2012 (IIS 7.x & 8.x, PHP 5.6).
I had written a plugin for ldap authentication for wordpress - as was trying to get LDAPS (ldap secure over port 636 working).
Couple things:
When using PHP LDAPS, the documentation states you simply prefix the LDAP server with ldaps://. So server1.domain.com for LDAPS should be ldaps://server1.domain.com/ ...note you don't need to pass the port at all for the connection method (per http://php.net/manual/en/function.ldap-connect.php). This is very similar to what the original question has in its submission.
The windows PHP libraries are hard-coded to look for an open ldap config file (ldap.conf) in C:\openldap\sysconf\ldap.conf.
Create the text file mentioned in #2 above - this is where you point to your certificate store. Once you create this file, you can put in TLS_REQCERT never … but this means no certs are verified and all are trusted automatically (essentially) - should only be for testing...never for production, as you defeat part of TLS/SSL security measures (i.e. certifying you are indeed talking to the host you believe you are connected to).
Instead of the insecure TLS_REQCERT never option that seems to be a popular (and perhaps misguided) suggestion on the interwebs...grab the common public cert authority list used by curl and similar - http://curl.haxx.se/ca/cacert.pem. This essentially is what firefox comes with for public certificate authority trusts (i.e. it's why you can install firefox and go to https://amazon.com without a cert warning, etc.).
Drop the cacert.pem file you downloaded (it's just a text file with a bunch of certificate hashes and descriptions) with your PHP install. For instance, say i dumped it with my php install in c:\php5\cacert.pem. Your location may differ, but put it somewhere it can be accessed and will be grouped with php stuff since it is related. Here's a couple shots of the contents of the cacert.pem file to give you an idea of what's inside.
Edit the C:\openldap\sysconf\ldap.conf and add a line for the command TLS_CACERT like pictured.
This should allow you to now trust public, valid certificates just like modern web browsers do, etc. Note that it won't fix internally-issued or self-signed certificate trust issues. But you can easily do that as well by adding your own cert hashs to the cacert.pem file.
To add another certificate as a trusted has in the cacert.pem file, simply get a copy of the certificate in question (you just need to export it to .cer in base64 format - don't need the private key and extension really doesn't matter - just needs to be a hash output). If you've exported it in the right format, you can open the certificate file and see the hash - it will be similar (but not identical) to the screenshot here of the Thawte Server CA example. Simply append the hash you exported to the cacert.pem file and it will be trusted. If you are looking to be clever, you can instead import the issuing certificate for your private-issued certificate - this would then trust any cert signed by the imported cert. If in doubt, you can always just import the presented certificate.
After making such changes, I found it best to restart the web server (iis manager -> web server node -> restart option) so everything using php was reset.
For extra credit, you can use the same cacert.pem file for the curl implementation by editing your php.ini file and putting the full path to the cacert.pem file in the line curl.cainfo =.
Again, I know this is an older post, but I wanted to share what I had learned while hooking up wordpress to eDirectory via LDAPS.
Check your PHP libaries
<?php phpinfo() ?>
Since you can connect using a LDAP client i expect your LDAP runs SSL
Did you copy your SSL Cert?
Copy the server certificates to sys:/php5/cert directory. This location is configurable in php.ini file.
Use "ldaps://" prefix for host name argument or a value of 636 for port number argument in ldap_connect call.
I got this working! #s.lenders' 'answer' (thanks for that) pointed out toward some Certificate issue and indeed it was a Certificate issue. The remove LDAP Server had its Certificate in expired state--I got a warning about that even when I connected using the desktop application (SoftTerra LDAP Browser). So imported the Certificate to my Windows Certificates--SoftTerra LDAP Browser application allowed me that option. And, voila!. SSL LDAP calls are working.
** Update: Not sure if the above Certificate thingy helped or not but here is something more concrete which helped me: It looks like the php LDAP libraries look for a certain conf files; hard-coded to: C\OpenLDAP\sysconf\ldap.conf ?! So I basically created new folders per this info and put an ldap.conf file with nothing but 'TLS_REQCERT never' and that helps! I have tested it..if I were to remove this file then my Secure LDAP queries don't work and fail in the 'bind' step. Note, I am comfortable with the 'never' in my conf because this will be only on my own workstation.
Also note I am on a Windows 7 running IIS 7+ **
Hope this helps someone.

Categories