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.
views:
3209answers:
6I 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.
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();
}
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
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
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