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");
Related
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 fairly new to Network Programming and i need help with connecting a TCP Client via TLS. I was given a project that was coded already and it came with both the certificate and the public key provided. I have installed the pfx certificate on my local machine and have coded a new TCP Listener/Server and TCP Client pointing to local host as below:
The Client
static void Main(string[] args)
{
string server = "localhost";
TcpClient client = new TcpClient(server, 5997);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
//client.Connect(server, 5997);//connection without TLS - authenticating properly
using (SslStream sslStream = new SslStream(client.GetStream(), false,
new RemoteCertificateValidationCallback(ValidateServerCertificate), null))
{
try
{
var servername = "myAuthenticatingServerName";//the server name must be the same as the one on the server certificate
sslStream.AuthenticateAsClient(servername);
try
{
Console.WriteLine("Client Connected...");
// Encode a test message into a byte array.
// Signal the end of the message using the "<EOF>".
byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
// Send hello message to the server.
sslStream.Write(messsage);
sslStream.Flush();
// Read message from the server.
string serverMessage = ReadMessage(sslStream);
Console.WriteLine("Server says: {0}", serverMessage);
// Close the client connection.
client.Close();
Console.WriteLine("Client closed.");
}
catch (Exception e)
{
Console.WriteLine("Error..... " + e.StackTrace);
}
}
catch (AuthenticationException e)
{
Console.WriteLine($"Error: { e.Message}");
if (e.InnerException != null)
{
Console.WriteLine($"Inner exception: {e.InnerException.Message}");
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
}
}
client.Close();
Console.WriteLine($"Connection closed at {DateTime.Now}.");
Console.ReadLine();
}
static string ReadMessage(SslStream sslStream)
{
// Read the message sent by the server.
// The end of the message is signaled using the
// "<EOF>" marker.
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
try
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
throw ex;
}
// 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, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
// Check for EOF.
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
} while (bytes != 0);
return messageData.ToString();
}
public static bool ValidateServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine($"Certificate error: {sslPolicyErrors}");
// Do not allow this client to communicate with unauthenticated servers.
return false;
}
The Server
public sealed class SslTcpServer
{
static X509Certificate2 serverCertificate = null;
// The certificate parameter specifies the name of the file
// containing the machine certificate.
public static void RunServer(X509Certificate2 certificate)
{
serverCertificate = certificate;
// Create a TCP/IP (IPv4) socket and listen for incoming connections.
TcpListener listener = new TcpListener(IPAddress.Any, 8080);
listener.Start();
while (true)
{
Console.WriteLine("Waiting for a client to connect...");
// Application blocks while waiting for an incoming connection.
// Type CNTL-C to terminate the server.
TcpClient client = listener.AcceptTcpClient();
ProcessClient(client);
}
}
static void ProcessClient(TcpClient client)
{
// A client has connected. Create the
// SslStream using the client's network stream.
SslStream sslStream = new SslStream(
client.GetStream(), false);
// Authenticate the server but don't require the client to authenticate.
try
{
sslStream.AuthenticateAsServer(serverCertificate, clientCertificateRequired: false, enabledSslProtocols: SslProtocols.Tls, checkCertificateRevocation: true);
// Display the properties and settings for the authenticated stream.
DisplaySecurityLevel(sslStream);
DisplaySecurityServices(sslStream);
DisplayCertificateInformation(sslStream);
DisplayStreamProperties(sslStream);
// Set timeouts for the read and write to 5 seconds.
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
// Read a message from the client.
Console.WriteLine("Waiting for client message...");
string messageData = ReadMessage(sslStream);
Console.WriteLine("Received: {0}", messageData);
// Write a message to the client.
byte[] message = Encoding.UTF8.GetBytes("Hello from the server.");
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
{
// The client stream will be closed with the sslStream
// because we specified this behavior when creating
// the sslStream.
sslStream.Close();
client.Close();
}
}
static string ReadMessage(SslStream sslStream)
{
// Read the message sent by the client.
// The client signals the end of the message using the
// "<EOF>" marker.
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
// Read the client's test message.
bytes = sslStream.Read(buffer, 0, buffer.Length);
// 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, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
// Check for EOF or an empty message.
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
} while (bytes != 0);
return messageData.ToString();
}
static void DisplaySecurityLevel(SslStream stream)
{
Console.WriteLine("Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);
Console.WriteLine("Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);
Console.WriteLine("Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);
Console.WriteLine("Protocol: {0}", stream.SslProtocol);
}
static void DisplaySecurityServices(SslStream stream)
{
Console.WriteLine("Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);
Console.WriteLine("IsSigned: {0}", stream.IsSigned);
Console.WriteLine("Is Encrypted: {0}", stream.IsEncrypted);
}
static void DisplayStreamProperties(SslStream stream)
{
Console.WriteLine("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
Console.WriteLine("Can timeout: {0}", stream.CanTimeout);
}
static void DisplayCertificateInformation(SslStream stream)
{
Console.WriteLine("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);
X509Certificate localCertificate = stream.LocalCertificate;
if (stream.LocalCertificate != null)
{
Console.WriteLine("Local cert was issued to {0} and is valid from {1} until {2}.",
localCertificate.Subject,
localCertificate.GetEffectiveDateString(),
localCertificate.GetExpirationDateString());
}
else
{
Console.WriteLine("Local certificate is null.");
}
// Display the properties of the client's certificate.
X509Certificate remoteCertificate = stream.RemoteCertificate;
if (stream.RemoteCertificate != null)
{
Console.WriteLine("Remote cert was issued to {0} and is valid from {1} until {2}.",
remoteCertificate.Subject,
remoteCertificate.GetEffectiveDateString(),
remoteCertificate.GetExpirationDateString());
}
else
{
Console.WriteLine("Remote certificate is null.");
}
}
public static int Main(string[] args)
{
RunServer(Certificate.GetCertificate());
return 0;
}
}
public class Certificate
{
public static X509Certificate2 GetCertificate()
{
string certificatePath = ConfigurationManager.AppSettings["certificatePath"].ToString();
var stream = File.OpenRead(certificatePath);
return new X509Certificate2(ReadStream(stream), "mypassword");
}
private static byte[] ReadStream(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
}
My problem is that the client runs properly and even Authenticates on
sslStream.AuthenticateAsClient(servername);
but the server does not pick up any connection on
TcpClient client = listener.AcceptTcpClient();
ProcessClient(client);
Am i missing something, or doing something wrong? Also, is it worth starting afresh, getting a new certificate etc?
On the server-side you are listening to port 8080 and on the client-side your are connecting to port 5997. To be more specific your server is hosting [YourIP]:8080 and your client is trying to connect to [YourIP]:5997
Moreover Tls and SSL are usually used on the port 443. The client and server ports need to be the same so they can connect to each other.
I’m also not sure if c# is recognising ‘localhost’ as ‘YourIP’ its better to open up your cmd(in case you are using windows) Type in ‘ipconfig’ hit enter and lookup and use your IPv4 Address.
I'm writing client-server application. Client sending command and server recieves it and do some manipulations. It works well. But when i'm trying to send response from server to client and recieve it on client side nothing happens. Even server do nothing. Program hangs and only Shift+F5 helps to finish it.
Server code:
class TNPClient
{
TNPBaseInterraction tnp_base;
private void SendError(TcpClient Client, int Code)
{
byte[] buf = Encoding.ASCII.GetBytes(Code.ToString());
Client.GetStream().Write(buf, 0, buf.Length);
Client.Close();
}
private void SendResponse(TcpClient Client, string response)
{
byte[] buf = Encoding.ASCII.GetBytes(response);
Client.GetStream().Write(buf, 0, buf.Length);
Client.Close();
}
void ParseMonitorRequest(TcpClient Client, string req)
{
MessageBox.Show("inside parser");
int term_id = Convert.ToInt32(req.Substring(2));
switch (req[1])
{
case '0':
List<MonitorStruct> monitors = tnp_base.GetMonitors(term_id);
foreach (MonitorStruct mon in monitors)
{
}
break;
case '1':
break;
case '2':
break;
case '3':
break;
case '4':
MessageBox.Show("inside 4");
List<TerminalStruct> terminals = tnp_base.GetTerminals();
foreach (TerminalStruct term in terminals)
{
MessageBox.Show("sending response");
MessageBox.Show(string.Format("ID: {0} Address: {1} Comment: {2}", term.TerminalID, term.Address, term.Comment));
//SendResponse(Client, string.Format("ID: {0} Address: {1} Comment: {2}", term.TerminalID, term.Address, term.Comment));
}
break;
}
}
void ParseTerminalRequest(TcpClient Client, string req)
{
}
public TNPClient(TcpClient Client)
{
try
{
tnp_base = new TNPBaseInterraction("127.0.0.1", "tnp", "tnp_user", "tnp123", "3406");
string Request = "";
byte[] buf = new byte[1024];
int Count = 0;
while ((Count = Client.GetStream().Read(buf, 0, buf.Length)) > 0)
{
Request += Encoding.ASCII.GetString(buf, 0, Count);
}
if (Request[0].Equals('0'))
{
ParseMonitorRequest(Client, Request);
}
else
{
ParseTerminalRequest(Client, Request);
}
}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.Message);
}
}
}
class TNPServer
{
TcpListener Listener;
int Port = 5252;
public TNPServer(int ServerPort)
{
int MaxThreadsCount = Environment.ProcessorCount * 4;
ThreadPool.SetMaxThreads(MaxThreadsCount, MaxThreadsCount);
ThreadPool.SetMinThreads(2, 2);
Port = ServerPort;
}
public void StartServer()
{
Listener = new TcpListener(IPAddress.Any, Port);
Listener.Start();
while (true)
{
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(ClientThread), Listener.AcceptTcpClient());
}
}
static void ClientThread(Object StateInfo)
{
new TNPClient((TcpClient)StateInfo);
}
~TNPServer()
{
if (Listener != null)
{
Listener.Stop();
}
}
}
Client side code (this code gives problem):
try
{
TcpClient client = new TcpClient("127.0.0.1", 5365);
if (client.Connected) MessageBox.Show("Connected");
byte[] buf = Encoding.ASCII.GetBytes(tbSendText.Text);
NetworkStream stream = client.GetStream();
stream.Write(buf, 0, buf.Length);
// System.Threading.Thread.Sleep(5000);
//client.ReceiveTimeout = Convert.ToInt32(TimeSpan.FromSeconds(1).TotalMilliseconds);
byte[] buffer = new byte[256];
int Count = 0;
string response = string.Empty;
// while ((Count = client.GetStream().Read(buffer, 0, buffer.Length)) > 0)
//{
Count = stream.Read(buffer, 0, buffer.Length);
response = Encoding.ASCII.GetString(buffer, 0, Count);
//}
stream.Close();
client.Close();
MessageBox.Show(response);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
All messages on server side are shown then i'm not trying to get response on client side. When i'm trying to get response no messages are shown, but connection is established.
In server side, the read loop is blocked forever. You need to read only up to to the length of the text & then parse the request. You can write the length of the text from client side & then write the content.
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;
}
How can I test SMTP is up and running via C# without sending a message.
I could of course try:
try{
// send email to "nonsense#example.com"
}
catch
{
// log "smtp is down"
}
There must be a more tidy way to do this.
You can try saying EHLO to your server and see if it responds with 250 OK. Of course this test doesn't guarantee you that you will succeed sending the mail later, but it is a good indication.
And here's a sample:
class Program
{
static void Main(string[] args)
{
using (var client = new TcpClient())
{
var server = "smtp.gmail.com";
var port = 465;
client.Connect(server, port);
// As GMail requires SSL we should use SslStream
// If your SMTP server doesn't support SSL you can
// work directly with the underlying stream
using (var stream = client.GetStream())
using (var sslStream = new SslStream(stream))
{
sslStream.AuthenticateAsClient(server);
using (var writer = new StreamWriter(sslStream))
using (var reader = new StreamReader(sslStream))
{
writer.WriteLine("EHLO " + server);
writer.Flush();
Console.WriteLine(reader.ReadLine());
// GMail responds with: 220 mx.google.com ESMTP
}
}
}
}
}
And here's the list of codes to expect.
I use this method and classes to validate the credentials (link to github):
public static bool ValidateCredentials(string login, string password, string server, int port, bool enableSsl) {
SmtpConnectorBase connector;
if (enableSsl) {
connector = new SmtpConnectorWithSsl(server, port);
} else {
connector = new SmtpConnectorWithoutSsl(server, port);
}
if (!connector.CheckResponse(220)) {
return false;
}
connector.SendData($"HELO {Dns.GetHostName()}{SmtpConnectorBase.EOF}");
if (!connector.CheckResponse(250)) {
return false;
}
connector.SendData($"AUTH LOGIN{SmtpConnectorBase.EOF}");
if (!connector.CheckResponse(334)) {
return false;
}
connector.SendData(Convert.ToBase64String(Encoding.UTF8.GetBytes($"{login}")) + SmtpConnectorBase.EOF);
if (!connector.CheckResponse(334)) {
return false;
}
connector.SendData(Convert.ToBase64String(Encoding.UTF8.GetBytes($"{password}")) + SmtpConnectorBase.EOF);
if (!connector.CheckResponse(235)) {
return false;
}
return true;
}
SmtpConnectorBase:
internal abstract class SmtpConnectorBase {
protected string SmtpServerAddress { get; set; }
protected int Port { get; set; }
public const string EOF = "\r\n";
protected SmtpConnectorBase(string smtpServerAddress, int port) {
SmtpServerAddress = smtpServerAddress;
Port = port;
}
public abstract bool CheckResponse(int expectedCode);
public abstract void SendData(string data);
}
SmtpConnectorWithoutSsl:
internal class SmtpConnectorWithoutSsl : SmtpConnectorBase {
private Socket _socket = null;
public SmtpConnectorWithoutSsl(string smtpServerAddress, int port) : base(smtpServerAddress, port) {
IPHostEntry hostEntry = Dns.GetHostEntry(smtpServerAddress);
IPEndPoint endPoint = new IPEndPoint(hostEntry.AddressList[0], port);
_socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
//try to connect and test the rsponse for code 220 = success
_socket.Connect(endPoint);
}
~SmtpConnectorWithoutSsl() {
try {
if (_socket != null) {
_socket.Close();
_socket.Dispose();
_socket = null;
}
} catch (Exception) {
;
}
}
public override bool CheckResponse(int expectedCode) {
while (_socket.Available == 0) {
System.Threading.Thread.Sleep(100);
}
byte[] responseArray = new byte[1024];
_socket.Receive(responseArray, 0, _socket.Available, SocketFlags.None);
string responseData = Encoding.UTF8.GetString(responseArray);
int responseCode = Convert.ToInt32(responseData.Substring(0, 3));
if (responseCode == expectedCode) {
return true;
}
return false;
}
public override void SendData(string data) {
byte[] dataArray = Encoding.UTF8.GetBytes(data);
_socket.Send(dataArray, 0, dataArray.Length, SocketFlags.None);
}
}
SmtpConnectorWithSsl:
internal class SmtpConnectorWithSsl : SmtpConnectorBase {
private SslStream _sslStream = null;
private TcpClient _client = null;
public SmtpConnectorWithSsl(string smtpServerAddress, int port) : base(smtpServerAddress, port) {
TcpClient client = new TcpClient(smtpServerAddress, port);
_sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
// The server name must match the name on the server certificate.
try {
_sslStream.AuthenticateAsClient(smtpServerAddress);
} catch (AuthenticationException e) {
_sslStream = null;
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();
}
}
~SmtpConnectorWithSsl() {
try {
if (_sslStream != null) {
_sslStream.Close();
_sslStream.Dispose();
_sslStream = null;
}
} catch (Exception) {
;
}
try {
if (_client != null) {
_client.Close();
_client = null;
}
} catch (Exception) {
;
}
}
// The following method is invoked by the RemoteCertificateValidationDelegate.
private 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;
}
public override bool CheckResponse(int expectedCode) {
if (_sslStream == null) {
return false;
}
var message = ReadMessageFromStream(_sslStream);
int responseCode = Convert.ToInt32(message.Substring(0, 3));
if (responseCode == expectedCode) {
return true;
}
return false;
}
public override void SendData(string data) {
byte[] messsage = Encoding.UTF8.GetBytes(data);
// Send hello message to the server.
_sslStream.Write(messsage);
_sslStream.Flush();
}
private string ReadMessageFromStream(SslStream stream) {
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do {
bytes = stream.Read(buffer, 0, buffer.Length);
// 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, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
// Check for EOF.
if (messageData.ToString().IndexOf(EOF) != -1) {
break;
}
} while (bytes != 0);
return messageData.ToString();
}
}
You could open up the port (25) with a socket or TcpClient and see if it responds.
Open a socket connection to the smtp server on port 25 and see if you get anything. If not, no smtp server.
Here is a nice open source tool (does more than MX):
http://www.codeproject.com/KB/IP/DNS_NET_Resolver.aspx