Push Notification in PHP using PEM file - php

I've been working through Ray Wenderlich's tutorials on push notifications using a PHP Script.
Reference: http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1
http://www.raywenderlich.com/32963/apple-push-notification-services-in-ios-6-tutorial-part-2
I have done a ton of research but I have ran into an issue on part 2 where you run the push.php file using the ck.pem file.
/Applications/XAMPP/bin/php push.php development
My only difference is that I'm using XAMPP and not MAMP.
I have confirmed that all the extensions are installed.
I am able to test out my certificate in the terminal by doing this:
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushChatCert.pem -key PushChatKey.pem
It asks me to enter my passphrase and I do. Everything works successfully in the terminal.
However, when I run the push.php file, I get these errors:
PHP Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
PHP Warning: stream_socket_client(): Failed to enable crypto in
PHP Warning: stream_socket_client(): unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error) in on line 140
Line 140 in the php file is:
$this->fp = stream_socket_client('ssl://' . $this->server, $err, $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
I found these:
iOS push notification does not work when using crontab scheduler
https://devforums.apple.com/message/1054840#1054840
Push notification in PHP
I have literally retried everything. I know my passphrase is correct. I've re-created my certificates, pem files, and keys. Nothing works. Same error every time I do this.
I'm beyond confused.
Does anyone have any suggestions or know what's going on?
Thank you!
UPDATE 1:
I have also tried setting the full path to the ck.pem file that does not fix the error either.

Okay, I finally figured this out! After 2 days of working with this.
So, for anyone else who runs into this issue:
Download the entrust_2048_ca.cer certificate from:
https://www.entrust.com/get-support/ssl-certificate-support/root-certificate-downloads/
Scroll down and get the Entrust.net Certificate Authority (2048)
Then, in the push.php file, after you set the context for the passphrase, add this line:
stream_context_set_option($ctx, 'ssl', 'cafile', 'entrust_2048_ca.cer');

Updated to macOS Sierra 10.12.4
The problem is in new PHP Version in macOS Sierra.
They changed the way it works with certificates.
I would suggest a small changes in the accepted answer to make things explicit.
You have to setup path to entrust_2048_ca.cer and other parameters during creation of the stream context like
$streamContext = stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'cafile' => '/path/to/bundle/entrust_2048_ca.cer',
]
]);
The full explanation and links are here

Update to macOS sierra 10.12.5
please add
stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
The problem is in new PHP Version in macOS Sierra

add
stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
which defeats the server validation.
Credits #One In a Million Apps in a comment above.

Related

I am using laravel version 6. I received below error when try to reset password. How can I Solve this error [duplicate]

