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.
Related
Here is my code:
A listener to wait for connection from client:
static void Main(string[] args)
{
IPAddress ipAddr = IPAddress.Parse(args[1]);
TcpListener listener = new TcpListener(ipAddr, Int32.Parse(args[2]));
listener.Start();
Console.WriteLine("Waiting for a connection.");
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Client accepted.");
while (true)
{
NetworkStream stream = client.GetStream();
StreamReader sr = new StreamReader(client.GetStream());
StreamWriter sw = new StreamWriter(client.GetStream());
try
{
if (stream.DataAvailable)
{
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
int recv = 0;
foreach (byte b in buffer)
{
if (b != 0)
{
recv++;
}
}
string request = Encoding.UTF8.GetString(buffer, 0, recv);
Console.WriteLine("request received: " + request);
if (request != null)
{
string response = null;
response = apiQueryAndReponse(request, args[0]);
if (response != null)
{
byte[] byData = Encoding.ASCII.GetBytes(response);
stream.Write(byData, 0, byData.Length);
stream.Flush();
}
}
}
}
catch (Exception e)
{
Console.WriteLine("Something went wrong.");
Console.WriteLine(e.Message.ToString());
//sw.WriteLine(e.ToString());
}
}
}
Get and return the response:
private static string apiQueryAndReponse(string rec, String stagingfilepath)
{
String response = null;
if (rec.Contains("GetTesterInfo"))
{
response = getLatestStatusOK("GetTesterInfo", stagingfilepath);
if (response != null)
{
Console.WriteLine("Response: " + response + "," + fileline + "\n");
fileline++;
}
}
return response;
}
Read the text file and get the response:
private static String getLatestStatusOK(String key, String filedir)
{
using (var fs = new FileStream(filedir, FileMode.Open, FileAccess.Read))
{
using (var sr = new StreamReader(fs, Encoding.UTF8))
{
while ((stagingfiledata = sr.ReadLine()) != null)
{
try
{
if (stagingfiledata.Contains(key))
{
String[] data = stagingfiledata.Split(",");
response = data[2];
}
}
catch (Exception exp)
{
Console.WriteLine("err message:" + exp.Message);
}
}
}
}
return response;
}
What I trying to do here: I will read a text file and get response to reply to client. But the socket will disconnect after access the text file.(I have tried connect with client without call the text file access function). I want to maintain connection and read text file when it necessary.
There are a number of issues with this code.
Your primary issue: you are creating a new StreamReader and StreamWriter on each loop, and they dispose the underlying stream when they are garbage-collected.
You aren't even using those readers and writers, you may as well remove them
You are missing using in a number of places.
The number of bytes received is returned from the Read function, you do not have to guesstimate by checking for \0.
static void Main(string[] args)
{
IPAddress ipAddr = IPAddress.Parse(args[1]);
TcpListener listener = new TcpListener(ipAddr, Int32.Parse(args[2]));
listener.Start();
Console.WriteLine("Waiting for a connection.");
using TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Client accepted.");
using NetworkStream stream = client.GetStream();
while (true)
{
try
{
byte[] buffer = new byte[1024];
int recv = stream.Read(buffer, 0, buffer.Length);
string request = Encoding.UTF8.GetString(buffer, 0, recv);
Console.WriteLine("request received: " + request);
if (request != null)
{
string response = null;
response = apiQueryAndReponse(request, args[0]);
if (response != null)
{
byte[] byData = Encoding.UTF8.GetBytes(response);
stream.Write(byData, 0, byData.Length);
stream.Flush();
}
}
}
catch (Exception e)
{
Console.WriteLine("Something went wrong.");
Console.WriteLine(e.Message.ToString());
//sw.WriteLine(e.ToString());
}
}
}
There are other serious flaws with your design:
TCP does not guarantee that a single write will become a single read on the other end of the wire. Chunks of data may be split or combined. It's a stream, not a messaging protocol.
So you need a framing mechanism. The easiest one to use is to first pass the size of your data, then read that amount of bytes.
You are also not able to handle multiple clients. You need to hand off each one to a Task.
Corrollary to that, you should use async functions to improve performance and responsiveness.
You should also have a cancellation token which you can use if someone presses CTRL+C.
You probably shouldn't try to handle an exception and then continue. If an exception happens, log it and close the connection.
static CancellationTokenSource _cancellation = new();
static async Task Main(string[] args)
{
Console.CancelKeyPress += (sender, e) => _cancellation.Cancel();
IPAddress ipAddr = IPAddress.Parse(args[1]);
TcpListener listener = new TcpListener(ipAddr, Int32.Parse(args[2]));
listener.Start();
try
{
Console.WriteLine("Waiting for a connection.");
TcpClient client = await listener.AcceptTcpClientAsync(_cancellation.Token);
Console.WriteLine("Client accepted.");
Task.Run(() => HandleClient(client), _cancellation.Token);
}
catch (OperationCanceledException)
{ //
}
finally
{
listener.Stop();
}
}
private async Task HandleClient(TcpClient client)
{
using var _ = client;
await using NetworkStream stream = client.GetStream();
var lengthBuf = new byte[4];
try
{
while (true)
{
await stream.ReadExactlyAsync(lengthBuf, 0, 4, _cancellation.Token);
var length = BitConverter.ToInt32(lengthBuf, 0);
if(length > SomeMaxLengthHere || length <= 0)
throw new Exception("Too long");
byte[] buffer = new byte[length];
await stream.ReadExactly(buffer, 0, length, _cancellation.Token);
string request = Encoding.UTF8.GetString(buffer, 0, length);
Console.WriteLine("request received: " + request);
if (request != null)
{
string response = apiQueryAndReponse(request, args[0]);
if (response != null)
{
byte[] byData = Encoding.UTF8.GetBytes(response);
await stream.WriteAsync(byData, 0, byData.Length, _cancellation.Token);
await stream.FlushAsync(_cancellation.Token);
}
}
}
}
catch (OperationCanceledException)
{ //
}
catch (Exception e)
{
Console.WriteLine("Something went wrong.");
Console.WriteLine(e.Message.ToString());
//sw.WriteLine(e.ToString());
}
}
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
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 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");
I'm trying to make a server client using a local console server on my pc and a client on windows phone 8.1. The problem that I have is that I don't know how to read the incoming data from the client. I've searched the internet and read serveral microsoft tutorials but they do not explain how to read the incoming data in the server. Here's what I have.
Client on windows phone 8.1:
private async void tryConnect()
{
if (connected)
{
StatusLabel.Text = "Already connected";
return;
}
try
{
// serverHostnameString = "127.0.0.1"
// serverPort = "1330"
StatusLabel.Text = "Trying to connect ...";
serverHost = new HostName(serverHostnameString);
// Try to connect to the
await clientSocket.ConnectAsync(serverHost, serverPort);
connected = true;
StatusLabel.Text = "Connection established" + Environment.NewLine;
}
catch (Exception exception)
{
// If this is an unknown status,
// it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
StatusLabel.Text = "Connect failed with error: " + exception.Message;
// Could retry the connection, but for this simple example
// just close the socket.
closing = true;
// the Close method is mapped to the C# Dispose
clientSocket.Dispose();
clientSocket = null;
}
}
private async void sendData(string data)
{
if (!connected)
{
StatusLabel.Text = "Must be connected to send!";
return;
}
UInt32 len = 0; // Gets the UTF-8 string length.
try
{
StatusLabel.Text = "Trying to send data ...";
// add a newline to the text to send
string sendData = "jo";
DataWriter writer = new DataWriter(clientSocket.OutputStream);
len = writer.MeasureString(sendData); // Gets the UTF-8 string length.
// Call StoreAsync method to store the data to a backing stream
await writer.StoreAsync();
StatusLabel.Text = "Data was sent" + Environment.NewLine;
// detach the stream and close it
writer.DetachStream();
writer.Dispose();
}
catch (Exception exception)
{
// If this is an unknown status,
// it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
StatusLabel.Text = "Send data or receive failed with error: " + exception.Message;
// Could retry the connection, but for this simple example
// just close the socket.
closing = true;
clientSocket.Dispose();
clientSocket = null;
connected = false;
}
}
(from http://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj150599.aspx)
And the server:
public class Server
{
private TcpClient incomingClient;
public Server()
{
TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1330);
listener.Start();
Console.WriteLine("Waiting for connection...");
while (true)
{
//AcceptTcpClient waits for a connection from the client
incomingClient = listener.AcceptTcpClient();
//start a new thread to handle this connection so we can go back to waiting for another client
Thread thread = new Thread(HandleClientThread);
thread.IsBackground = true;
thread.Start(incomingClient);
}
}
private void HandleClientThread(object obj)
{
TcpClient client = obj as TcpClient;
Console.WriteLine("Connection found!");
while (true)
{
//how to read and send data back?
}
}
}
It comes to the point where the server prints 'Connection found!', but I don't know how to go further.
Any help is appreciated!
EDIT:
Now my handleclientthread method looks like this:
private void HandleClientThread(object obj)
{
TcpClient client = obj as TcpClient;
netStream = client.GetStream();
byte[] rcvBuffer = new byte[500]; // Receive buffer
int bytesRcvd; // Received byte count
int totalBytesEchoed = 0;
Console.WriteLine("Connection found!");
while (true)
{
while ((bytesRcvd = netStream.Read(rcvBuffer, 0, rcvBuffer.Length)) > 0)
{
netStream.Write(rcvBuffer, 0, bytesRcvd);
totalBytesEchoed += bytesRcvd;
}
Console.WriteLine(totalBytesEchoed);
}
}
But it still doesn't write the bytes to the console
So... after a lot of searching the internet I have found a solution...
Server: to read from the server and send data back to the phone:
// method in a new thread, for each connection
private void HandleClientThread(object obj)
{
TcpClient client = obj as TcpClient;
netStream = client.GetStream();
Console.WriteLine("Connection found!");
while (true)
{
// read data
byte[] buffer = new byte[1024];
int totalRead = 0;
do
{
int read = client.GetStream().Read(buffer, totalRead, buffer.Length - totalRead);
totalRead += read;
} while (client.GetStream().DataAvailable);
string received = Encoding.ASCII.GetString(buffer, 0, totalRead);
Console.WriteLine("\nResponse from client: {0}", received);
// do some actions
byte[] bytes = Encoding.ASCII.GetBytes(received);
// send data back
client.GetStream().WriteAsync(bytes, 0, bytes.Length);
}
}
Phone(client): to send messages from the phone and read the messages from server:
private async void sendData(string dataToSend)
// import for AsBuffer(): using System.Runtime.InteropServices.WindowsRuntime;
{
if (!connected)
{
StatusLabel.Text = "Status: Must be connected to send!";
return;
}
try
{
byte[] data = GetBytes(dataToSend);
IBuffer buffer = data.AsBuffer();
StatusLabel.Text = "Status: Trying to send data ...";
await clientSocket.OutputStream.WriteAsync(buffer);
StatusLabel.Text = "Status: Data was sent" + Environment.NewLine;
}
catch (Exception exception)
{
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
StatusLabel.Text = "Status: Send data or receive failed with error: " + exception.Message;
closing = true;
clientSocket.Dispose();
clientSocket = null;
connected = false;
}
readData();
}
private async void readData()
{
StatusLabel.Text = "Trying to receive data ...";
try
{
IBuffer buffer = new byte[1024].AsBuffer();
await clientSocket.InputStream.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.Partial);
byte[] result = buffer.ToArray();
StatusLabel.Text = GetString(result);
}
catch (Exception exception)
{
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
StatusLabel.Text = "Receive failed with error: " + exception.Message;
closing = true;
clientSocket.Dispose();
clientSocket = null;
connected = false;
}
}
The 'await clientSocket.InputStream.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.Partial)' command in the readData method was very unclear for me. I didn't know you had to make a new buffer, and the ReadAsync-method fills it(as i inderstand it). Found it here: StreamSocket.InputStreamOptions.ReadAsync hangs when using Wait()