views:

3209

answers:

6

Apple really has bad documentation about how the provider connects and communicates to their service. I am confused about the protocol. If anyone out there could provide a C# sample of how this is don, it would be greatly appreciated.

+4  A: 

I hope this is relevant (slightly), but I have just successfully created one for Java, so conceptually quite similar to C# (except perhaps the SSL stuff, but that shouldn't be too hard to modify. Below is a sample message payload and crypto setup:

    int port = 2195;
    String hostname = "gateway.sandbox.push.apple.com";


    char []passwKey = "<keystorePassword>".toCharArray();
    KeyStore ts = KeyStore.getInstance("PKCS12");
    ts.load(new FileInputStream("/path/to/apn_keystore/cert.p12"), passwKey);

    KeyManagerFactory tmf = KeyManagerFactory.getInstance("SunX509");
    tmf.init(ts,passwKey);
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(tmf.getKeyManagers(), null, null);
    SSLSocketFactory factory =sslContext.getSocketFactory();
    SSLSocket socket = (SSLSocket) factory.createSocket(hostname,port); // Create the ServerSocket
    String[] suites = socket.getSupportedCipherSuites();
    socket.setEnabledCipherSuites(suites);
    //start handshake

    socket.startHandshake();


    // Create streams to securely send and receive data to the server
    InputStream in = socket.getInputStream();
    OutputStream out = socket.getOutputStream();



    // Read from in and write to out...
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    baos.write(0); //The command
    System.out.println("First byte Current size: " + baos.size());

    baos.write(0); //The first byte of the deviceId length    
    baos.write(32); //The deviceId length

    System.out.println("Second byte Current size: " + baos.size());

    String deviceId = "<heaxdecimal representation of deviceId";
    baos.write(hexStringToByteArray(deviceId.toUpperCase()));
    System.out.println("Device ID: Current size: " + baos.size());


    String payload = "{\"aps\":{\"alert\":\"I like spoons also\",\"badge\":14}}";
    System.out.println("Sending payload: " + payload);
    baos.write(0); //First byte of payload length;
    baos.write(payload.length());
    baos.write(payload.getBytes());

    out.write(baos.toByteArray());
    out.flush();

    System.out.println("Closing socket..");
    // Close the socket
    in.close();
    out.close();

}

Once again, not C#, but at least closer than the poor ObjC sample that Apple provides.

Chaos
I am still trying to get this to work. I have duplicated your code in C#, but since .NET uses a different kind of object to connect via SSL "SSLStream" it doesn't have a "handshake" method. I can't seem to figure out how to get the proper handshake to occur.
Phobis
Look here: http://msdn.microsoft.com/en-us/library/system.net.security.sslstream.aspx (about three quarters of the page down, there is a C# SSL client handshake example, using the SSLStream object. Seems to be the way to do it, by the look of it (a callback)
Chaos
This is wrong. It's not creating a server socket. It's creating a client socket. ts and tmf are sometimes used to refer to "trust store" and "trust manager factory," but here they refer to the client key material... really strange.
erickson
OK, I think I got it to work. I will post the code, once I can verify it working. One issue though. I connect and send the payload and then read the server response (which is - no response) And I get no push to my phone. Is there something I am missing (my app already registered the push service) I am using the sandbox, is this the issue?
Phobis
Does your app show up under 'notifications' on the device? If not, you probably need to regenerate your certificates and redeploy your app - that one got me for a while.. Also, make sure that you are *absolutely* right, byte-for-byte, in what you are transmitting (see above) . If you have it wrong, it will not work at all. Also - in that MS document on SSLStream, about 1/2 to 3/4 of the way down, it deals with Client->Server connections.
Chaos
My app does show in the 'notifications' section of my phone. I have verified it about 5 times (byte for byte)... but as we all know it never hurts to look again (I am hoping I missed something)
Phobis
Hi Phobis, I am having same problem as yours. I have also registered my app and using sandbox. Using sandbox environment, there will be a virtual device. then how can we connect a phone and check?As Chaos said, how to check the notification which i am is correct? - dsk
saikamesh
+7  A: 