After upgrading to PHP 5.6 I get an error when trying to connect to a server via fsockopen()..
The certificate on the server (host) is self-signed
PHP Warning: fsockopen(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
code
if($fp = fsockopen($host, $port, $errno, $errstr, 20)){
$this->request = 'POST '.substr($this->url, strlen($this->host)).' HTTP/1.1'.$crlf
.'Host: '.$this->host.$crlf
.'Content-Length: '.$content_length.$crlf
.'Connection: Close'.$crlf.$crlf
.$body;
fwrite($fp, $this->request);
while($line = fgets($fp)){
if($line !== false){
$this->response .= $line;
}
}
fclose($fp);
}
Have tried
# cd /etc/ssl/certs/
# wget http://curl.haxx.se/ca/cacert.pem
php.ini
openssl.cafile = "/etc/ssl/certs/cacert.pem"
But the script still fails to work
update
This works
echo file_get_contents("/etc/ssl/certs/cacert.pem");
update 2
$contextOptions = array(
'ssl' => array(
'verify_peer' => true, // You could skip all of the trouble by changing this to false, but it's WAY uncool for security reasons.
'cafile' => '/etc/ssl/certs/cacert.pem',
//'CN_match' => 'example.com', // Change this to your certificates Common Name (or just comment this line out if not needed)
'ciphers' => 'HIGH:!SSLv2:!SSLv3',
'disable_compression' => true,
)
);
$context = stream_context_create($contextOptions);
$fp = stream_socket_client("{$host}:{$port}", $errno, $errstr, 20, STREAM_CLIENT_CONNECT, $context);
error
PHP Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
The file that you downloaded (http://curl.haxx.se/ca/cacert.pem) is a bundle of the root certificates from the major trusted certificate authorities. You said that the remote host has a self-signed SSL certificate, so it didn't use a trusted certificate. The openssl.cafile setting needs to point to the CA certificate that was used to sign the SSL certificate on the remote host. PHP 5.6 has been improved over previous versions of PHP to now verify peer certificates and host names by default (http://php.net/manual/en/migration56.openssl.php)
You'll need to locate the CA certificate that was generated on the server that signed the SSL certificate and copy it to this server. If you're using self-signed certificates, you'll need to add the CA cert that was used to sign the remote host's SSL certificate to the trusted store on the server you're connecting from OR use stream contexts to use that certificate for each individual request. Adding it to the trusted certificates is the simplest solution. Just add the contents of the remote host's CA cert to the end of the cacert.pem file you downloaded.
Previous:
fsockopen doesn't support stream contexts, so use stream_socket_client instead. It returns a resource that can be used with all the commands that fsockopen resources can.
This should be a drop in replacement for the snippet you have in your question:
<?php
$contextOptions = array(
'ssl' => array(
'verify_peer' => true, // You could skip all of the trouble by changing this to false, but it's WAY uncool for security reasons.
'cafile' => '/etc/ssl/certs/cacert.pem',
'CN_match' => 'example.com', // Change this to your certificates Common Name (or just comment this line out if not needed)
'ciphers' => 'HIGH:!SSLv2:!SSLv3',
'disable_compression' => true,
)
);
$context = stream_context_create($contextOptions);
$fp = stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, 20, STREAM_CLIENT_CONNECT, $context);
if (!$fp) {
echo "$errstr ({$errno})<br />\n";
}else{
$this->request = 'POST '.substr($this->url, strlen($this->host)).' HTTP/1.1'.$crlf
.'Host: '.$this->host.$crlf
.'Content-Length: '.$content_length.$crlf
.'Connection: Close'.$crlf.$crlf
.$body;
fwrite($fp, $this->request);
while (!feof($fp)) {
$this->response .= fgets($fp);
}
fclose($fp);
}
I faced a similar issue during work with Ubuntu 16.04 by using Docker. In my case that was a problem with Composer, but error message (and thus the problem) was the same.
Because of minimalist Docker-oriented base image I had missing ca-certificates package and simple apt-get install ca-certificates helped me.
Add
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
));
before
mail->send()
and replace
require "mailer/class.phpmailer.php";
with
require "mailer/PHPMailerAutoload.php";
The problem is in new PHP Version in macOS Sierra
Please add
stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
In my case, I was on CentOS 7 and my php installation was pointing to a certificate that was being generated through update-ca-trust. The symlink was /etc/pki/tls/cert.pem pointing to /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem. This was just a test server and I wanted my self signed cert to work properly. So in my case...
# My root ca-trust folder was here. I coped the .crt file to this location
# and renamed it to a .pem
/etc/pki/ca-trust/source/anchors/self-signed-cert.pem
# Then run this command and it will regenerate the certs for you and
# include your self signed cert file.
update-ca-trust
Then some of my api calls started working as my cert was now trusted. Also if your ca-trust gets updated through yum or something, this will rebuild your root certificates and still include your self signed cert. Run man update-ca-trust for more info on what to do and how to do it. :)
Firstable, make sure that you Antivirus software doesn't block SSL2.
Because I could not solve a problem for a long time and only disabling the antivirus helped me
I used the following script to check the issue
<?php
$url = "mail.example.com";// your host which has issue
$orignal_parse = parse_url($url, PHP_URL_HOST);
$get = stream_context_create(array("ssl" => array("capture_peer_cert" => TRUE)));
$read = stream_socket_client("ssl://".$url.":993", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $get);
if (!$read) {
// ssl connection failed for some reason
// could be a certificate error or failure to connect on port 443
echo "Failed to connect to site. Error {$errno}: {$errstr}\n";
} else {
$cert = stream_context_get_params($read);
$certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']);
var_dump($certinfo);
}
?>
I was getting below error
PHP Warning: stream_socket_client(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
Updated ca certificates and then ran reinstall that helped
yum reinstall ca-certificates
You mention the certificate is self-signed (by you)? Then you have two choices:
add the certificate to your trust store (fetching cacert.pem from cURL website won't do anything, since it's self-signed)
don't bother verifying the certificate: you trust yourself, don't you?
Here's a list of SSL context options in PHP:
https://secure.php.net/manual/en/context.ssl.php
Set allow_self_signed if you import your certificate into your trust store, or set verify_peer to false to skip verification.
The reason why we trust a specific certificate is because we trust its issuer. Since your certificate is self-signed, no client will trust the certificate as the signer (you) is not trusted. If you created your own CA when signing the certificate, you can add the CA to your trust store. If your certificate doesn't contain any CA, then you can't expect anyone to connect to your server.
If you are using macOS sierra there is a update in PHP version. you need to have Entrust.net Certificate Authority (2048) file added to the PHP code. more info check accepted answer here Push Notification in PHP using PEM file
Have you tried using the stream_context_set_option() method ?
$context = stream_context_create();
$result = stream_context_set_option($context, 'ssl', 'local_cert', '/etc/ssl/certs/cacert.pem');
$fp = fsockopen($host, $port, $errno, $errstr, 20, $context);
In addition, try file_get_contents() for the pem file, to make sure you have permissions to access it, and make sure the host name matches the certificate.

SSL routines:tls_process_server_certificate:certificate verify failed

Since last night, several of my scripts (on different servers) using file_get_contents("https://...") and curl functions stopped working.
Example request that fails:
file_get_contents("https://domain.tld/script.php");
Error:
PHP Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages:
error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed in /home/domain/public_html/script.php on line 19
I already "fixed" the problem using:
$arrContextOptions=array(
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
),
);
file_get_contents("https://domain.tld/path/script.php", false, stream_context_create($arrContextOptions));
The "fix" is far from ideal since I'm not verifying the authenticity of the connection, but until I understand the origin of the problem and how to prevent it from happening again, I'll be forced to use it.
Notes:
PHP scripts with Curl also stopped working and the fix is similar:
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);;
The SSL certificate is issued by Let's Encrypt and it was renewed last night ("not valid before 2020/12/24");
All servers have the same timezone;
I'm using CentOS 7/Ubuntu 18 and Virtualmin;
If I open "https://domain.tld/script.php" on Firefox/Chrome, no SSL warnings are shown and the certificate is valid;
I've tried to update the CA certificates (yum install ca-certificates.noarch), but the latest version is already installed;
I understand what's wrong, what I cannot figure out is why it started happening and how to fix it (the real fix).
Question:
How to fix and prevent it from happening again?
The problem was an outdated CA certificate and I found the solution on a Let's Encrypt community thread :
Manual Solution:
Replace the contents of /home/[domain]/ssl.ca with lets-encrypt-r3-cross-signed.pem
restart apache/nginx
Virtualmin Solution:
Go to Virtualmin -> Server Configuration -> SSL Certificate -> CA Certificate
Option 1: Choose upload file and use lets-encrypt-r3-cross-signed.pem
Option 2: Paste the contents of lets-encrypt-r3-cross-signed.pem using the Pasted certificate text option.
Press "Save Certificate"
Note:
This issue was fixed on webmin 1.970, so make sure you've the latest version installed, which wasn't my case due to the webmin repo not being enabled. If that's also your case, just enable or add the webmin repo and run yum update.

