Using ClientWebSocket for Secured(SSL) connection to Websocket server - c#

I tried to do googling for example code for echo client which supports SSL (and proxy) in .Net, i have simple echo client which does not support SSL and Proxy, but i need client which has got SSL implementation with Certificates (Self Signed)
I am using ClientWebSocket for connection (Websocket Server is in java).
How to share same self-signed certificate between websocket client(.net) and server (java) ?
I am trying to find some example but not getting anyhting, any direction would be a great help.

I am not completely sure about what are you actually asking, but...
If you want to use a self-signed certificate for the client, you need to validate that certificate manually using : ServicePointManager.ServerCertificateValidationCallback
For the sake of testing, you can do:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
But when moving to production you should remove this code or put an actual certification validation code.

The previous solution will not work for the ClientWebSocket connection.
webSocket = new ClientWebSocket();
webSocket.Options.RemoteCertificateValidationCallback += (o, c, ch, er) => true;
Will emphasize one more time
when moving to production you should remove this code or put an actual certification validation code.

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!

Mailkit IMapClient not hitting ServerCertificateValidationCallback & SslHandshakeException

I'm attempting to connect to an IMAP server using the following code on a Windows Server 2019 machine:
using (var client = new ImapClient(new ProtocolLogger("protocol.log")))
{
var address = EnvReader.GetStringValue("EMAIL_ADDRESS");
var password = EnvReader.GetStringValue("EMAIL_PASSWORD");
var creds = new NetworkCredential(address, password);
client.CheckCertificateRevocation = false;
client.ServerCertificateValidationCallback = (s, c, h, e) =>
{
Console.WriteLine("ALL UP IN THIS CALLBACK" + e.ToString());
return true;
};
client.Connect("outlook.office365.com", 993, SecureSocketOptions.SslOnConnect);
client.Authenticate(address, password);
}
On my Mac, this code runs perfectly fine, I can connect and subsequently authenticate just fine.
On the Windows machine I receive the following exception:
MailKit.Security.SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection.
This usually means that the SSL certificate presented by the server is not trusted by the system for one or more of
the following reasons:
1. The server is using a self-signed certificate which cannot be verified.
2. The local system is missing a Root or Intermediate certificate needed to verify the server's certificate.
3. A Certificate Authority CRL server for one or more of the certificates in the chain is temporarily unavailable.
4. The certificate presented by the server is expired or invalid.
5. The set of SSL/TLS protocols supported by the client and server do not match.
6. You are trying to connect to a port which does not support SSL/TLS.
See https://github.com/jstedfast/MailKit/blob/master/FAQ.md#SslHandshakeException for possible solutions
Based on the info in the linked FAQ, I added the ServerCertificateValidationCallback, however the callback is never hit (The previously mentioned exception is still thrown, the relevant console logging never occurs, and a breakpoint inside the callback is never hit while debugging).
From my reading, the ServerCertificateValidationCallback should handle cases #1-4 that the exception message mentions. The fact that I can connect on the specified port on my Mac would seem to rule out case #6 (I also tried port 143 + SecureSocketOptions.StartTls). That leaves case #5, however, I can't find any information suggesting that Windows Server 2019 can't handle SSL/TSL protocols.
Any ideas for a) dealing with this exception and/or b) figuring out why the ServerCertificateValidationCallback is not firing would be greatly appreciated.
Edit: My project is referencing .NET 5.0
Let's go through each of the possibilities:
The server is using a self-signed certificate which cannot be verified.
outlook.office365.com would not be using a self-signed certificate, so that wouldn't be an issue in this case.
The local system is missing a Root or Intermediate certificate needed to verify the server's certificate.
This one is very possible, but the ServerCertificateValidationCallback override should be overriding this failure. However, it's not getting hit... so it's not actually bypassing this potential error.
A Certificate Authority CRL server for one or more of the certificates in the chain is temporarily unavailable.
This would be negated by client.CheckCertificateRevocation = false;
The certificate presented by the server is expired or invalid.
This is not the case because the certificate does not expire until 1/21/2022.
The set of SSL/TLS protocols supported by the client and server do not match.
The server supports at least TLSv1.2 which is a default TLS protocol version supported by MailKit in all target framework versions (.NET 4.5 -> 5.0 + netstandard2.x's).
You are trying to connect to a port which does not support SSL/TLS.
Port 993 is the correct port and SslOnConnect is the correct option, so this is not the issue.
Assuming there isn't a bug in MailKit's SslStream.AuthenticateAsClientAsync() call that passes in the validation callback method (.NET 5.0 is different than other versions), what is the InnerException? Maybe that will provide some insight.

SslStream and Authentication

I was reading MSDN info articles for quite a long time and still I fail to understand it.
Based on the assumption that client Authentication is not required:
1.When I call SslStream.AuthenticateAsServer(...) do I call this method on the server side or on the client side?
2.When establishing a SslStream is it only the responsibility of the server to establish the SslStream or both server and client?
3.If it is only the responsibility of the server, does it mean that the client can just use regular send() and receive() operations without creating a SslStream by himself?
4.Does the client need to get the certificate file in order to authenticate the server?
Thank you very much in advance, I really could not find much information about this topic and I've been searching for this information for a long time...
EDIT: the MSDN has a complete working example at the bottom of this page: https://msdn.microsoft.com/en-us/library/system.net.security.sslstream?f=255&MSPPError=-2147217396 - so you should really start experimenting there because that example has it all.
Original answer:
I must preface this answer that "client authentication not required" is the case for most of the SSL implementations. Client authentication is rare: you're likely to see it in VPN apps, the banking industry and other secure apps. So it would be wise when you are experimenting with SslStream() to start without client authentication.
When you browse to an HTTPS website, you don't authenticate your browser with a client cert, instead you just want to confirm the server name you are connecting to matches up to the CNAME found in the cert and that the server cert is signed by a CA that your machine trusts - there's more to it, but essentially that's what it boils down to.
So, having said that, let me answer your questions:
1) SslStream.AuthenticateAsServer(...) is done ONLY on server side with the server 509 certificate. On the client side, you must call SslStream.AuthenticateAsClient(serverName) with server name being the CNAME (common name) of your certificate (example: "domain.com")
2) SslStream must be created for both the client and the server. You create it simply by "wrapping" a TcpClient NetworkStream around it (for example, but there are other methods)
Example for the server:
// assuming an 509 certificate has been loaded before in an init method of some sort
X509Certificate serverCertificate = X509Certificate2.CreateFromCertFile("c:\\mycert.cer"); // for illustration only, don't do it like this in production
...
// assuming a TcpClient tcpClient was accepted somewhere above this code
slStream sslStream = new SslStream(tcpClient.GetStream(), false);
sslStream.AuthenticateAsServer(
serverCertificate,
false,
SslProtocols.Tls,
true);
3) No. The communication is encrypted on both ends. So both sides must use SslStream. Using receive() and send() on the client would yield binary encrypted data.
4) No. The client passes a callback method to the SslStream creation in order to validate the certificate received by the server.
Example:
// assuming a TcpClient tcpClient was connected to the server somewhere above this code
SslStream sslStream = new SslStream(
tcpClient.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
sslStream.AuthenticateAsClient(serverName); // serverName: "domain.com" for example
then somewhere else in your code:
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None) {
return true;
}
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
// refuse connection
return false;
}