Working code example:

 int port = 2195;
 String hostname = "gateway.sandbox.push.apple.com";

 //load certificate
 string certificatePath = @"cert.p12";
 string certificatePassword = "";
 X509Certificate2 clientCertificate = new X509Certificate2(certificatePath, certificatePassword);
 X509Certificate2Collection certificatesCollection = new X509Certificate2Collection(clientCertificate);

 TcpClient client = new TcpClient(hostname, port);
 SslStream sslStream = new SslStream(
   client.GetStream(),
   false,
   new RemoteCertificateValidationCallback(ValidateServerCertificate),
   null
 );

 try
 {
  sslStream.AuthenticateAsClient(hostname, certificatesCollection, SslProtocols.Tls, true);
 }
 catch (AuthenticationException ex)
 {
  client.Close();
  return;
 }

 // Encode a test message into a byte array.
 MemoryStream memoryStream = new MemoryStream();
 BinaryWriter writer = new BinaryWriter(memoryStream);

 writer.Write((byte)0); //The command
 writer.Write((byte)0); //The first byte of the deviceId length (big-endian first byte)
 writer.Write((byte)32); //The deviceId length (big-endian second byte)

 String deviceId = "DEVICEIDGOESHERE";
 writer.Write(ToByteArray(deviceId.ToUpper()));

 String payload = "{\"aps\":{\"alert\":\"I like spoons also\",\"badge\":14}}";

 writer.Write((byte)0); //First byte of payload length; (big-endian first byte)
 writer.Write((byte)payload.Length); //payload length (big-endian second byte)

 byte[] b1 = System.Text.Encoding.UTF8.GetBytes(payload);
 writer.Write(b1);
 writer.Flush();

 byte[] array = memoryStream.ToArray();
 sslStream.Write(array);
 sslStream.Flush();

 // Close the client connection.
 client.Close();
}
more info: http://x-cake.ning.com/profiles/blogs/setting-up-a-push-notification
SteveCav
+1  A: 

http://code.google.com/p/apns-sharp/

This link returns a 500 error now.
hipplar
A: 

Hi Shader,

I am using the same code as your's excpet that on the call to the sslStream.AuthenticateAsClient method, I get the following exception:

The credentials supplied to the package were not recognized

Any thoughts?

Thanks, Anthony

Anthony
Did you resolve this issue? I am also receiving this exception.
Luke
A: 

Hi there Phobis, Shader, and Chaos!

This posting is great (thank you Phobis for asking)! I am also trying to write an APNS in Java.

Have some questions:

(1) Is this approach viable to plug-in into a RESTful Web Service (say Jersey)?

(2) How does one create / register the cert.p12 and obtain the keystorePassword (is it done using XCode)?

(3) Why are there angle brackets in the ?

(4) Shader: Can you please post the hexStringToByteArray() method? I am just curious to see how it was set up.

This is what I've come up with:

public static byte[] hexStringToByteArray(String hexString) {

byte[] b = new byte[hexString.length() / 2];
for (int i = 0; i < b.length; i++) {
    int index = i * 2; 
    int v = Integer.parseInt(hexString.substring(index, index + 2), 16);
    b[i] = (byte) v;
 }
 return b;

}

(5) As one might have noticed, I had trouble formatting code in this post... Any tips?

Would really appreciate it if someone could help me with these questions or point me in the right direction...

How can I send people private messages on StackOverflow? Is there a way to send e-mails as well?

Happy programming,

Mike

mw_javaguy
A: 

Hi Phobis, I am having same problem as yours. I have also registered my app and using sandbox. Using sandbox environment, there will be a virtual device. then how can we connect a phone and check?

As Chaos said, how to check the notification which i am is correct? - dsk

saikamesh