C# SSLStream Read Function Throws IOException - c#

Im trying to create my own HTTPS proxy Server.
For some reason, i got an exception when I try to read from sslstream object.
Here is it:
An unhandled exception of type 'System.IO.IOException' occurred in System.dll
Additional information: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
Here is my code:
public Server()
{
m_portnumber = 4434;
m_tcplistener = new TcpListener(IPAddress.Any, m_portnumber);
m_cert = createCertificate();
}
public void start()
{
m_tcplistener.Start();
while (true)
{
TcpClient client = m_tcplistener.AcceptTcpClient();
ClientHandler(client);
}
}
private void ClientHandler(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(m_cert,
false, SslProtocols.Tls, 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);
}
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 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
{
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.
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.");
}
}
private static void DisplayUsage()
{
Console.WriteLine("To start the server specify:");
Console.WriteLine("serverSync certificateFile.cer");
Environment.Exit(1);
}
private X509Certificate createCertificate()
{
byte[] c = Certificate.CreateSelfSignCertificatePfx(
"CN=localhost", //host name
DateTime.Parse("2015-01-01"), //not valid before
DateTime.Parse("2020-01-01"), //not valid after
"sslpass"); //password to encrypt key file
using (BinaryWriter binWriter = new BinaryWriter(File.Open(#"cert.pfx", FileMode.Create)))
{
binWriter.Write(c);
}
return new X509Certificate2(#"cert.pfx", "sslpass");
}
}

This is not an SSL error, but a TCP one. You are connecting to an ip/port pair that is not listening. Is an an active reject, so it is not like you're reaching the IP and is telling you there is no port. Is a timeout, that could mean invalid IP or the target's firewall is ignoring you (intentionally).
My first suspicion would be line m_portnumber = 4434;. This is an unusual port number. You're sure is not a typo, and you wanted the HTTPS usual port (443) ? If you truly mean 4434, you need to check the network connectivity. Make sure the IP resolves properly, is reachable, the target is listening and the firewall is letting you in.

I guess above code is taken from https://learn.microsoft.com/en-us/dotnet/api/system.net.security.sslstream?view=net-5.0
Was facing similar issue with ReadMessage(SslStream sslStream) on one of windows machine (Win10 1809).
There are two things to note here :
sslStream.Read will read everything in authenticated network stream including Header and Body. So it's your responsibility to parse both and come out loop.
In my case machine was taking really long time (more than 5 seconds) to read from the stream. So i had to remove time read timeout. Here it is
sslStream.ReadTimeout = 5000; \\ By default it is infinite

Related

Fix for Ssl Asynchronous Client in C#

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

How to make an HTTP Get request along with SslStream & TcpClient

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.

TLS - TCP Client Authenticates on SslStream.AuthenticateAsClient(servername) but the server is not connecting to any Client, all on local machine C#

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.

C# ssl/tls with socket tcp

I am new in C# development. I am trying to use ssl/tls over tcp but in my code, system.net.sockets.socket (bare socket) is used not tcpclient or tcplistner. I have searched over net atleast 200 links but I didn't get anything related that. I want to use less coding and done ssl or tsll over tcp socket connection. I have client, server, ca certificate, key in .key format. Please help with example or link. You can ask questions if u feel more details.
Why don't you want to use TcpClient? Creating a SSL connection with TcpClient and Ssltream is quite easy. Unless you require thousands of simultaneous connections I would stick with TcpClient and SSLStream.
A basic TcpClient and SslStream example would be as follows:
static void Main(string[] args)
{
string server = "127.0.0.1";
TcpClient client = new TcpClient(server, 443);
using (SslStream sslStream = new SslStream(client.GetStream(), false,
new RemoteCertificateValidationCallback(ValidateServerCertificate), null))
{
sslStream.AuthenticateAsClient(server);
// This is where you read and send data
}
client.Close();
}
public static bool ValidateServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
High performance socket code can be difficult to write in .NET but there are good examples out there. You have a number of choices. I'm not sure one solution fits all so I'll list a few here.
Using an [Asynchronous Client Socket] maybe be a good place to start
There are a few existing libraries you can make use of. I've used Nito/Async however I don't think it has been updated since 2009. There was talk of a version 2 but I don't believe it materialized.
I'm not familar with it but CodeProject has C# SocketAsyncEventArgs High Performance Socket Code
Review Microsoft's guidance, High Performance .NET Socket Server Using Async Winsock
Read everything that Stephen Toub has to say including Awaiting Socket Operations
I didn't address SSL specifically but look into the SslStream Class.
You'll also want to look into buffer pooling. If you're serving thousands of clients garbage collection will be a problem. An excellent introduction to this is Sunny Ahuwanya's Blog
https://github.com/StephenCleary/AsyncEx
before.
You should be able to use System.Net.Sockets.NetworkStream to wrap your socket and then System.Net.Security.SslStream to wrap the network stream.
Socket socket;
Stream networkStream = new NetworkStream(socket);
Stream sslStream = new SslStream(networkStream);
this is for you
public class SSL
{
private bool CertificateValidationCallback(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return true;
}
public class Certificate
{
public System.Security.Cryptography.X509Certificates.X509Certificate2 GetCertificate()
{
string certificatePath = #"D:\Rayan Ab Niro\Projecr\VPN\WindowsVPN\SSL.pfx"; //ConfigurationManager.AppSettings["certificatePath"].ToString();
var stream = File.OpenRead(certificatePath);
return new System.Security.Cryptography.X509Certificates.X509Certificate2(ReadStream(stream), "mypassword", System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.MachineKeySet | System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable);
}
private 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();
}
}
}
public System.Net.Security.SslStream GetStream(NetworkStream _NetworkStream)
{
try
{
System.Net.Security.SslStream sslStream = new System.Net.Security.SslStream(_NetworkStream, false,);
sslStream.AuthenticateAsServer(serverCertificate, clientCertificateRequired: false,
enabledSslProtocols:
System.Security.Authentication.SslProtocols.Default |
System.Security.Authentication.SslProtocols.None |
System.Security.Authentication.SslProtocols.Tls |
System.Security.Authentication.SslProtocols.Tls11 |
System.Security.Authentication.SslProtocols.Tls12 |
System.Security.Authentication.SslProtocols.Ssl2 |
System.Security.Authentication.SslProtocols.Ssl3
, checkCertificateRevocation: true);
new SSL().DisplaySecurityLevel(sslStream);
new SSL().DisplaySecurityServices(sslStream);
new SSL().DisplayCertificateInformation(sslStream);
new SSL().DisplayStreamProperties(sslStream);
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
return sslStream;
}
catch (Exception ex)
{
throw ex;
}
}
public string ReadMessage(System.Net.Security.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();
}
public void DisplaySecurityLevel(System.Net.Security.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);
}
public void DisplaySecurityServices(System.Net.Security.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);
}
public void DisplayStreamProperties(System.Net.Security.SslStream stream)
{
Console.WriteLine("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
Console.WriteLine("Can timeout: {0}", stream.CanTimeout);
}
public void DisplayCertificateInformation(System.Net.Security.SslStream stream)
{
Console.WriteLine("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);
System.Security.Cryptography.X509Certificates.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.
System.Security.Cryptography.X509Certificates.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.");
}
}
}

