How can I connect a C# SSLStream client to an OpenSSL server? - c#

I am currentry writing a C# client (with Mono using SSLStream) for an already existing OpenSSL server written in C++. I know that in theory, they should be able to communicate, but I always get an Exception:
Authentication or decryption has failed.
Which takes place in Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback when I invoke sslStream.AuthenticateAsClient(ServerName);
I do not need client authentication, so i don't use any client certificate, and i can't load my server certificate into a client's trust store, as I can't use the command line on the client side.
On the server, I seem to receive some random data, which should not happen, as I never invoke sslStream.Write() in the client code. Maybe I don't completely understand how the handshake happens. I have an OpenSSL C++ client that works perfectly though.
Does someone know How I could get this working?
I am using, on the server side, a self-signed certificate generated by OpenSSL, which i converted to DER format.
How I proceed:
Server side
Initialize OpenSSL
Load server certificate (DER) and private key (PEM)
Open a non-SSL listening socket
As a client connects, I open a new socket which I connect to an SSL_Bio and accept the connection
Read the SSL socket for incoming data.
Client side
I am trying to connect using a temporary client adapted from the one in this topic.
Open a new TCPClient
Link a new SSLStream to it with the same validation callback as in the thread above;
Calling AuthenticateAsClient(ServerName) which brings this exception.
Invoke sslStream.Write but the execution never gets here.
Any help would be greatly appreciated as I am not sure to understand how sslStream internally works.

Ok, got it. In fact, I was setting up the BIO and the SSL variables in the wrong order. You have to create and attach your BIO to your SSL BEFORE you actually call SSL_Accept in OpenSSL, otherwise the authentication cannot complete, which seems logical.
Also, I was using SSLStream in C# on client side to send my messages, while I was using BIO_read and BIO_write on the server Side with OpenSSL.
This could not work, because BIO_read and BIO_write are no equivalent to SSL_read and SSL_write. These two functions only read and write on the underlying socket, without any encryption or decryption, making the SSL handshake almost useless (aside from checking the identity of the other end of the line).
This explains why I was not able to engage communication as the two ends did not speak the same language: one sent unencrypted messages while the other expected SSL-wrapped messages and the other way round.
The SSL_read and SSL_Write functions take care of the encryption and decryption, so they are the ones to use here. Now it works perfectly.
If you need any further details about how I did that, please comment this answer.

Related

C# HttpListener (netsh) problems with SelfSigned Certificate

I have some trouble with my HttpListener. I already searched and read some dicussions.
For example: Httplistener with HTTPS support
Firstly I try to describe my scenario:
I want to create a HTTP-Listener with SSL/HTTPS Support. Thats the main target.
I used OpenSSL to create my own CA and I created my own server cert.
Now I have:
myCa.key
myCa.pem
myCa.srl
myServer.key
myServer.csr
myServer.crt
I installed the myCa.pem and the myServer.crt certificate to my local computer. I moved the CA in the trusted store and the server certificate in "own certificates"
Then I took the fingerprint (certHash) of my server certificate.
I created the netsh entry with admin-rights
netsh http add sslcert ipport=0.0.0.0:9649 appid= '{0a5ce-569a-4dc6-8ed7-9ef91241dec3}' certhash=4F556BDC3F97B31D555266DA74F573777FCCAA55
My C# implementation is relativly simple:
this.Listener = new HttpListener();
this.Listener.Prefixes.Add("https://*:9649");
this.Listener.Start();
this.Listener.BeginGetContext(new AsyncCallback(ProcessClient), this.Listener);
//Process an incoming connection
private void ProcessClient(IAsyncResult result)
{
var listener = (HttpListener)result.AsyncState;
var clientContext = listener.EndGetContext(result);
}
When I implemented SSL in my TcpStack I used a SSL-Stream and there I can validate a certificate with a ValidationCallback. Im not sure if this is possible here. I tried
ServicePointManager.ServerCertificateValidationCallback += ValidateCert; But I never hit my breakpoint there.
Now to the problems:
When I try to connect with my own HttpClient (.NET HttpClient Class) I get always a RemoteNameMismatch Error on the SSL-Layer. I dont hit the breakpoint in the ProcessClient method. I tried without specific certificate (auto detection) and I tried also to advise the same certificate (with the same hash) to the client. In both cases I got the same error. I dont understand why I get any erros when I use the same certificate on the client and the server side. I always thought the netsh will compare the certhashes.
When I try a connect with Postman I hit the ProcessClient function. But Postman gets an error that he cant check the certificate. But I think the problem is that my certificate isnt a official certifcate . But the data exchange is working.
Another point is: I want to roll out my app also in containers with a unix os. .NET60 is designed for crossplatform. But whats the unix pendant to netsh? Is it possible to run my listener with https on unix? How works the mapping here between app and certificate?
Maybe I have to change my technology? Alternative to HttpListener? Mainly I dont want to use thridparty stuff.
UPDATE Solution:
See my answer below
Thanks for reading and for help.
Greetings
Like the guys said in the in comments. The FDQN was the problem. In easy words: I created my own CA and then I created a server cert signing request against the CA. Inside the server cert the CN is matching to my DNS of my personal computer. The connection with my HTTP-Listener is working now. Thank you for your help!

C# SslStream app encountering "No Common Algorithm" error, yet cipher in trace is available

