Origins : http://blog.naver.com/melilyoo/140104172738
우선 이곳에서 푸시서비스를 등록해 보자.
http://ti-agile.blogspot.com/2010/01/programming-apple-push-notification.html
Device에 토큰 받아오는 부분까지 성공했다면.....
자바로 프로바이더 부분을 구현해보자.
KeyManagerFactory의 인증은 내가 가진 apple의 인증서를 넣어야 한다.
그리고 TrustManagerFactory의 인증은 공인인증된 인증서를 넣어야 한다. 그래서 java의 공인인증서를 넣은것이다.
그런다음 소켓을 연결하고 byte형식으로 토큰을 보내면 된다. ^^V
토큰은 아래와 같이 저장되었다. 알아보기 쉽게 하기 위해서 HEX값으로 표기했다. 보툥 0x를 빼고 저장하던데... 그냥 0x까지 붙여넣었다.
0x6d0x4b0x950x440x6d0xe30x650x8f0xd90xe30xab0x5c0x090xcb0x970xd20x1b0x6f0x880xfe0x810xa50xf80xf70x030xfa0xeb0x7f0xc00x130xe50xc0
====== Source ======================================================================================================
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import kr.co.confitech.girlboard.common.RowData;
public class APNSProvider{
private final String HOST = "gateway.sandbox.push.apple.com";
private final int POST = 2195;
private SSLSocket socket = null;
public void provider(String[] tokens, RowData data) throws Exception{
try{
// Apple PNS 인증
char[] password = "confitech".toCharArray();
FileInputStream fin = new FileInputStream("C:/Java/jdk1.5.0_16/jre/lib/security/pns.p12");
KeyStore ks1 = KeyStore.getInstance("PKCS12");
ks1.load(fin, password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks1, password);
// 공인 인증서 인증
char[] password1 = "changeit".toCharArray();
fin = new FileInputStream("C:/Java/jdk1.5.0_16/jre/lib/security/cacerts");
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(fin, password1);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
fin.close();
// 소켓 연결
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLSocketFactory sf = ctx.getSocketFactory();
socket = (SSLSocket)sf.createSocket(HOST, POST);
if(!socket.isConnected()){
return;
}
socket.setSoTimeout(10000);
socket.startHandshake();
String msg = "{\"aps\":{\"alert\":\""+data.get("alert")+"\",\"badge\":"+data.get("badge");
if(data.get("sound") == null){
msg = msg + "}}";
}else{
msg = msg + ",\"sound\":\""+data.get("sound")+"\"}}";
}
System.out.println(msg);
OutputStream out = socket.getOutputStream();
for(int i=0; i<tokens.length; i++){
String strToken = tokens[i];
ByteArrayOutputStream bout = new ByteArrayOutputStream();
//1. 0
bout.write((byte)0x00);
//2. 32 (token length)
bout.write(to2ByteArray(32));
//3. token
for(int j = 0; j < 32; j++){
String s = strToken.substring((j * 4) + 2, (j * 4) + 4);
int z = Integer.parseInt(s, 16);
bout.write((byte)z);
}
//4. message length
bout.write(to2ByteArray(msg.getBytes().length));
//5. message
bout.write(msg.getBytes());
// 푸시 보내기
out.write(bout.toByteArray());
out.flush();
}
out.close();
socket.close();
}catch(Exception e){
e.printStackTrace();
}
}
/**
*
* @param i
* @return
*/
public static byte[] to2ByteArray(int i){
byte[] b = new byte[2];
b[0] = (byte)((i >>> 8) & 0xFF);
b[1] = (byte)((i >>> 0) & 0xFF);
return b;
}
}
====== end ======================================================================================================
Generating a Certificate Request

Figure 1. Generating a certificate request

Figure 2. Saving the certificate request to disk

Figure 3. Naming the certificate request
Creating an App ID

Figure 4. Launching the iPhone Developer Program Portal

Figure 5. The welcome screen of the iPhone Developer Program Portal

Figure 6. Clicking on the App ID tab
net.learn2develop.MyPushApp. Click Submit (see Figure 7).
Figure 7. Creating a new App ID

Figure 8. Viewing the newly created App ID
Configuring an App ID for Push Notifications

Figure 9. Configuring an App ID for push notification service

Figure 10. The Apple Push Notification service SSL Certificate Assistant screen

Figure 11. Generating the SSL certificate

Figure 12. The APNs SSL certificate that is generated

