Only from PHP: Unable to connect APNS gateway.push.apple.com:2195 - php

It's a late night. I just spent 10 hours in google/stackoverflow search and experiments. And seems I hate Apple Push Notifications. I'm totally frustrated and will appreciate any help.
Thank you.
The problem:
The PHP code for sending Apple Push Notifications, which successfully worked two weeks ago stopped to work now and throws following errors:
PHP Warning: stream_socket_client(): Failed to enable crypto in /home/...
PHP Warning: stream_socket_client(): unable to connect to ssl://gateway.push.apple.com:2195 (Unknown error) in /home/...
It stopped to work on two separate servers, which are using separate scripts for APNs sending.
Environment:
Servers: CentOS 6.5 with PHP 5.4.32 and Ubuntu 14.04.3 with PHP 5.5.9
APN: In production mode
Certificates: tested with 700+ push notifications.
One of the servers is using https://github.com/immobiliare/ApnsPHP, other - https://github.com/antongorodezkiy/wp-apn, on localhost I tested simple file without using any third party code.
Investigation:
For all cases below I used the same active device token and the same production PEM certificate.
php
However, even this simple code doesn't work on both servers and localhost and return the same error as above:
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', '/absolute/path/to/apn_prod.pem');
// Open a connection to the APNS server
$fp = stream_socket_client(
'ssl://gateway.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
I also tried to play with stream_context_set_option() options, include entrust_2048_ca.cer, etc, and even some options from this article. Although provided code worked without any modifications before August 2015.
openssl
Connection worked with openssl (link):
openssl s_client -connect gateway.push.apple.com:2195 -cert /absolute/path/to/apn_prod.pem -debug -showcerts -CAfile /absolute/path/to/server-ca-cert.pem
And got with CONNECTED(00000003) and Verify return code: 0 ( ok ).
telnet
Connection worked with telnet:
-sh-4.1$ telnet gateway.push.apple.com 2195
Trying 17.172.233.150...
Connected to gateway.push.apple.com.
pecl apn
It didn't send push notification. I just tried to use adaptation of sample code, but got the error Invalid token. The token is active and same token I used everywhere and for Houston and Ruby too.
houston
It worked with Houston
apn push "0346a53f...231d9d6abe11" -c /absolute/path/to/apn_prod.pem -m "Hello from the command line!" -e "production"
ruby
I'm not a Ruby programmer (yet at least), but after success with Houston, I found and adapted Ruby code without Houston dependency.
And it worked:
#!/usr/bin/env ruby
require 'openssl'
require 'socket'
require 'json'
token = "0346a53f...231d9d6abe11"
cert = File.read("/absolute/path/to/apn_prod.pem")
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = OpenSSL::PKey::RSA.new(cert, '') #set passphrase here, if any
ctx.cert = OpenSSL::X509::Certificate.new(cert)
sock = TCPSocket.new('gateway.push.apple.com', 2195) #development gateway
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.connect
payload = {"aps" => {"alert" => "Oh hai!", "badge" => 1, "sound" => 'default'}}
json = payload.to_json()
token = [token.delete(' ')].pack('H*') #something like 2c0cad 01d1465 346786a9 3a07613f2 b03f0b94b6 8dde3993 d9017224 ad068d36
apnsMessage = "\0\0 #{token}\0#{json.length.chr}#{json}"
ssl.write(apnsMessage)
ssl.close
sock.close
puts "End"
Questions:
What's wrong with PHP? Is there some bug related to this issue? (I didn't find bug report though)
Any ideas how to solve this issue?
Any ideas what could be the difference in PHP and Ruby cases (I assume that Python or Perl could work fine too)? I even tried to read PHP sources, but without success to understand how stream_socket_client() implemented.
Please help.

I've found the issue and fixed it.
The problem was in .pem certificate. Somehow there were two certificates in one file for both production and development .pem files. The same .pem file with two certificates was in the repo for a long time but APNs stopped to work only few months ago. Maybe something was upgraded/changed on the Apple's side.
I assume the Ruby code somehow removes certificate duplication or maybe it took only first certificate, so it worked in Ruby.
However, the solution was to remove the second certificate from the .pem file. After that APNs started to work and they work now (I received some just yesterday).