I have a simple C# application that talks to a remote server over TLS. When I attempt to "AuthenticateAsClient," I immediately receive the dreaded "the client and the server cannot communicated, because they do not possess a common algorithm."
So, I broke out a wire trace and observe that the "Client Hello" completes and serves up a cipher suite list. The "Server Hello" completes with a certificate and a selected cipher suite from the list provided in the "Client Hello". However, I then expect to see a "CIPHER CHANGE SPEC" next, but instead, the client resets/acks the connection, and fails, and I have no idea why. The server is sending down TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xC014).
What I've tried:
I suspected a problem with the server certificate, but verified that it has a KeySpec of 1 and validates properly. Its cert chain validates. Even when exported from the server and moved to my client box (sans private key), the validation chain seems OK.
I've verified via the IISCrypto tool that AES_256 is enabled and available on both the server and the client. I do notice, however, that in spite of my specifying TLS 1.2, the wire traces show that the server is only responding with TLS 1.0. I pointed the client to another instance of the application on a different machine, but it does respond with TLS 1.2, and works correctly - but with a different cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xC028).
I've also tried the opposite extreme of forcing the app down to TLS 1.0 against the "good" (working) server, and the result is the same - it still works.
I would assume that the failure to send the CIPHER CHANGE SPEC was due to the presumably incorrect determination that it had no common algorithms, but how could the client have offered it if it weren't available? Can something be "corrupted" within one of those cipher specs that wouldn't be realized until it was actually used?
For completeness, I've excerpted the portion of the program that does the SSL connect into a small console app, and it seems trivially simple, but I've reproduced it here in the event someone can see that I'm doing something incorrectly:
static void Main(string[] args)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
int portNumber = 9999999; //edited for obvious reasons
TcpClient x = new TcpClient("the.server.name", portNumber);
var foo = new System.Net.Security.SslStream(x.GetStream(), false,
(a,b,c,d) => {
Console.WriteLine("connected");
return true;
});
foo.AuthenticateAsClient("the.server.name");
Console.WriteLine(foo.CipherAlgorithm.ToString());
}

How to tell C# SslStream AuthenticateAsServer to send a Certificate Trust List (CTL)

I am trying to build my own secure (non-http) server using C#. Of course, I'm using the SslStream and AuthenticateAsServer. This works like a champ, except for I can't figure out how to make the server send a Certificate Trust List (CTL) to the client as part of the Client Certificate Request.
I see the LocalCertificateSelectionCallback, but that only seems applicable for AuthenticateAsClient since the AcceptedIssuers list is passed into the call back rather than you being able to specify it somehow.
I see how to set the CTL in IIS, but not in C# code. I'm working with raw sockets (with TLS encryption) so I can't just use IIS to host this service.
Finally figured this out:
create a DWORD Value under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL
called "SendTrustedIssuerList" and set to "1"

C#-Server / C++ Client: Socket.Accept() never finishes

I'm writing a simple C++ client that should connect to a C# server. And, while the client runs throught fine and even states it sent a bunch of bites, the C# server never gets past listener.Acceppt():
Console.WriteLine("Waiting for a connection...");
// Program is suspended while waiting for an incoming connection.
Socket handler = listener.Accept();
I took the server source from here: Microsoft MSDN: Synchronous Server Socket Example
The client code I use can be found here: Microsoft MSDN: Complete Winsock Client Code
I also checked to server with the according C# client, which worked fine. I also checked the return values of the client, which also looked pretty plausible. So I'd guess the problem lies somewhere in how C++ / C# handle the details.
I tried Wireshark to find some hints, but mysteriously there was absolutely no trace of any communication on the used port (11000).
Where should I start looking to solve this issue?
Update: All communication at the moment runs just locally. I tried my IPAddress (from ipconfig) and 127.0.0.1 for both server and client.
I just have one network adapter installed, I use that IP adress. The client checks the getaddrinfo(argv[1], DEFAULT_PORT, &hints, &result) and returns valid information (e.g. TCP as protocol). connect() returns 0, which should be ok.
Update 2:
I tried different combinations of C++/C# Server-Client-Setups:
C++-Server, C++ Client: Works
C++-Server, C# Client: Works
C#-Server, C# Client: Works
C#-Server, C++ Client: Does not work
C#-Server, putty: Works
Update 3: I tried Wireshark on the other Server-Client constellations, but neither of them did show any traffic on tcp.port == 11000, although they did work (see Update 2). It looks like Wireshark does not show any results, because everything is just local (see Wireshark localhost traffic capture)
I had the same problem.
The connection with winsock is asynchronous. And the example in C# that you are using is synchronous.
You have to use a Asynchronous example to get communication with your C++ code.
I used this two examples!
C Sharp server
https://msdn.microsoft.com/en-us/library/fx6588te(v=vs.110).aspx
C plus plus client
https://msdn.microsoft.com/en-us/library/windows/desktop/ms737591(v=vs.85).aspx

C#, Metro, Stream Socket, SSL Untrusted Host, Squid

I need your help one more time.
I can't get how to successfully establish a StreamSocket connection from Metro application to untrusted root certificate host.
I'm connecting like this:
await socket.ConnectAsync(new HostName("_UNTRUSTED_HOST_IP_ADDRESS_"), "_SSL_PORT_", SocketProtectionLevel.Ssl);
And it fails :(
I can't establish a plain HTTP connection because of the host restriction.
Some say that I need to make a proxy server that will forward SSL queries to that untrusted host and which will mark this connection as trusted.
But how to do it? I didn't get it for now :(
I'm using Squid on my CentOS web-server and did this to squid.conf:
acl TrustedHosts url_regex _UNTRUSTED_HOST_IP_ADDRESS_
sslproxy_cert_error allow TrustedHosts
sslproxy_cert_error deny all
I need StreamSocket, because I must read to and write from the socket to make some valuable actions.
If anyone can help me with this...
I did it by modifying the Control of the StreamSocket that way :
_streamSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted);
_streamSocket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName);
ChainValidationResult contains multiple options, choose the one best suited for you.

Categories