No client certificate was presented during SSL Handshake

I'd like to ask for explanation. I am using X.509 certificate, and when I tried to post my data to a webservice which I want to communicate with, I am getting the following error: "
No client certificate was presented during SSL Handshake
can you please explain me what is the issue?
NB: I am using .NET Framework 3.5 / C#
What I did exactly is: First I imported the certificate into the store, then I used the code below in order to find it and then recieve the token (using AskForToken function). However, when I send with my data, I got handshake failure.
My first question is why I succeed to get token (if I am not mistaken, the client (which is my application) sent the certificate to the server and got the token, which means the connection has been done well)?
My second question, what do I have to change or check to get rid of this handshake failure.
private static string RequestSecurityToken()
{
WSTrustChannelFactory trustChannelFactory = new Microsoft.IdentityModel.Protocols.WSTrust.WSTrustChannelFactory(
new CertificateWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(new Uri(stsAddress)));
trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
string thumb = "fe14593dd66b2406c5269d742d04b6e1ab03adb1";
trustChannelFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindByThumbprint, thumb);
trustChannelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
cert = trustChannelFactory.Credentials.ClientCertificate.Certificate;
var tokenString = AskForToken(serviceURL, trustChannelFactory);
trustChannelFactory.Close();
return tokenString;
}
SSL has a possibility to demand client authentification. So the Client (your application) has to send a certificate that the Server trusts before the connection is established. It seems that this client authentification fails, because your application doesn´t send such a certificate.
Depending on the webservice you try to use it won´t be possible to create such a certificate, because the server only trusts application from e.g. a certain company.
During an SSL handshake, the client presents it's public key to the other party, which apparently is not happening.
I'm not a C# programmer so I can't present you with the code. But you need to create an SSL keypair (private + public key pair) and use it to define your SSL sockets etc.

