phpiosrubyapple-push-notificationsapns-php

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


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:

  1. What's wrong with PHP? Is there some bug related to this issue? (I didn't find bug report though)
  2. Any ideas how to solve this issue?
  3. 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.


Solution

  • 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).