Figure 13. Downloading the certificate generated
aps.developer.identity.cer. Double-click on it to install it in the Keychain Access application (see Figure 14). The SSL certificate will be used by your provider application so that it can contact the APNs to send push notifications to your applications.
Figure 14. Installing the generated certificate into the Keychain Access application
Creating a Provisioning Profile

Figure 15. Selecting the Provisioning tab
MyDevicesProfile as the profile name. Select PushAppID as the App ID. Finally, check all the devices that you want to provision (you can register these devices with the iPhone Developer Program Portal through the Devices tab). Click Submit (see Figure 16).
Figure 16. Creating a new provisioning profile

Figure 17. Pending the approval of the provisioning profile
Provisioning a Device
MyDevicesProfile.mobileprovision file onto the Xcode icon on the Dock.MyDevicesProfile installed on the device (see Figure 18).
Figure 18. Viewing the installed provisioning profile
Creating the iPhone Application
beep.wav in this example) onto the Resources folder in Xcode (see Figure 19).
Figure 19. Adding a WAV file to the project

Figure 20. Entering the App ID for the application
net.learn2develop.MyPushApp.
Figure 21. Selecting the profile for code signing
ApplePushNotificationAppDelegate.m file, type the following code in bold:#import "ApplePushNotificationAppDelegate.h" #import "ApplePushNotificationViewController.h" @implementation ApplePushNotificationAppDelegate @synthesize window; @synthesize viewController; - (void)applicationDidFinishLaunching:(UIApplication *)application { [window addSubview:viewController.view]; [window makeKeyAndVisible]; NSLog(@"Registering for push notifications..."); [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)]; } - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSString *str = [NSString stringWithFormat:@"Device Token=%@",deviceToken]; NSLog(str); } - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err { NSString *str = [NSString stringWithFormat: @"Error: %@", err]; NSLog(str); } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { for (id key in userInfo) { NSLog(@"key: %@, value: %@", key, [userInfo objectForKey:key]); } } - (void)dealloc { [viewController release]; [window release]; [super dealloc]; } @end
Command-R to test the application on a real device. Press Shift-Command-R in Xcode to display the Debugger Console window. Observe carefully the device token that is printed (see Figure 22). In the figure below, the token is:38c866dd bb323b39 ffa73487 5e157ee5 a85e0b7c e90d56e9 fe145bcc 6c2c594b. Record down this device token (you might want to cut and paste it into a text file).
Figure 22. Viewing the device token for push notification

Figure 23. Viewing the Notifications item in the Settings application
Creating the Push Notification Provider
1. Communicate with the APNs using the SSL certificate you have created earlier.
2. Construct the payload for the message you want to send.
3. Send the push notification containing the payload to the APNs.

Figure 24. Format of a push notification message
The payload is a JSON formatted string (maximum 256 bytes) carrying the information you want to send to your application. An example of a payload looks like this:
{ "aps": { "alert" : "You got a new message!" , "badge" : 5, "sound" : "beep.wav"}, "acme1" : "bar", "acme2" : 42 }
Right-click on the Resources folder in Xcode and select Add Existing Files…. Select theaps.developer.identity.cer file that you have downloaded earlier (see Figure 25).
Figure 25. Adding the SSL certificate to the application
ApplicationDelegate.m file, modify the code as shown in bold below:- (id)init { self = [super init]; if(self != nil) { self.deviceToken = @"38c866dd bb323b39 ffa73487 5e157ee5 a85e0b7c e90d56e9 fe145bcc 6c2c594b"; self.payload = @"{\"aps\":{\"alert\":\"You got a new message!\",\"badge\":5,\"sound\":\"beep.wav\"},\"acme1\":\"bar\",\"acme2\":42}"; self.certificate = [[NSBundle mainBundle] pathForResource:@"aps_developer_identity" ofType:@"cer"]; } return self; }

Figure 26. Granting access to the SSL certificate
{ "aps": { "alert" : "You got a new message!" , "badge" : 5, "sound" : "beep.wav"}, "acme1" : "bar", "acme2" : 42 }