SSL TCP SslStream Server throws unhandled exception "System.Security.Cryptography.CryptographicException: cannot find the original signer"

I'm trying to create a C# TCP server to receive TCP data with SslStream on a Windows 2008 server from a client (objective C mobile application) sending TCP data.
I'm using Microsoft's sample code (NOTE: my modified version of that code is at the end of this question) i.e. it is the server code, just under the line "The following code example demonstrates creating an TcpListener that uses the SslStream class to communicate with clients.")
Exception
However, when I run this server code I get the following exception:
System.Security.Cryptography.CryptographicException: Cannot find the original si
gner.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicExce
ption(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromFile(
String fileName, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCer
tContextHandle& pCertCtx)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertific
ateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlag
s)
at System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromCe
rtFile(String filename)
at SslTcpServer.LocationSslTcpServer.RunServer(String certificate) in c:\SslTcpServer\Program.cs:line 20
at SslTcpServer.Program.Main(String[] args) in c:\SslTcpServer\Program.cs:line 180
I've also tried the code at http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate.aspx and it throws a similar exception.
Additional Information
I got my SSL certificate from NameCheap. I purchased the EssentialSSL Wildcard certificate. I made a Created Certificate Request on the Windows 2008 server i.e. that gaint text starting with:
-----BEGIN NEW CERTIFICATE REQUEST-----
alots of random characters
-----END NEW CERTIFICATE REQUEST-----
and uploaded that gaint text file to NameCheap and got emailed a Certificate.cer file.
My Code
public sealed class LocationSslTcpServer
{
static X509Certificate serverCertificate = null;
// The certificate parameter specifies the name of the file
// containing the machine certificate.
public static void RunServer(string certificate)
{
serverCertificate = X509Certificate.CreateFromCertFile(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,
false, SslProtocols.Tls, 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.<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
{
// 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 void DisplayUsage()
{
Console.WriteLine("To start the server specify:");
Console.WriteLine("serverSync certificateFile.cer");
Environment.Exit(1);
}
}
class Program
{
static int Main(string[] args)
{
string certificate = null;
certificate = "Certificate.cer";
try
{
LocationSslTcpServer.RunServer(certificate);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
Console.ReadLine();
}
return 0;
}
}
Thanks in advance for helping!
I successfully used X.509 Digital Certificate Generator to:
Create CA certificate (Ca.pfx).
Create Client certificate signed with a CA certificate (Client.pfx).
Create Server certificate signed with a CA certificate (Server.pfx).
Install CA certificate (Ca.pfx) in the Trusted Root Certificate folder.
Install Client and Server certificate (Client.pfx and Server.pfx) in Personal folder.
For steps 4 and 5: From the Search Box (near the Windows start button - left bottom your desktop) typo: cert, then select the Manage Computer Certificates application.

Categories

Resources