If you're just reusing an old Certificate Signing Request (CSR), make sure to remove the expired/old APNs certificate from your Keychain before exporting the new one and its private key as p12 file. If you don't do that, the PEM file you generated out from the exported p12 will still contain the expired/old certificate which doesn't goes well with Apple's Push provider. Thus resulting to unable to connect to ssl....

Related

Trying to connect SSL server using fsockopen()

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.

curl https://SERVER_IP:8443 works on Mac but not when I SSH into server

Via terminal on a Mac (PHP 7.1.23), I can issue a curl (curl 7.54.0) command via the CLI such as:
curl https://www.SERVER_HOSTNAME:8443
and it works without error.
I SSH into the VPS Linux server running x86_64-redhat-linux-gnu with curl 7.61.1 and PHP 7.3.5 and if I do a curl command without the port:
curl https://www.SERVER_HOSTNAME
it works fine but as soon as I add the port it chokes.
curl https://www.SERVER_HOSTNAME:8443
gives this error:
"SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above.
The actual certificate is not self signed. It is via Godaddy with a Bundle and a good CA.
Any suggestions on what I need to fix or set?
Thanks in advance.

APNS + PHP "stream_socket_client(): Failed to enable crypto"

I'm having trouble with using APNS with PHP and getting the following message:
stream_socket_client(): Failed to enable crypto
The problem only happens sometimes, and other times it would actually send the push.
Since I have the test script on a loop of 10 iterations, I would sometimes get this:
stream_socket_client(): SSL: Connection reset by peer
I'm testing using the sandbox server tls://gateway.sandbox.push.apple.com:2195
Here is what I tried:
I tried to reissue the PEM and all certificates with it.
I played around with the request protocol sslv3:// and tls://.
I played around with the passphrase (push worked without the passphrase btw)
I tried searching stackoverflow for a solution and nothing worked.
Checked pem file permissions 644
Checked pem parent directories permissions 755
It seems that all the solutions I found on Google and SO are people having problem pushing altogether.
I feel like the service is rate limited maybe? Because we waited a while (around 15 minutes) and then tried it again, and was able to successfully push around 100 messages until I started getting that message again.
The sandbox push service is rate limited. I have experienced this myself when testing but have never encountered any such limit using the production API.
You might also be hitting their other protections.
Are you opening a connection, sending a message, closing connection and then looping and doing it all over again?
That will get your notifications dropped. Apple wants you to send several push notifications using the same connection, not a new one each time.
Best Practices for Managing Connections
You may establish multiple connections to the same gateway or to
multiple gateway instances. If you need to send a large number of
remote notifications, spread them out over connections to several
different gateways. This improves performance compared to using a
single connection: it lets you send the remote notifications faster,
and it lets APNs deliver them faster.
Keep your connections with APNs open across multiple notifications;
don’t repeatedly open and close connections. APNs treats rapid
connection and disconnection as a denial-of-service attack. You should
leave a connection open unless you know it will be idle for an
extended period of time—for example, if you only send notifications to
your users once a day it is ok to use a new connection each day.
From Apple Docs # https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html
My PHP code was generating following error:
PHP Warning: stream_socket_client(): Failed to enable crypto in /private/tmp/t.php on line 12
PHP Warning: stream_socket_client(): unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error) in /private/tmp/t.php on line 12
PHP Warning: fclose() expects parameter 1 to be resource, boolean given in /private/tmp/t.php on line 24
The problem was, the damn certificate, expired the day before yesterday! :-) Can you believe this?
So, I need to recreate my PEM file.
It is not necessary recreate your pem file
that error happens when you use an incorrect PassPhrase
regards
Emiliano
I had this problem. Disappeared after giving write permission for 'everyone' for the .pem file.
I have this problem because I foolishly forgot to include the file extension (.pem) when supplying the file path for local_cert.
few checks :
device token should be - with out spaces and with out < or >
make sure the path of certificate is correct and expired date of it.
make sure the passphrase you are using is the one u used to make certificate
In my case, the issue was with my mac (OSX Sierra). I uploaded php and cert to my server, ran it, and the notification was delivered.
I tried examples from book of Marin Todorov iOs 6 by Tutorials.
And before I could send push notifications for automatical update I had a lot of headache cause of handshake error - stream_socket_client(): Failed to enable crypto.
I did all of what I found in Stackoverflow - changed permissions on certificate and others.
What I did eventually?
I created selfsigned SSL certificate and setup Apache for serving SSL.
Also I changed SSL protocol from ssl to tls in hostname:
tls://gateway.push.apple.com:2195
After that service works.