Figure 27. Receiving a Push Notification message
Command-R and send a push message from the PushMeBaby application, the Debugger Console window will display the following outputs:2009-11-24 21:11:49.182 ApplePushNotification[1461:207] key: acme1, value: bar
2009-11-24 21:11:49.187 ApplePushNotification[1461:207] key: aps, value: {
alert = "You got a new message!";
badge = 5;
sound = "beep.wav";
}
2009-11-24 21:11:49.191 ApplePushNotification[1461:207] key: acme2, value: 42
From http://mobiforge.com/developing/story/programming-apple-push-notification-services
UIInterfaceOrientation(String) : UIInterfaceOrientationLandscapeRight
UIStatusBarHidden(Boolean) : YES
How to build an Apple Push Notification provider server (tutorial)
One of the widely anticipated features of the new iPhone OS 3.0 is push notifications which allow messages to be sent directly to an individual device relevant to the application that has been installed. Apple have demoed this as useful for news alerts, or IM notifications however it fits in perfectly with the nature of our server monitoring service, Server Density.
As part of the product, we have an iPhone application that includes push notifications as an alerting option so you can be notified via push direct to your iPhone when one of your server alerts have been triggered. This is useful since our app can then be launched to instantly see the details of the server that has caused the alert.
Apple provides detailed code documentation for the iPhone OS code that is needed to implement and handle the alerts on the device but only provides a higher level guide for the provider server side.
As a provider, you need to communicate with the Apple Push Notification Service (APNS) to send the messages that are then pushed to the phone. This is necessary so that the device only needs to maintain 1 connection to the APNS, helping to reduce battery usage.
This tutorial will go into code-level detail about how we built our push notification provider server to allow us to interact with the APNS and use the push notifications with our server monitoring iPhone application. Since we develop in PHP, our examples will be in PHP 5.
Basic Structure
- You connect to the APNS using your unique SSL certificate
- Cycle through the messages you want to send (or just send 1 if you only have 1)
- Construct the payload for each message
- Disconnect from APNS
The flow of remote-notification data is one-way. The provider composes a notification package that includes the device token for a client application and the payload. The provider sends the notification to APNs which in turn pushes the notification to the device.
Restrictions
- The payload is limited to 256 bytes in total – this includes both the actual body message and all of the optional and additional attributes you might wish to send. Push notifications are not designed for large data transfer, only for small alerts. For example we only send a short alert message detailing the server monitoring alert triggered.
- APNS does not provide any status feedback as to whether your message was successfully delivered. One reason for this is that messages are queued to be sent to the device if it is unreachable, however only the last sent message will be queued – overwriting any previously sent but undelivered messages.
- Push notifications should not be used for critical alerts because the message will only be delivered if the device has wifi or cellular connectivity, which is why we recommend combining push with another alerting method such as e-mail or SMS for our server monitoring alerts.
- The SSL certificates used to communicate with APNS, discussed below, are generated on an application level. The implementation discussed in this tutorial only concerns a single iPhone application so if you have several, you will need to adapt the code to use the appropriate certificate(s) where necessary.
Device Token
Each push message must be “addressed” to a specific device. This is achieved by using a unique deviceToken generated by APNS within your iPhone application. Once this token has been retrieved, you need to store it on your server, not within your iPhone application itself. It looks something like this:
c9d4c07c fbbc26d6 ef87a44d 53e16983 1096a5d5 fd825475 56659ddd f715defc
For the Server Density iPhone application, we call the necessary generation methods on app launch and pass it back to our servers via an HTTP API call. This stores the deviceToken in a database on our servers for that user so we can then communicate with the device linked to that user.
Feedback Service
Apple provide a feedback service which you are supposed to occasionally poll. This will provide a list of deviceTokens that were previously but are no longer valid, such as if the user has uninstalled your iPhone application. You can then remove the deviceToken from your database so you do not communicate with an invalid device.
Using the feedback service is not covered by this tutorial.
Certificates
The first thing you need is your Push certificates. These identify you when communicating with APNS over SSL.
Generating the Apple Push Notification SSL certificate on Mac:
- Log in to the iPhone Developer Connection Portal and click App IDs
- Ensure you have created an App ID without a wildcard. Wildcard IDs cannot use the push notification service. For example, our iPhone application ID looks something like
AB123346CD.com.serverdensity.iphone - Click Configure next to your App ID and then click the button to generate a Push Notification certificate. A wizard will appear guiding you through the steps to generate a signing authority and then upload it to the portal, then download the newly generated certificate. This step is also covered in the Apple documentation.
- Import your
aps_developer_identity.cerinto your Keychain by double clicking the.cerfile. - Launch Keychain Assistant from your local Mac and from the login keychain, filter by the Certificates category. You will see an expandable option called “Apple Development Push Services”
- Expand this option then right click on “Apple Development Push Services” > Export “Apple Development Push Services ID123″. Save this as apns-dev-cert.p12 file somewhere you can access it.
- Do the same again for the “Private Key” that was revealed when you expanded “Apple Development Push Services” ensuring you save it as apns-dev-key.p12 file.
- These files now need to be converted to the PEM format by executing this command from the terminal:
openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12 openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12 - If you wish to remove the passphrase, either do not set one when exporting/converting or execute:
openssl rsa -in apns-dev-key.pem -out apns-dev-key-noenc.pem - Finally, you need to combine the key and cert files into a apns-dev.pem file we will use when connecting to APNS:
cat apns-dev-cert.pem apns-dev-key-noenc.pem > apns-dev.pem
It is a good idea to keep the files and give them descriptive names should you need to use them at a later date. The same process above applies when generating the production certificate.
Payload Contents
The payload is formatted in JSON, compliant with the RFC 4627 standard. It consists of several parts:
- Alert – the text string to display on the device
- Badge – the integer number to display as a badge by the application icon on the device home screen
- Sound – the text string of the name of the sound to accompany the display of the message on the device
- This tutorial will only deal with the basics by sending a simple alert text string but this can also be another dictionary containing various options to display custom buttons and the like.
Creating the payload
Using PHP it is very easy to create the payload based on an array and convert it to JSON:
$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload = json_encode($payload);
Echoing the contents of $payload would show you the JSON string that can be sent to APNS:
{
"aps" : { "alert" : "This is the alert text", "badge" : 1, "sound" : "default" }
}
This will cause a message to be displayed on the device, trigger the default alert sound and place a “1″ in the badge by the application icon. The default buttons “Close” and “View” would also appear on the alert that pops up.
For the Server Density server monitoring iPhone application, it is important for the user to be able to tap “View” and go directly to the server that generated the alert. To do this, we add an extra dictionary in of our own custom values:
$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload['server'] = array('serverId' => $serverId, 'name' => $name);
$output = json_encode($payload);
The custom dictionary server is passed to the application on the device when the user taps “View” so we can load the right server. The JSON looks like this:
{
"aps" : { "alert" : "This is the alert text", "badge" : 1, "sound" : "default" },
"server" : { "serverId" : 1, "name" : "Server name")
}
The size limit of 256 bytes applies to this entire payload, including any custom dictionaries.
The raw interface
Once an alert is generated within Server Density, the payload is built and then inserted into a queue. This is processed separately so that we can send multiple payloads in one go if necessary.
Apple recommends this method because if you are constantly connecting and disconnecting to send each payload, APNS may block your IP.
As described by Apple:
The raw interface employs a raw socket, has binary content, is streaming in nature, and has zero acknowledgment responses.
Opening the connection
The PHP 5 code to open the connection looks like this:
$apnsHost = 'gateway.sandbox.push.apple.com';
$apnsPort = 2195;
$apnsCert = 'apns-dev.pem';
$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort, $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);
If an error has occurred you can pick up the error message from $errorString. This will also contain the details if your SSL certificate is not correct.
The certificate file is read in relative to the current working directory of the executing PHP script, so specify the full absolute path to your certificate if necessary.
Note that when testing you must use the sandbox with the development certificates. The production hostname is gateway.push.apple.com and must use the separate and different production certificate.
Sending the payload
At this point, the code we use loops through all the queued payloads and sends them. Constructing the binary content to send to APNS is simple:
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceToken)) . chr(0) . chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);
Note that the $deviceToken is included from our database and stripped of the spaces it is provided with by default. We also include a check to send an error to us in the event that the $payload is over 256 bytes.
$apnsMessage contains the correctly binary formatted payload and the fwrite call writes the payload to the currently active streaming connection we opened previously, contained in $apns.
Once completed, you can close the connection:
socket_close($apns);
fclose($apns);
php-apns
There is a free, open source server library that does all the above functionality called php-apns. We chose to implement it ourselves because it has a further dependancy on memcached, we do not want to rely on 3rd party code for large and critical aspects of our code-base and I am apprehensive about the suitability of PHP for running a continuous server process. We do all the above queue processing using our own custom cron system which runs every few seconds – that way PHP scripts do not need to be run as processes, something I’m not sure they were designed to do!
All done
That’s it! If you have any problems, post in the comments below and we’ll do our best to help out. Also, Stack Overflow is your friend.





