I've had the experience in the last month of building my first iOS / iPad application. This is quite the experience learning Objective-C and all the quirks to get an application running on an iPad. I'll say that I really like the experience, and thin that Objective-C is a very interesting language, simultaneously being both archaic and powerful in the way it handles objects and messaging. It feels to me a lot like Smalltalk, which I haven't used since the late 80's in undergrad school. You can really tell that when Next created Objective-C in that same timeframe that they took a lot of the ideas from Smalltalk into consideration.
But this post is not about Objective-C or Smalltalk. It is about how to write a C# application that can send messages to a iOS device using Apple Push Notifications (APN's). The need for having this done in C# is a requirement of the system I have been developing, as all the internal systems are written in .NET and therefore the easiest and most maintainable code for sending messages would be in C#. It is obviously possible to do this in Objective-C, and a good example of doing this is provided in the PushMeBaby application that you can find on the Internet. But it doesn't make sense for my customer to have an apple system running in their data center just to send these messages, and hence the need to use C#.
This seemed to me like it would be a fairly simple task at first as the PushMeBaby code is Objective-C but is very close to native C and uses sockets and SSL to communicate with Apple. I first set out to make a fairly honest port of PushMeBaby into C#, but this ended up having several difficulties, of which the most significant was in getting the correct certificates in place to authenticate with SSL to Apple's servers. During trying to fix this I also came across the APNS-Sharp library which also worked to greatly simplify the process, but still left the issue of correctly generating and using certificates. There was even some discussion on the APNS-Sharp website covering how to create the required certificates and use them in APNS-Sharp code, but I still did find this discussion to not completely explain the process effectively to a noob like myself.
So, I will explain it here for you so you may not have to waste your time when you try this. :)
As the audience of this post is intended to be C# developers trying to integrate with APNS, there are a few concepts that first must be taken into consideration when working with APN's which are different from most .Net development. Primary amongst these are that to deploy an iOS application you need to go through several steps in the iOS provisioning portal to generate several certificates for both your development account as well as for authenticating with APN servers. I'll briefly go over how to accomplish these tasks. Second, you will need to convert the certificate given to you by Apple for APN authentication from a .CER file to a .P12 file that you can use in your C# application to do the authentication. Objective-C applications can directly use the .CER file (like PushMeBaby does), but that file does not work in C#/SSL or with APNS-Sharp and you need to convert it to a .P12 file. In a nutshell the steps you will need to go through to successfully talk to the APN's servers follows these steps:
- Register with Apple for an iOS developers certificate
- Register the device(s) with Apple so that the devices can be authenticated
- Create an application ID which is used to uniquely identify your application to Apple's services
- Inform APN services that you want to deliver messages to that application
- Provision your application to be allowed to run on specific devices
This entire process is quite a bit different from .Net development and deployment. These steps are required by Apple for various reasons, primarily of which is security and quality of experience. You need to prove to Apple that you are a registered developer (step 1), let them know all the devices you will deploy your applicaiton to (step 2), let Apple know your application by name and code signature (steps 3 and 5), which devices are allowed to run your application (step 5), and that you want to them to build channels between your application and APN servers for you to deliver message to your users.
It's worth noting briefly that these steps are required for an enterprise deployment of the application. Device registration and application provisioning is not strictly needed when deploying your application to the App Store.
The process begins with your requesting a developer certificate from Apple. Although the audience for this post is C# developers, it is assumed that you do have access to a Mac to perform several of these tasks. Requesting a developer certificate starts with using the KeyChain appliction in OSX. Open the Keychain Access application from the utilities folder on the mac:

When the application starts, make sure that the login keychain is selected, and the category specified is 'Certificates'. You should see something similar to the following:

At this point, I do not have any certificates installed on my system. What I need to do right now is create a developer certificate in the apple developer portal and install it onto my development machine. To do this, you first use the keychain access application / certificate assistant / Request a certificate from a certificate authority menu item:

Selecting that menu item you will be presented with the following screen. Enter your e-mail address, your common name (typically your name), and select the 'Saved to disk' radio button:

Press continue and save the certificate request to your disk and press the done button. You will need this file at two points later in the process.
With the certificate request created, no go into the iOS developer portal, and more specifically the provisioning portal, and click on 'Certificates' in the left column. You should see a screen similar to the following, where I do not have have any developer certificates created / installed.