fockopen: Failed to enable crypto

Similar questions have been answered before, but I haven't been able to solve this particular case.
On a PHP 5.6+ machine when I try to use fsockopen on a particular domain I receive the following (not the real domain):
$ php -r "var_dump(fsockopen(\"ssl://www.domain.net\", 9085, \$errnum, \$errstr, 5));"
PHP Warning: fsockopen(): Failed to enable crypto in Command line code on line 1
PHP Warning: fsockopen(): unable to connect to ssl://www.domain.net:9085 (Unknown error) in Command line code on line 1
bool(false)
This works fine on PHP 5.5, which points to it being by the change in 5.6 dealing with the way fsockopen verifies ssl certificates.
Other connections can be made without issue:
$ php -r "var_dump(fsockopen(\"ssl://www.google.com\", 443, \$errnum, \$errstr, 5));"
resource(4) of type (stream)
Based on other suggestions I've checked the default cert file
$ php -r "print_r(openssl_get_cert_locations());"
Array
(
[default_cert_file] => /usr/lib/ssl/cert.pem
[default_cert_file_env] => SSL_CERT_FILE
[default_cert_dir] => /usr/lib/ssl/certs
[default_cert_dir_env] => SSL_CERT_DIR
[default_private_dir] => /usr/lib/ssl/private
[default_default_cert_area] => /usr/lib/ssl
[ini_cafile] =>
[ini_capath] =>
)
The file /usr/lib/ssl/cert.pem was originally missing, I download the ca bundle from curl and renamed it to match. Still no luck.
I'm not receiving any additional information indicating that verifying the certificate fails. Are there any other ways to debug the issue?
After much head bashing, I finally discovered the cause.
PHP 5.6 implemented a default set of support ciphers, which removed support for older more vulnerable ones. http://php.net/manual/en/migration56.openssl.php
The default ciphers used by PHP have been updated to a more secure list based on the ยป Mozilla cipher recommendations, with two additional exclusions: anonymous Diffie-Hellman ciphers, and RC4. The domain I was attempting to connected currently supports TLSv1/SSLv3, Cipher RC4-MD5
Do work around the issue I switched to stream_socket_client instead of fsockopen. Added RC4-MD5 to stream cipher support in the context:
$context = stream_context_create(['ssl' => [
'ciphers' => 'RC4-MD5'
]]);
$socket = stream_socket_client('ssl://'.$host.':'.$port, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);

