pem, cer, p12 and the pains of iOS Push Notifications encryption

The serious pains of setting up a Remote Push Notification in an iOS app come not from coding the app itself. They mostly relate in making the intermediate environment to connect with the Apple Push Notification Servers (APNS) and the security behind it…

I will not refer to anything specific to the xCode or Obj-C coding of Push Notifications here.  A very nice and simple implementation for Apple Push Notifications management is the  PHP-Mysql—Apple-Push-Notification-Server developed by Benjamin Ortuzar. What I’m covering in this post refers on how to create the encryption .pem files needed for the management system to communicate with the APNS.

If you decide not to use Benjamin Ortuzar’s system and go on making your own instead, you’ll probably need all the same the encryption files covered here. If on the other hand you choose to use a service providing Push Notifications like Urban Airship, you won’t need anything from below.

Things taken for granted in this post:

  1. You already have a Apple Developer ID, 
  2. You have already setup the App ID and enabled it for Push Notifications,
  3. You have created the development and production certificate for Push Notifications for your app,
  4. You have the private key in your keychain (it must be there since its needed for the certificate creation above).

All we need is 2 files:

  1. the private key .p12 file (let’s call it pkey.p12). This can be found in the Keys section of the OSX keychain. Right click on it, select export, enter the filename in .p12 file format and enter its password.
  2. the SSL certificate (let’s call this sslcert.cer). For this post, I’m using the development certificate and this can be either downloaded from the Developer Connection website (same page where you created it) or you can simply drag and drop it to Finder/Desktop from the My Certificates section in the keychain.

Having done the above, open Ternimal in a Mac OSX. A Linux distribution with openssl installed will do the job as well (100% compatible and tested on Fedora Core 18). If your Linux doesn’t have openssl, type

sudo yum install openssl

for RedHat-type distributions or

sudo apt-get install openssl

for Debian.

Step 1: The Certificate

At first we need to convert the sslcert.cer to a .pem format the APNS will understand. Just type in Terminal:

$ openssl x509 -inform der -in sslcert.cer -out certificate.pem

and let’s call the output file certificate.pem.

Step 2: The Private Key

Same must be done for the private key. This is a bit more complex as it involves a security pass phrase. Again on Terminal:

$ openssl pkcs12 -nocerts -in pkey.p12 -out pkey.pem

You will be asked to enter the password protecting the pkey.p12 file and then enter a pass phrase that will protect the output pem file. We call the output pkey.pem and we’ll use it later on. Type the password, type the pass phrase, remember the pass phrase and let’s proceed…

Step 3: Merging the pem

Where we need to merge the two files into a single pem file. Extremely simple:

$ cat certificate.pem pkey.pem > apns_cert.pem

The created apns_cert.pem file will have the same pass phrase entered in step 2 and is the file needed for communication with the APNS servers.

At this point, the only thing left is to test if the file is correct.

Step 4: Testing

So far, the required pem file for the APNS communication is ready but we need to test it. The openssl command provides this as well. Since we used the development certificate on this post, we are going to test the sandbox APNS using this command:

$ openssl s_client -connect -cert apns_cert.pem -key apns_cert.pem

And type the pass phrase again. After that, if all is done correctly, the connection will open with a wall of text letting you know what is going on. Typing a couple of characters, it will disconnect which is normal. If there is an error in the file, openssl will give an error message and you’ll have to read the whole wall of text to find what went wrong.

An indication of a successful connection will look something like this:

depth=2 O =, OU = incorp. by ref. (limits liab.), OU = (c) 1999 Limited, CN = Certification Authority (2048)
verify return:1
depth=1 C = US, O = "Entrust, Inc.", OU = is incorporated by reference, OU = "(c) 2009 Entrust, Inc.", CN = Entrust Certification Authority - L1C
verify return:1
depth=0 C = US, ST = California, L = Cupertino, O = Apple Inc., OU = iTMS Engineering, CN =
verify return:1
Certificate chain
0 s:/C=US/ST=California/L=Cupertino/O=Apple Inc./OU=iTMS Engineering/
i:/C=US/O=Entrust, Inc./ is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
1 s:/C=US/O=Entrust, Inc./ is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
i:/ incorp. by ref. (limits liab.)/OU=(c) 1999 Limited/ Certification Authority (2048)
Server certificate

where the certificate data are printed and then:

subject=/C=US/ST=California/L=Cupertino/O=Apple Inc./OU=iTMS Engineering/
issuer=/C=US/O=Entrust, Inc./ is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C
No client certificate CA names sent
SSL handshake has read 4318 bytes and written 2172 bytes
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
Protocol : TLSv1
Cipher : AES256-SHA
Session-ID: D85E15E39624323B4EBA268214077587711A2EEC4FB083C7F79435EC1A0E58EC
Master-Key: D433A396CE2FCF8C97C2E53B1C4F08BFAB738D343D5D4E69D2F6E42268A67EB490AFA554FA59FB4337F7FFA34AFC0A3A
Key-Arg : None
Krb5 Principal: None
PSK identity: None
PSK identity hint: None
TLS session ticket:

followed by the ticket data.

Finally, with the pem file created, you can use it in any application or system you have developed or used to manage Push Notifications. Copy it where it needs to be and start sending…