APNS Openssl Connection from PHP for Apple Push Notification?

Finally i have sent a notification from my local server to my device. I followed this tutorial http://www.raywenderlich.com/3525/apple-push-notification-services-tutorial-part-2 and many of the peoples from stack overflow helped me to reach this. I Thank you all my friends.
I have one doubt on the server setup. For my local use i have used MAMP for Apache and MySQL servers. Finally i open the ssl from Terminal used certificate.pem and key.pem. Is there any way to open ssl from php script. But, i don't know any single script of php because i am ios developer. Sorry for this.
Yesterday i have used below commands in my Terminal to open ssl and connect to APNS,
unknownc42c032e8297:~ gopi$ /Applications/MAMP/bin/php/php5.3.6/bin/php /Users/gopi/Desktop/PushChatServer/push/push.php development
unknownc42c032e8297:~ gopi$ cd /Users/gopi/Desktop/PushChatServer/push
unknownc42c032e8297:push gopi$ telnet gateway.sandbox.push.apple.com 2195
Trying 17.149.34.66...
Connected to gateway.sandbox.push-apple.com.akadns.net.
Escape character is '^]'.
Connection closed by foreign host.
unknownc42c032e8297:push **gopi$ openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert gopiAPNCert.pem -key gopiAPNKey.pem**
Enter pass phrase for gopiAPNKey.pem:
CONNECTED(00000003)
.
.
.
.
Verify return code: 0 (ok)
---
creagx
closed
unknownc42c032e8297:push gopi$ /Applications/MAMP/bin/php/php5.3.6/bin/php /Users/gopi/Desktop/PushChatServer/push/push.php development
^C
Is there any way to openssl from our php file? It is possible or Terminal usage is better and is the only way for this? Can anyone please help me on this? Thanks in advance.
To post the Terminal output from a command to PHP , you can use exec() , system() or passthru(). The usage is slightly different among these functions, but they basically do the same job.

Execute Apple Push Notification Server (PHP) from Android Device using "PHP for Android"

I recently downloaded and installed "PHP for Android." I created a .php file that utilizes an SSL connection with port 2195. I followed a guide for writing a php server that sends push notifications to Apple's APNS, and SUCCESSfully ran it on my Mac. When I put both the .php and the .pem onto my Android phone in the same folder and tried to run it with the same WIFI connection, I get the following error message:
Error:14094410:SSL routines:func(148):reason(1040) in /mnt/sdcard/sl4a/scripts/lot.php on line 19
Warning: stream_socket_client(): Failed to enable crypto in /mnt/sdcard/sl4a/scripts/lot.php on line 19
Warning: stream_socket_client(): unable to connect to ssl://gateway.sandbox.push.apple.com:2195 (Unknown error) in /mnt/sdcard/sl4a/scripts/lot.php on line 19
Does anyone have any ideas of how I can fix this?
THANKS!
I had the SSL error on my Windows desktop, with a PHP script that accessed a HTTPS resource.
The solution was to enable the openssl extension. So maybe it's the same kind of problem you have here, something related to opensll on Php for Android.
I'm not sure how to fix it, but Google has it's own push notification framework. There is an example here: http://code.google.com/android/c2dm/
Edit to Answer:
I think that most likely the problem has to do with the certificates needed to make the call over SSL. It is possible that the PHP for Android app does not have permissions to access security certificates you need.

Categories