Getting "The remote certificate is invalid according to the validation procedure" when SMTP server has a valid certificate

This seems a common error but while I've found a work-around (see below) I can't pin down the reason I'm getting it in the first place.
I am writing SMTP functionality into our application and I'm attempting to add SSL functionality to the working SMTP we already have.
I am testing using our company's MS Exchange server and specifically the webmail option enabled on that. I can send emails internally through my code by not authenticating my connection and sending anonymously, however those emails won't relay to external email addresses due to our companies policy. Besides which I am programming this for our customers and they don't all allow open relay and/or anonymous connections.
I believe the Exchange server is using Explicit SSL/ TLS. I have tried telnet to the server's address on port 25 and got a text response, human readable response, which according to some of my searches previously means it's using Explicit SSL/ TLS.
I have the following test code
SmtpClient SMTPClient = new SmtpClient(webmailaddress);
SMTPClient.Port = 25;
SMTPClient.UseDefaultCredentials = true;
SMTPClient.EnableSsl = true;
System.Net.Mail.MailMessage Message = new `
System.Net.Mail.MailMessage(emailFrom,emailTo,subject,body);
SMTPClient.Send(Message);
During my searching for a solution I came across this "The remote certificate is invalid according to the validation procedure." using Gmail SMTP server
From which I got the following code...
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
public static bool ValidateServerCertificate(object sender,X509Certificate certificate,X509Chain chain,SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
else
{
if (System.Windows.Forms.MessageBox.Show("The server certificate is not valid.\nAccept?", "Certificate Validation", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
return true;
else
return false;
}
}
This works in my test code. HOWEVER the actual process I'm writing (rather than my test code) is going to run in the background and can't really ask the user (instead it reports errors in the windows error log).
As I started, my question is really why I'm getting this error at all. If I go to https:webmail.ourdomain.co.uk in a browser it shows a valid certificate and there is no option to install the certificate (as I would have done if it were a self-signed one).
However when I run my code, with a debug break poing in the ValidateServerCertificate method, I look at the certificate values and see an issuer of our local server and 'don't use before', and 'don't use after' properties of today. This does not match the certificate I am getting.
I've also checked what the sslPolicyErrors flags are in the debug of ValidateServerCertificate, and they are showing "RemoteCertificateChainErrors" and "RemoteCertificateNameMismatch".
So what am I missing about this... why is it not using the correct certificate? If there are steps I need to take to install the certificate locally for it to use then I need to know them so I can tell my customers what to do if they get this.
I don't want to just by-pass the check by returning true from the ValidateServerCertificate method, and because it's a background process I can't ask the user, so I need to understand how to get my code to use the correct/trusted certificate.
Hope someone can advise.
The answer I have finally found is that the SMTP service on the server is not using the same certificate as https.
The diagnostic steps I had read here make the assumption they use the same certificate and every time I've tried this in the past they have done and the diagnostic steps are exactly what I've done to solve the problem several times.
In this case those steps didn't work because the certificates in use were different, and the possibility of this is something I had never come across.
The solution is either to export the actual certificate from the server and then install it as a trusted certificate on my machine, or to get a different valid/trusted certificate for the SMTP service on the server. That is currently with our IT department who administer the servers to decide which they want to do.
Old post but as you said "why is it not using the correct certificate" I would like to offer an way to find out which SSL certificate is used for SMTP (see here) which required openssl:
openssl s_client -connect exchange01.int.contoso.com:25 -starttls smtp
This will outline the used SSL certificate for the SMTP service. Based on what you see here you can replace the wrong certificate (like you already did) with a correct one (or trust the certificate manually).
Old post, but I thought I would share my solution because there aren't many solutions out there for this issue.
If you're running an old Windows Server 2003 machine, you likely need to install a hotfix (KB938397).
This problem occurs because the Cryptography API 2 (CAPI2) in Windows
Server 2003 does not support the SHA2 family of hashing algorithms.
CAPI2 is the part of the Cryptography API that handles certificates.
https://support.microsoft.com/en-us/kb/938397
For whatever reason, Microsoft wants to email you this hotfix instead of allowing you to download directly. Here's a direct link to the hotfix from the email:
http://hotfixv4.microsoft.com/Windows Server 2003/sp3/Fix200653/3790/free/315159_ENU_x64_zip.exe

Categories