iphone push didn't receive (Window Server/IIS/PHP)

First of all, it is not a problem about certification or dev/distribution.
I used distribution certification, and server url -> gateway.push.apple.com:2195
I tested in my local server(apache2), and it work!
However, in my real server(IIS/Window server) I used same php code, same certification file(.pem), and same iphone application... etc, it printed "Connected to APNS Message successfully delivered", but my phone didn't receive message.
One suspect is, I tested my certification file(.pem) by cmd command line 'openssl s_client -connect gateway.push.apple.com:2195 -cert xxxx.pem', It returned "Verify return code: 20 (unabled to get local issuer certificate). So I found solution that downloaded entrust certification(.cer) and did "openssl s_client -connect gateway.push.apple.com:2195 -cert xxxx.pem -CAfile entrust_2048_ca.cer", and it returned 'Verify return code :0'. So I added my php code 'stream_context_set_option($ctx, 'ssl', 'CAfile', 'entrust_2048_ca.cer');', but it didn't work same problem.

XAMPP under Debian 6: cannot open "ssl://" URLs

I am having trouble opening URLs with the ssl:// scheme. Detailing:
phpinfo(): http://pastie.org/2391404
Sample code:
$socket = #fsockopen("ssl://cgw.ubb.bg", 443, $errno, $errstr, 30);
if (!$socket) {
$err = "$errstr ($errno) - " . $socket;
echo "Unable to connect. $err";
} else {
echo "Connected.";
fclose($socket);
}
echo PHP_EOL;
What I get in response is this:
Unable to connect. (0) -
(Yes, I did try to connect with telnet/browser/etc., the host/port combo is not the problem.)
In other words, errno is 0, and no error message is provided.
When I put this in /opt/lampp/etc/php.ini:
extension="openssl.so"
I am getting this error:
Warning: PHP Startup: Unable to load dynamic library '/opt/lampp/lib/php/extensions/no-debug-non-zts-20090626/openssl.so' - /opt/lampp/lib/php/extensions/no-debug-non-zts-20090626/openssl.so: cannot open shared object file: No such file or directory in Unknown on line 0
Alas, there is indeed no openssl.so file in there.
My XAMPP platform is: i686-pc-linux-gnu.
My question is:
Can I "cheat" somehow by just downloading the proper file and be done with it?
If not, how could I compile it from source and integrate it into XAMPP without ripping it apart?
EDIT:
It's a shame no viable suggestions were given thus far. Having in mind I used a ready-made XAMPP installation (basically unpacked the archive) and that XAMPP is pretty broadly used, I am very surprised nobody else stumbled upon this problem.
After losing few hours to debug this problem on debian (squeeze with libssl0.9.8 installed) I've found a solution.
I'm using a dotdeb.org php version: php5-cgi 5.3.13-1~dotdeb.0 (doesn't seems to work with debian default package 5.3.3-7+squeeze13! - no_ticket is still missing in this version).
The solutions is to open the socket like this:
<?php
$host = "ssl://cgw.ubb.bg";
$context = stream_context_create();
$result = stream_context_set_option($context, 'ssl', 'no_ticket',true);
$socket = stream_socket_client($host.':443', $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);
afterwards you simply use the socket as it was a fsockopen resource.
From your phpinfo(), openssl is available :
openssl
OpenSSL support => enabled
OpenSSL Library Version => OpenSSL 1.0.0c 2 Dec 2010
OpenSSL Header Version => OpenSSL 1.0.0c 2 Dec 2010
PHP allows building openssl into the core, and in that case it does not need to be in a separate extension. Does a file_get_contents("https://cgw.ubb.bg/") gives you the same error ?
EDIT : This seems to be a bug in openssl : I tried the sample I provided, and got the following error :
error:140773F2:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert unexpected message
After googling it, I found multiple bug reports / forum posts that all seem to say it is a problem between the openssl lib and the remote server. Apparently, forcing openssl to sslv3 corrects the problem, but I don't know how to do this with php.
Some bug reports (not related to php) :
https://bugs.launchpad.net/ubuntu/+source/curl/+bug/595415
http://forum.parallels.com/printthread.php?t=82502

Categories