Click on the 'Request Certificate' button and you will be shown this page:

Click the 'Choose File' button and select the certificate request file you created in prevously.



After you have selected the request file, press submit and Apple will generate you a developer certificate that you can use to build iOS applications. You will first see a screen such as the following while the certificate is being generated:

Refresh the page occasionally, and after a few seconds you will see somthing similar to the following, which is a reference to your developer certificate. Press the download button and save the certificate to your computer (the default filename is developer_identity.cer).


You will then need to install this into the keychain access program by double clicking the .cer file after it is saved to your system. This will cause the keychain access program to install the certificate and you should see something similar to the following in the keychain access application:

Note that I have expanded the top level item to show the private key that is part of the certificate. You will need that private key to generate a certificate file for the C# application to use. Right click on the private key and select export and save the file as a .p12 file, and I'm going to use developer_private_key.p12 as its name. You will be asked to prive a password for the file which will be used to encrypt the data in the file. Create a password and save it as it will be needed later.

You also will need to authenticate with OSX as having permission to export the key. Enter your password so that the .p12 file can be created...
Now i'm going to step the process by where you register your iOS device in the developer portal with the assumption that this is already completed. The next step is to create an App ID which identifies the application to Apple and the APN servers (after some configuration). I already have an App ID created for my example, MobileMarketInfo, which an be seen in the following list.

If you need to create a new App ID, click the 'New App ID' button, and you will be presented with the following screen:

Enter a unique string for the App ID and Bundle ID. By convention I make these identical. Also, leaving 'Generate New' selected as the selection for creating a Bundle Seed ID is all that is needed. Press the 'Submit' button and the app ID will be created and you will be taken back to the main App ID screen where you will be able to see your new App ID. It will look similar to the following (different name of course for your application):

This is now where configuration of the application to receive APN's is done. You will notice two amber icons in the list, one for development and the other for production. For purposes of this post, I will demonstrate only using the development environment. Click the 'Configure' button and you will be taken to the following screen to configure APN's for your application:

Check the 'Enable for Apple Push Notification service' check box, and the two 'Configure' buttons will be enabled. Press the 'Configure' button for the 'Development Push SSL Certificate'. You will then be presented with a series of dialogs to guide you through creating the SSL certificate. The first screen is as follows:

Technically, you can follow these instructions and generate a new certificate request file with the keychain access application, but I find it suffices to simply use the one we created earlier for your developer account. If you don't have this available, then just create a new one. Press 'Continue' and the following screen is presented:

I've chosen the request file I generated earlier. Now press 'Generate' and you will be shown the following status panel:

And when the generation is done you should see the following:


Press 'Continue' again, and you will be shown the following screen:

Save the certificate to your system and press 'Done'. By default, the file will be named 'aps_developer_identity.cer'. We will be using this file in an upcoming step(s) to create a .p12 version of that certificate that the C# application can use for authentication. When this is all done, you should see the following screen and the amber icon on the 'Development Push SSL Certificate' should now be green. At this point, the APN service at apple is ready to receive messages from your service to deliver to your application.

But wait, we're not quite done in the portal yet. It is still needed to create a provisioning profile for the application so that XCode can sign the application, thereby allowing it to be run on specific iOS devices. To do this, click on the Provisioning link in the left column, and you will see all of your provisioning profiles:

I don't currently have one created at this point, so I click on the 'New Profile' button and get taken to the following screen to create the profile:

I've given the profile the same name as the application, specified that I want to use my developer certificate to sign the profile, and that I want to allow my iPad 2 to be able to run the application. Press submit and you will be shown that the profile is being generated, and after a few seconds refresh the page and you will see it has been created.

Download the provisioning file and drag it onto the XCode icon, where XCode will install the provisioning file into the organizer and use it to sign your application. A result of dragging the file onto the XCode icon is the display of the Organizer application showing that the profile is installed:

Phew!!! A lot of work so far, and we are not done. At this point you would be able to use the PressMeBaby application to send notifications, but we require more work to be done to get .NET / C# to be able to push notifications. These steps basically convert the aps_developer_identity.cer certificate into a .p12 file that you can use in the C# application.
I originally following the instructions to do this that are found on the APNS-Sharp project site (click to see this). I had some issues with what was written here, so I will show you the sequence of commands required to get this done. We will need to open the terminal application to issue several commands to convert the certificate into a .p12 file. I stored all of these files on my desktop and with these files available, we now need to issue four openssl commands in order as shown in the following terminal window (assuming your files are named the same as mine - otherwise you need to change those filenames):

