I cannot send the notification to iphone. Everything seems fine, as the service works out fine, but to message to iphone.
Here is the code:
using (NetworkStream networkStream = client.GetStream())
{
Console.WriteLine("Client connected.");
//X509Certificate clientCertificate = new X509Certificate(#"C:\Users\yagizozturk\Documents\Visual Studio 2010\Projects\GarantiKampanya\Garanti.Web.Service\apns-prod.pem", "");
X509Certificate clientCertificate = new X509Certificate(#"C:\Users\yagizozturk\Documents\Visual Studio 2010\Projects\GarantiKampanya\Garanti.Web.Service\apns-prod-cert.p12", "1234567");
X509CertificateCollection clientCertificateCollection = new X509CertificateCollection(new X509Certificate[1] { clientCertificate });
// Create an SSL stream that will close the client's stream.
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
try
{
sslStream.AuthenticateAsClient("gateway.sandbox.push.apple.com", clientCertificateCollection, SslProtocols.Default, false);
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
}
}
string token = "f5aaa8a729613480c79270085e881c435535dac4704dc00b082bfa8fc84aca10";
//string token = "f5aaa8a7 29613480 c7927008 5e881c43 5535dac4 704dc00b 082bfa8f c84aca90";
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
byte[] myByteArray = new byte[10000000];
myByteArray = encoding.GetBytes(token);
//int i = 0;
//foreach(char c in token.ToCharArray())
//{
// myByteArray [i] = (byte)c;
// i++;
//}
GeneratePayload(myByteArray, "test", "default");
}
private static byte[] GeneratePayload(byte[] deviceToken, string message, string sound)
{
MemoryStream memoryStream = new MemoryStream();
// Command
memoryStream.WriteByte(0);
byte[] tokenLength = BitConverter.GetBytes((Int16)32);
Array.Reverse(tokenLength);
// device token length
memoryStream.Write(tokenLength, 0, 2);
// Token
memoryStream.Write(deviceToken, 0, 32);
// String length
string apnMessage = string.Format("{{\"aps\":{{\"alert\":{{\"body\":\"{0}\",\"action-loc-key\":null}},\"sound\":\"{1}\"}}}}",
message,
sound);
byte[] apnMessageLength = BitConverter.GetBytes((Int16)apnMessage.Length);
Array.Reverse(apnMessageLength);
// message length
memoryStream.Write(apnMessageLength, 0, 2);
// Write the message
memoryStream.Write(System.Text.ASCIIEncoding.ASCII.GetBytes(apnMessage), 0, apnMessage.Length);
return memoryStream.ToArray();
}
// The following method is invoked by the RemoteCertificateValidationDelegate.
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
// Do not allow this client to communicate with unauthenticated servers.
return false;
}
I got the answer. Here is a complete code to send push notifications from c#.
public bool ConnectToAPNS()
{
X509Certificate2Collection certs = new X509Certificate2Collection();
// Add the Apple cert to our collection
certs.Add(getServerCert());
// Apple development server address
string apsHost;
if (getServerCert().ToString().Contains("Production"))
apsHost = "gateway.push.apple.com";
else
apsHost = "gateway.sandbox.push.apple.com";
// Create a TCP socket connection to the Apple server on port 2195
TcpClient tcpClient = new TcpClient(apsHost, 2195);
// Create a new SSL stream over the connection
sslStream = new SslStream(tcpClient.GetStream());
// Authenticate using the Apple cert
sslStream.AuthenticateAsClient(apsHost, certs, SslProtocols.Default, false);
//PushMessage();
return true;
}
private X509Certificate getServerCert()
{
X509Certificate test = new X509Certificate();
//Open the cert store on local machine
X509Store store = new X509Store(StoreLocation.CurrentUser);
if (store != null)
{
// store exists, so open it and search through the certs for the Apple Cert
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = store.Certificates;
if (certs.Count > 0)
{
int i;
for (i = 0; i < certs.Count; i++)
{
X509Certificate2 cert = certs[i];
if (cert.FriendlyName.Contains("Apple Production Push Services: KFDLV3XL6Y:ZJPGDMQBKC"))
{
//Cert found, so return it.
return certs[i];
}
}
}
return test;
}
return test;
}
private static byte[] HexToData(string hexString)
{
if (hexString == null)
return null;
if (hexString.Length % 2 == 1)
hexString = '0' + hexString; // Up to you whether to pad the first or last byte
byte[] data = new byte[hexString.Length / 2];
for (int i = 0; i < data.Length; i++)
data[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
return data;
}
public bool PushMessage(string token, string message)
{
String cToken = token;
String cAlert = message;
int iBadge = 1;
// Ready to create the push notification
byte[] buf = new byte[256];
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
bw.Write(new byte[] { 0, 0, 32 });
byte[] deviceToken = HexToData(cToken);
bw.Write(deviceToken);
bw.Write((byte)0);
// Create the APNS payload - new.caf is an audio file saved in the application bundle on the device
string msg = "{\"aps\":{\"alert\":\"" + cAlert + "\",\"badge\":" + iBadge.ToString() + ",\"sound\":\"new.caf\"}}";
// Write the data out to the stream
bw.Write((byte)msg.Length);
bw.Write(msg.ToCharArray());
bw.Flush();
if (sslStream != null)
{
sslStream.Write(ms.ToArray());
return true;
}
return false;
}
Related
I'm attempting to write a .Net Framework 4.7.2 client that can communicate with a proto buffer payload eventually, over SSL. I think the best approach would be a TAP model, but I've had less luck finding a working example to repeatedly do the send (if data to send) and process server messages as and when the server replies, and therefore I've gone with the APM approach here below.
My problem is I get a "The BeginWrite method cannot be called when another write is pending" error in SendWorker(). My server does receive the connection and the message. I can work on the SSL cert later.
My output is:
Client connected.
Client is selecting a local certificate.
To quit, press the enter key.
Client is selecting a local certificate.
Validating the server certificate.
Certificate error: RemoteCertificateNameMismatch
Writing data to the server.
Writing data to the server.
The test code is:
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
namespace SslClient
{
// This is a client side of a
// client-server application that communicates using the
// SslStream and TcpClient classes.
// After connecting to the server and authenticating,
// the client should repeatedly send items from a q and listen for any replies
public class SslTcpClient
{
// complete is used to terminate the application when all
// asynchronous calls have completed or any call has thrown an exception.
// complete might be used by any of the callback methods.
private bool complete = false;
// e stores any exception thrown by an asynchronous method so that
// the mail application thread can display the exception and terminate gracefully.
// e might be used by any of the callback methods.
private Exception e = null;
// readData and buffer holds the data read from the server.
// They is used by the ReadCallback method.
private StringBuilder readData = new StringBuilder();
private byte[] buffer = new byte[2048];
EventWaitHandle _trigger = new AutoResetEvent(false);
private TcpClient _tcpClient;
private SslStream _sslStream;
private Thread _senderThread;
private readonly object _locker = new object();
public ConcurrentQueue<byte[]> WriteQueue = new ConcurrentQueue<byte[]>();
// The following method is invoked by the CertificateValidationDelegate.
public bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
Console.WriteLine("Validating the server certificate.");
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
// Do not allow this client to communicate with unauthenticated servers.
return true; // ToDo
}
public X509Certificate SelectLocalCertificate(
object sender,
string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate,
string[] acceptableIssuers)
{
Console.WriteLine("Client is selecting a local certificate.");
if (acceptableIssuers != null &&
acceptableIssuers.Length > 0 &&
localCertificates != null &&
localCertificates.Count > 0)
{
// Use the first certificate that is from an acceptable issuer.
foreach (X509Certificate certificate in localCertificates)
{
string issuer = certificate.Issuer;
if (Array.IndexOf(acceptableIssuers, issuer) != -1)
return certificate;
}
}
if (localCertificates != null &&
localCertificates.Count > 0)
return localCertificates[0];
return null;
}
private void AuthenticateCallback(IAsyncResult ar)
{
SslStream stream = (SslStream)ar.AsyncState;
try
{
stream.EndAuthenticateAsClient(ar);
}
catch (Exception authenticationException)
{
e = authenticationException;
//complete = true;
return;
}
}
public void ProduceConsumerQueue(TcpClient tcpClient, SslStream sslStream)
{
_tcpClient = tcpClient;
_sslStream = sslStream;
_senderThread = new Thread(SenderWorker);
_senderThread.Start();
}
public void Send(byte[] payload)
{
WriteQueue.Enqueue(payload);
_trigger.Set();
}
//our workhorse. This will just sit and wait until there is something to send
//it will continue to send until there is nothing left
void SenderWorker()
{
while (!complete)
{
string msg = null;
if (WriteQueue.Count > 0)
{
byte[] message;
WriteQueue.TryDequeue(out message);
if (_tcpClient.Connected && message.Length > 0)
{
if (_sslStream.CanWrite)
{
_sslStream.BeginWrite(message, 0, message.Length,
new AsyncCallback(WriteCallback),
_sslStream);
}
}
}
else
{
_trigger.WaitOne(); // Zero msgs left.. just wait
}
}
}
private void WriteCallback(IAsyncResult ar)
{
SslStream stream = (SslStream)ar.AsyncState;
try
{
Console.WriteLine("Writing data to the server.");
stream.EndWrite(ar);
// Asynchronously read a message from the server.
//stream.BeginRead(buffer, 0, buffer.Length,
// new AsyncCallback(ReadCallback),
// stream);
}
catch (Exception writeException)
{
e = writeException;
complete = true;
return;
}
}
private void ReadCallback(IAsyncResult ar)
{
// Read the message sent by the server.
// The end of the message is signaled using the
// "<EOF>" marker.
SslStream stream = (SslStream)ar.AsyncState;
int byteCount = -1;
try
{
Console.WriteLine("Reading data from the server.");
byteCount = stream.EndRead(ar);
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, byteCount)];
decoder.GetChars(buffer, 0, byteCount, chars, 0);
readData.Append(chars);
// Check for EOF or an empty message.
if (byteCount != 0)
{
// We are not finished reading.
// Asynchronously read more message data from the server.
stream.BeginRead(buffer, 0, buffer.Length,
new AsyncCallback(ReadCallback),
stream);
}
else
{
Console.WriteLine("Message from the server: {0}", readData.ToString());
}
if (WriteQueue.Count>0)
{
//byte[] message;
//WriteQueue.TryDequeue(out message);
//if (message.Length > 0)
//{
// stream.BeginWrite(message, 0, message.Length,
// new AsyncCallback(WriteCallback),
// ar);
//}
}
}
catch (Exception readException)
{
e = readException;
complete = true;
return;
}
complete = true;
}
public int Main(string serverName, int port)
{
// Create a TCP/IP client socket.
TcpClient client = new TcpClient(serverName, port);
Console.WriteLine("Client connected.");
// Create an SSL stream that will close the client's stream.
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectLocalCertificate)
);
var clientCertificate = FindClientCertificate(X509FindType.FindBySubjectName, "IIS Express Development Certificate"); // IIS Express Development Certificate
var clientCertificates = new X509CertificateCollection(new X509Certificate[] { clientCertificate });
// Begin authentication.
// The server name must match the name on the server certificate.
sslStream.BeginAuthenticateAsClient(
"UIClient",
clientCertificates,
SslProtocols.Tls12,
false,/* cert revocation */
new AsyncCallback(AuthenticateCallback),
sslStream);
// sslStream.AuthenticateAsClient("UIClient", clientCertificates, SslProtocols.Tls12, false);
ProduceConsumerQueue(client, sslStream);
// User can press a key to exit application, or let the
// asynchronous calls continue until they complete.
Console.WriteLine("To quit, press the enter key.");
do
{
// Real world applications would do work here
// while waiting for the asynchronous calls to complete.
Send(System.Text.Encoding.UTF8.GetBytes("hello".ToArray()));
System.Threading.Thread.Sleep(100);
} while (complete != true && Console.KeyAvailable == false);
if (Console.KeyAvailable)
{
Console.ReadLine();
Console.WriteLine("Quitting.");
client.Close();
sslStream.Close();
return 1;
}
if (e != null)
{
Console.WriteLine("An exception was thrown: {0}", e.ToString());
}
sslStream.Close();
client.Close();
Console.WriteLine("Good bye.");
return 0;
}
private static X509Certificate FindClientCertificate(X509FindType type, string item)
{
return Find(type, item, StoreLocation.CurrentUser) ?? Find(type, item, StoreLocation.LocalMachine);
}
private static X509Certificate Find(X509FindType t, string item, StoreLocation location)
{
using (var store = new X509Store(location))
{
store.Open(OpenFlags.OpenExistingOnly);
var certs = store.Certificates.Find(t, item, true);
if (t == X509FindType.FindBySubjectName)
{
return store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.FriendlyName == item);
}
return certs.OfType<X509Certificate>().FirstOrDefault();
}
}
}
}
How can it be fixed? Is there a better complete solution that you can share?
Thanks in advance,
Steve
In my application I am using the below code to validate the client certificate
public static async Task<string> CallApi(string url, Context context)
{
var hostName = "mytestapp.azurewebsites.net";
var port = 443;
Stream keyin = Application.Context.Assets.Open("Server.pfx");
var password = "pass123";
using (MemoryStream memStream = new MemoryStream())
{
keyin.CopyTo(memStream);
var certificates = new X509Certificate2Collection(new X509Certificate2(memStream.ToArray(), password));
await Task.Run(() =>
{
// Create a TCP/IP client socket.
// machineName is the host running the server application.
TcpClient client = new TcpClient(hostName, port);
Console.WriteLine("Client connected.");
// Create an SSL stream that will close the client's stream.
SslStream sslStream = new SslStream(
client.GetStream(),
false,
ValidateServerCertificate);
// The server name must match the name on the server certificate.
try
{
sslStream.AuthenticateAsClient(hostName, certificates, SslProtocols.Tls12, true);
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
});
}
return string.Empty;
}
after successful authentication, I would like to make and HTTP get request.
sslStream.AuthenticateAsClient(hostName, certificates, SslProtocols.Tls12, true);
After this statement. Say for example I need to call below http Get call
https://mytestapp.azurewebsites.net/api/GetUserProfile?userId="Sooraj"
How I can invoke this call? Or is it possible to implement the same?
Please help
Try something like this,
try
{
sslStream.AuthenticateAsClient(hostName, certificates, SslProtocols.Tls12, true);
byte[] buffer = new byte[5120];
int bytes;
var pqr = string.Format("GET {0} HTTP/1.1\r\nHost: {1}\r\n\r\n", url, "mytestapp.azurewebsites.net");
byte[] request = Encoding.UTF8.GetBytes(pqr);
sslStream.Write(request, 0, request.Length);
var ppp = ReadStream(sslStream);
sslStream.Flush();
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
private static string ReadStream(Stream stream)
{
byte[] resultBuffer = new byte[2048];
string value = "";
//requestStream.BeginRead(resultBuffer, 0, resultBuffer.Length, new AsyncCallback(ReadAsyncCallback), new result() { buffer = resultBuffer, stream = requestStream, handler = callback, asyncResult = null });
do
{
try
{
int read = stream.Read(resultBuffer, 0, resultBuffer.Length);
value += Encoding.UTF8.GetString(resultBuffer, 0, read);
if (read < resultBuffer.Length)
break;
}
catch { break; }
} while (true);
return value;
}
It will give you the response with all set of data. and later we need to parse the required information.
I am creating client-server app.
Server Code:
public sealed class SslTcpServer {
static X509Certificate serverCertificate = null;
public static void RunServer(string certificate) {
serverCertificate = new X509Certificate2(certificate, "123", X509KeyStorageFlags.MachineKeySet);
TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 56748);
listener.Start();
while (true) {
Console.WriteLine("Waiting for a client to connect...");
TcpClient client = listener.AcceptTcpClient();
ProcessClient(client);
}
}
static void ProcessClient(TcpClient client) {
SslStream sslStream = new SslStream(client.GetStream());
try {
sslStream.AuthenticateAsServer(serverCertificate,
clientCertificateRequired: false,
checkCertificateRevocation: true);
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
Console.WriteLine("Waiting for client message...");
string messageData = ReadMessage(sslStream);
Console.WriteLine("Received: {0}", messageData);
byte[] message = Encoding.UTF8.GetBytes("Hello from the server.<EOF>");
Console.WriteLine("Sending hello message.");
sslStream.Write(message);
} catch (AuthenticationException e) {
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null) {
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
sslStream.Close();
client.Close();
return;
} finally {
sslStream.Close();
client.Close();
}
}
static string ReadMessage(SslStream sslStream) {
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do {
bytes = sslStream.Read(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
if (messageData.ToString().IndexOf("<EOF>") != -1)
break;
} while (bytes != 0);
return messageData.ToString();
}
public static int Main(string[] args) {
string certificate = "server.crt";
RunServer(certificate);
return 0;
}
}
Client Code:
public class SslTcpClient {
private static Hashtable certificateErrors = new Hashtable();
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors) {
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
return false;
}
public static void RunClient(string machineName, string serverName) {
TcpClient client = new TcpClient("127.0.0.1", 56748);
Console.WriteLine("Client connected.");
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
try {
sslStream.AuthenticateAsClient("127.0.0.1");
} catch (AuthenticationException e) {
Console.WriteLine("Exception: {0}", e.Message);
Console.WriteLine(e.StackTrace);
if (e.InnerException != null) {
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
sslStream.Write(messsage);
sslStream.Flush();
string serverMessage = ReadMessage(sslStream);
Console.WriteLine("Server says: {0}", serverMessage);
client.Close();
Console.WriteLine("Client closed.");
}
static string ReadMessage(SslStream sslStream) {
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do {
bytes = sslStream.Read(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
if (messageData.ToString().IndexOf("<EOF>") != -1) {
break;
}
} while (bytes != 0);
return messageData.ToString();
}
public static int Main(string[] args) {
string serverCertificateName = "127.0.0.1";
string machineName = "127.0.0.1";
SslTcpClient.RunClient(machineName, serverCertificateName);
return 0;
}
}
I had already created SSL certificate "server.crt", but I cannot connect user to server.
I have an Unhandled Exception:
System.NotSupportedException: The server mode SSL must use a certificate with the associated private key.
The exception is telling you that you need the private key also of the server certificate as the SSL server needs to have it to decrypt the session key generated by the client. Now, having said that, the certificate you are passing to the RunServer method is Server.crt which is a public cert only (i.e. it does not have associated private key). If you have generated this certificate by yourself you should also generate a server.p12 or server.pfx file (which has private key also in it) and then create a X509Certificate out of it. Something like this ..
var certificate = new X509Certificate("server.pfx", "password_of_the_private_key");
So I have my little server and client, since I have multiple users connecting I would like to have each user have his/her own Ssl certificate which is used for the SslStream.
So basically the server has a public key for each client, the client has his private key, when the client connects it will start an SslStream with those certificates, but the problem is, the server does not know which certificate should be loaded as it doesnt know what client is connecting, this is my server code:
while ((true))
{
try
{
byte[] buffer = new byte[4];
requestCount = requestCount + 1;
bool leaveInnerStreamOpen = false;
RemoteCertificateValidationCallback validationCallback =
new RemoteCertificateValidationCallback(ClientValidationCallback);
LocalCertificateSelectionCallback selectionCallback =
new LocalCertificateSelectionCallback(ServerCertificateSelectionCallback);
EncryptionPolicy encryptionPolicy = EncryptionPolicy.AllowNoEncryption;
_sslStream = new SslStream(clientSocket.GetStream(),
leaveInnerStreamOpen, validationCallback, selectionCallback, encryptionPolicy);
X509Certificate2 certificate = ServerCertificate.Servercertificate();
bool requireClientCertificate = false;
SslProtocols enabledSslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12;
bool checkCertificateRevocation = true;
_sslStream.AuthenticateAsServer
(certificate, requireClientCertificate, enabledSslProtocols, checkCertificateRevocation);
buffer = new byte[4];
int readBytes = _sslStream.Read(buffer, 0, 4);
if (readBytes == 0)
break;
int MessageSize = BitConverter.ToInt32(buffer, 0);
byte[] bufferreader = new byte[MessageSize];
clientSocket.ReceiveBufferSize = MessageSize;
readBytes = _sslStream.Read(bufferreader, 0, MessageSize);
Console.WriteLine(Convert.ToString(MessageSize));
rCount = Convert.ToString(requestCount);
dataFromClient = Encoding.ASCII.GetString(bufferreader);
byte[] outbuffer = new byte[4];
serverResponse = R.Respond(dataFromClient, clientSocket);
sendBytes = Encoding.ASCII.GetBytes(serverResponse);
outbuffer = new byte[4];
outbuffer = BitConverter.GetBytes(sendBytes.Length);
_sslStream.Write(outbuffer, 0, 4);
_sslStream.Flush();
clientSocket.SendBufferSize = sendBytes.Length;
_sslStream.Write(sendBytes, 0, sendBytes.Length);
_sslStream.Flush();
}
catch (Exception ex)
{
EndPointHandler.RemoveEndPoint(clientSocket);
clientSocket.Close();
Console.WriteLine("User Server >> " + ex.ToString());
Thread.CurrentThread.Abort();
}
}
So what should happen is that first the client sends his username, the server then looks for a matching certificate. It then authenticates itself with that public certificate and client authenticates with the private certificate, but the problem is that I dont exactly know how to do that and sadly a lot of google searches resulted in nothing.
Any help is greatly appreciated,
Thanks
I tried to implement a feed back service code which i found over here.
here is my code:
private X509Certificate getServerCert(string certName)
{
var store = new X509Store(StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindBySubjectName, certName, false);
return certs.Count == 0 ? null : certs[0];
}
finally
{
store.Close();
}
}
public String CheckFeedbackService(string certaName)
{
System.Net.ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
KIREIP.Core.Manager.IphoneErrorLog errorLog = new KIREIP.Core.Manager.IphoneErrorLog();
// Create an empty collection of certs
X509Certificate2Collection certs = new X509Certificate2Collection();
// Add the Apple cert to our collection
certs.Add(getServerCert(certaName));
// Apple feedback server address
string apsHostF;
if (getServerCert(certaName).ToString().Contains("Production"))
apsHostF = "feedback.push.apple.com";
else
apsHostF = "feedback.sandbox.push.apple.com";
// Create a TCP socket connection to the Apple server on port 2196
TcpClient tcpClientF = new TcpClient(apsHostF, 2196);
//Set up
byte[] buffer = new byte[38];
int recd = 0;
DateTime minTimestamp = DateTime.Now.AddYears(-1);
string result = string.Empty;
// Create a new SSL stream over the connection
SslStream sslStreamF = new SslStream(tcpClientF.GetStream(), true, new RemoteCertificateValidationCallback(ValidateServerCertificate));
try
{
// Authenticate using the Apple cert
sslStreamF.AuthenticateAsClient(apsHostF, certs, SslProtocols.Default, false);
//TODO: Read in data and remove device tokens if any found.
errorLog.AddErrorLog("Stream Readable ::" + sslStreamF.CanRead);
//errorLog.AddErrorLog("Host Name ::" + hostName);
errorLog.AddErrorLog("Cert Name ::" + certs[0].FriendlyName);
if (sslStreamF != null)
{
errorLog.AddErrorLog("Connection Started");
//Get the first feedback
recd = sslStreamF.Read(buffer, 0, buffer.Length);
errorLog.AddErrorLog("Buffer length ::" + recd);
//Continue while we have results and are not disposing
while (recd > 0)
{
errorLog.AddErrorLog("Reading Started");
//Get our seconds since 1970 ?
byte[] bSeconds = new byte[4];
byte[] bDeviceToken = new byte[32];
Array.Copy(buffer, 0, bSeconds, 0, 4);
//Check endianness
if (BitConverter.IsLittleEndian)
Array.Reverse(bSeconds);
int tSeconds = BitConverter.ToInt32(bSeconds, 0);
//Add seconds since 1970 to that date, in UTC and then get it locally
var Timestamp = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(tSeconds).ToLocalTime();
//Now copy out the device token
Array.Copy(buffer, 6, bDeviceToken, 0, 32);
string deviceToken = BitConverter.ToString(bDeviceToken).Replace("-", "").ToLower().Trim();
//Make sure we have a good feedback tuple
if (deviceToken.Length == 64 && Timestamp > minTimestamp)
{
errorLog.AddErrorLog("Feedback " + deviceToken);
result = deviceToken;
errorLog.AddErrorLog(result);
}
//Clear array to reuse it
Array.Clear(buffer, 0, buffer.Length);
//Read the next feedback
recd = sslStreamF.Read(buffer, 0, buffer.Length);
}
errorLog.AddErrorLog("Reading Ended");
}
if (sslStreamF != null)
sslStreamF.Close();
if (tcpClientF != null)
tcpClientF.Close();
}
catch (AuthenticationException e)
{
errorLog.AddErrorLog("Authentication failed - closing the connection.");
sslStreamF.Close();
tcpClientF.Close();
return "NOAUTH";
}
finally
{
// The client stream will be closed with the sslStream
// because we specified this behavior when creating
// the sslStream.
sslStreamF.Close();
tcpClientF.Close();
}
return "";
}
Whenever i run this code i get null value from getServerCert(certName) method, because of this null value my further execution get failed.
Any help would be greatly appreciated
I have used JdSoft.Apns.Feedback.dll that I found here (or you may google it directly to get latest version).
Using this .dll I have generate the below method with 2 delegates
public string CheckFeedBack()
{
//Variables you may need to edit:
//---------------------------------
//Log.AddErrorLog("Start");
//True if you are using sandbox certificate, or false if using production
bool sandbox = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings["sandbox"].ToString());
//Put your PKCS12 .p12 or .pfx filename here.
// Assumes it is in the same directory as your app
string p12File = System.Configuration.ConfigurationManager.AppSettings["p12File"].ToString();
//This is the password that you protected your p12File
// If you did not use a password, set it as null or an empty string
string p12FilePassword = System.Configuration.ConfigurationManager.AppSettings["p12FilePassword"].ToString();
//Actual Code starts below:
//--------------------------------
//Get the filename assuming the file is in the same directory as this app
string p12Filename = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, p12File);
//errorLog.AddErrorLog(p12Filename);
//Create the feedback service consumer
FeedbackService service = new FeedbackService(sandbox, p12Filename, p12FilePassword);
//Wireup the events
service.Error += new FeedbackService.OnError(service_Error);
service.Feedback += new FeedbackService.OnFeedback(service_Feedback);
//Run it. This actually connects and receives the feedback
// the Feedback event will fire for each feedback object
// received from the server
service.Run();
Log.AddErrorLog("Done.");
Log.AddErrorLog("Cleaning up...");
//Clean up
service.Dispose();
return "Checked database table log..";
}
Delegates to wireup feedback and error events
static void service_Feedback(object sender, Feedback feedback)
{
Log.AddErrorLog(string.Format("Feedback - Timestamp: {0} - DeviceId: {1}", feedback.Timestamp, feedback.DeviceToken));
//DeviceToken retrive over here
}
static void service_Error(object sender, Exception ex)
{
Log.AddErrorLog(string.Format("Error: {0}", ex.Message));
//Error Message if failed
}