Of importance here is the aps_developer_identity.p12 file, which we will use in the .Net / C# application to authenticate with the APN servers. With that in hand, it is now possible to write the C# application. My example wil be using the APNS-Sharp library, so make sure you have downloaded that. Also, I am going to assume that you have written the iOS application and had it register for notifications, and that you have obtained the device token needed to address that device.
Now what is needed to copy the aps_developer_identity.p12 file to my .NET developmet VM (I put in in c:\). The code I show here is simply a slight modification to the JdSoft.Apple.Apns.Test application to use the aps_developer_identity.p12 file I generated with the various openssl commands, and is as follows:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using JdSoft.Apple.Apns.Notifications;
6:
7: namespace JdSoft.Apple.Apns.Test
8: {
9: class Program
10: {
11: [STAThread]
12: static void Main(string[] args)
13: {
14: NotificationService service = new NotificationService(true, "c:\\aps_developer_identity.p12", "password!123", 1);
15:
16: service.SendRetries = 5;
17: service.ReconnectDelay = 5000;
18:
19: service.Error += new NotificationService.OnError(service_Error);
20: service.NotificationTooLong += new NotificationService.OnNotificationTooLong(service_NotificationTooLong);
21:
22: service.BadDeviceToken += new NotificationService.OnBadDeviceToken(service_BadDeviceToken);
23: service.NotificationFailed += new NotificationService.OnNotificationFailed(service_NotificationFailed);
24: service.NotificationSuccess += new NotificationService.OnNotificationSuccess(service_NotificationSuccess);
25: service.Connecting += new NotificationService.OnConnecting(service_Connecting);
26: service.Connected += new NotificationService.OnConnected(service_Connected);
27: service.Disconnected += new NotificationService.OnDisconnected(service_Disconnected);
28:
29: Notification alertNotification = new Notification("b0a2f562eeb993d98961834abecad051f7804132b5b058509eaab396d546414a");
30:
31: alertNotification.Payload.Alert.Body = string.Format("Testing...");
32: alertNotification.Payload.Sound = "default";
33: alertNotification.Payload.Badge = 1;
34:
35: if (service.QueueNotification(alertNotification))
36: Console.WriteLine("Notification Queued!");
37: else
38: Console.WriteLine("Notification Failed to be Queued!");
39:
40: Console.WriteLine("Cleaning Up...");
41:
42: service.Close();
43: service.Dispose();
44:
45: Console.WriteLine("Done!");
46: Console.ReadLine();
47: }
48:
49: static void service_BadDeviceToken(object sender, BadDeviceTokenException ex)
50: {
51: Console.WriteLine("Bad Device Token: {0}", ex.Message);
52: }
53:
54: static void service_Disconnected(object sender)
55: {
56: Console.WriteLine("Disconnected...");
57: }
58:
59: static void service_Connected(object sender)
60: {
61: Console.WriteLine("Connected...");
62: }
63:
64: static void service_Connecting(object sender)
65: {
66: Console.WriteLine("Connecting...");
67: }
68:
69: static void service_NotificationTooLong(object sender, NotificationLengthException ex)
70: {
71: Console.WriteLine(string.Format("Notification Too Long: {0}", ex.Notification.ToString()));
72: }
73:
74: static void service_NotificationSuccess(object sender, Notification notification)
75: {
76: Console.WriteLine(string.Format("Notification Success: {0}", notification.ToString()));
77: }
78:
79: static void service_NotificationFailed(object sender, Notification notification)
80: {
81: Console.WriteLine(string.Format("Notification Failed: {0}", notification.ToString()));
82: }
83:
84: static void service_Error(object sender, Exception ex)
85: {
86: Console.WriteLine(string.Format("Error: {0}", ex.Message));
87: }
88: }
89: }
When running the application, and waiting a few seconds while watching the iPad, the following message is delivered...

Success!
Over the next few days I'll elaborate further on some of the details of APN messages, including passing data and assigning badge numbers.