C# - TLS / SSL Websockets using fleck - c#

Im trying to make a TLS/SSL websocket connection using Fleck lib.
https://github.com/statianzo/Fleck (0.9.8.25)
now i got the server startet .
but when a client connects i get the following message.
28-02-2014 19:16:15 [Info] Server started at wss://localhost:8081
28-02-2014 19:18:51 [Debug] Client connected from 127.0.0.1:62543
28-02-2014 19:18:51 [Debug] Authenticating Secure Connection
28-02-2014 19:18:52 [Debug] 0 bytes read. Closing.
anybody got an idea of what im doing wrong ?
Browser: Chrome, version : 33.0.1750.117
// sample code.
FleckLog.Level = LogLevel.Debug;
var allSockets = new List<IWebSocketConnection>();
var server = new WebSocketServer("wss://localhost:8081");
server.Certificate = new X509Certificate2(#"CRT.pfx", "Pwd");
server.Start(socket =>
{
socket.OnOpen = () =>
{
Console.WriteLine("Open!");
allSockets.Add(socket);
};
socket.OnClose = () =>
{
Console.WriteLine("Close!");
allSockets.Remove(socket);
};
socket.OnMessage = message =>
{
foreach (var user in allSockets.ToList())
{
if(user.ConnectionInfo.Id != socket.ConnectionInfo.Id){
user.Send(message);
}
}
};
});
var input = Console.ReadLine();
while (input != "exit")
{
foreach (var socket in allSockets.ToList())
{
socket.Send(input);
}
input = Console.ReadLine();
}

Is certificate signed by a browser trusted CA? If not, the web page you are opening with Chrome has to be under HTTPS so you can be prompted to accept the certificate, otherwise the connection will fail.
If even doing that does not work, please try with WebSocketListener and tell me which error you get if any.
Some issues I found debugging WSS:
Remember to change the port number to a one different to the one you used for not secure connections. Some browsers get confused if suddenly a port becomes secure or viceversa.
Remember to use the hostname indicated in the certificate to connect and not the IP.
If you are using a self-signed certificate, use it for HTTPS so you can see the dialog for accepting that certificate. When accessing via WSS:// there is not certificate acceptance dialog, it will just fail to connect.
Try with a self-signed certificate as well and see if it works.

Related

Kafka Producer in .Net - SSL Handshake Failed

This is the first time I am trying to connect to Kafka server using Asp.Net console app and I ran into a bunch of issues. I solved most but can't shake this one off.
I am testing the producer part and keep getting "SSL Handshake Failed" error. I followed some suggestions here, including setting SslEndpointIdentificationAlgorithm to blank.
This is my producer config section:
_producerConfig = new ProducerConfig
{
BootstrapServers = bootstrapServer,
EnableDeliveryReports = true,
ClientId = Dns.GetHostAddresses(),
//SecurityProtocol = SecurityProtocol.SaslSsl,
SecurityProtocol = SecurityProtocol.Ssl,
SslCaLocation = #"D:\Something\Certificate\myCert.pem",
SslKeyPassword = "blahblah",
SslEndpointIdentificationAlgorithm=SslEndpointIdentificationAlgorithm.None
};
...
public async Task StartSendingMessages(string topicName)
{
using (var producer = new ProducerBuilder<long, string>(_producerConfig)
.SetKeySerializer(Serializers.Int64)
.SetValueSerializer(Serializers.Utf8)
.SetLogHandler((_, message) => Console.WriteLine($"Facility: {message.Facility}-{message.Level} Message: {message.Message}"))
.SetErrorHandler((_, e) => Console.WriteLine($"Error: {e.Reason}. Is Fatal: {e.IsFatal}"))
.Build())
try
{
Console.WriteLine("\nProducer loop started...\n\n");
for (var character = 'A'; character <= 'C'; character++)
{
var message = $"Character #{character} sent at {DateTime.Now:yyyy-MM-dd_HH:mm:ss}";
var deliveryReport = await producer.ProduceAsync(topicName,
new Message<long, string>
{
Key = DateTime.UtcNow.Ticks,
Value = message
});
When I run the application, I get:
Facility: FAIL-Error Message: [thrd:ssl://1.2.3.4:9093/bootstrap]: ssl://1.2.3.4:9093/bootstrap: SSL handshake failed: ssl\statem\statem_clnt.c:1890:tls_post_process_server_certificate error:0A000086:SSL routines::certificate verify failed: broker certificate could not be verified, verify that ssl.ca.location is correctly configured or root CA certificates are installed (add broker's CA certificate to the Windows Root certificate store) (after 46ms in state SSL_HANDSHAKE)
Error: ssl://1.2.3.4:9093/bootstrap: SSL handshake failed: ssl\statem\statem_clnt.c:1890:tls_post_process_server_certificate error:0A000086:SSL routines::certificate verify failed: broker certificate could not be verified, verify that ssl.ca.location is correctly configured or root CA certificates are installed (add broker's CA certificate to the Windows Root certificate store) (after 46ms in state SSL_HANDSHAKE). Is Fatal: False
What am I doing wrong, or missing?

How to connect mqtt with websocket in c#?

I tried use MQTTnet to connect mqtt.
But seems not worked, it would show the error message:
Unable to connect the remote server, the request was aborted: Could not create SSL/TLS secure channel.
I also found error message on windows event:
A fatal alert was received from the remote endpoint. The TLS protocol defined fatal alert code is 40.
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Options;
public override void Run()
{
var option = new MqttClientOptionsBuilder()
.WithWebSocketServer("wss://mymqttserver:443")
.WithClientId(Guid.NewGuid().ToString())
.WithTls(new MqttClientOptionsBuilderTlsParameters()
{
AllowUntrustedCertificates = true,
UseTls = true,
SslProtocol = SslProtocols.Tls12,
CertificateValidationCallback = delegate { return true; },
})
.WithCleanSession()
.Build();
var mqtt = new MqttFactory().CreateMqttClient() as MqttClient;
mqtt.ConnectAsync(option).Wait();
string convertMsg = JsonConvert.SerializeObject("Mqtt Connect Successfully!!");
var appMsg = new MqttApplicationMessage();
appMsg.Payload = Encoding.UTF8.GetBytes(convertMsg);
appMsg.Topic = "myTopic";
appMsg.QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce;
appMsg.Retain = false;
mqttClient.PublishAsync(appMsg).Wait();
}
I also tried connect my mqtt server with third party application.
It can connect successfully, so my mqtt server should be okay.
But I don't know why I can't use c# to connect.
In the line
.WithWebSocketServer("wss://mymqttserver:443")
You must remove the "wss://" because that is already being specified using the method ".WithWebSocketServer". So, you would have
.WithWebSocketServer("mymqttserver:443")
Just use the server and the port.

how to send email using MailKit in Asp.Net Core

Am not sure whether the old method of sending Mail using Mailkit is quite working with this code below
try
{
var emailMessage = new MimeMessage();
emailMessage.From.Add(new MailboxAddress(_emailConfig.SenderName, _emailConfig.SenderAddress));
emailMessage.To.Add(new MailboxAddress(email));
emailMessage.Subject = subject;
var builder = new BodyBuilder
{
HtmlBody = message
};
emailMessage.Body = builder.ToMessageBody();
using var smtp = new SmtpClient
{
ServerCertificateValidationCallback = (s, c, h, e) => true
};
smtp.AuthenticationMechanisms.Remove("XOAUTH2");
await smtp.ConnectAsync(_emailConfig.SmtpServer, Convert.ToInt32(_emailConfig.Port), false).ConfigureAwait(false);
await smtp.AuthenticateAsync(_emailConfig.Username, _emailConfig.Password).ConfigureAwait(false);
await smtp.SendAsync(emailMessage).ConfigureAwait(false);
await smtp.DisconnectAsync(true).ConfigureAwait(false);
}
catch (Exception ex)
{
throw new InvalidOperationException(ex.Message);
}
but am having exceptions if i use the code above to send email
nvalidOperationException: 534: 5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbv
5.7.14 26mzQKtlwfyEdGzHHdpi3ewWG6skAWgOBbdNNYmwzr9Sg3fGu-KixLAfODpJsVafutidE
5.7.14 8xBOp_8rNCvk9Y6iEcOkDlcZ1d-483zQ1Krw04NvqxQdq3w4iTtC8E9bL8uGprgV>
5.7.14 Please log in via your web browser and then try again.
5.7.14 Learn more at
5.7.14 https://support.google.com/mail/answer/78754 o5sm2555896wmh.8 - gsmtp
so when i changed this line of code below to use SSLS, A new error came out
await smtp.ConnectAsync(_emailConfig.SmtpServer, Convert.ToInt32(_emailConfig.Port), true).ConfigureAwait(false);
Exception returned
InvalidOperationException: 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.
Another possibility is that you are trying to connect to a port which does not support SSL/TLS.
It is also possible that the set of SSL/TLS protocols supported by the client and server do not match.
See https://github.com/jstedfast/MailKit/blob/master/FAQ.md#SslHandshakeException for possible solutions.
have searched everywhere on how to do it,even turned on my less secured app. some recommended sendGrid, i created a free account with them also,but i dont have access to the account created. Does anyone knows how to fix the code above using Mailkit
Try it like this.
using (var smtpClient = new SmtpClient())
{
smtpClient.ServerCertificateValidationCallback = (s, c, h, e) => true;
await smtpClient.ConnectAsync("host", port, false);
if (smtpClient.Capabilities.HasFlag(SmtpCapabilities.Authentication))
{
smtpClient.AuthenticationMechanisms.Remove("XOAUTH2");
await smtpClient.AuthenticateAsync(username, password);
}
await smtpClient.SendAsync(mailMsg);
smtpClient.Disconnect(true);
}

Client Certificate Authentication failed in WebRequest\HttpClient C#

Our customer met a fail when they were authenticated via Client Certificate Authentication.
The use IISCrypto completed their Tls1.2 configuration. The SSL protocols and TLS1.1 are marked disabled.
And they use IISCrypto completed their Cipher Suite configuration.
I checked Cipher Suite\ .net Framework version \ OS version \ Renegotiation of TLS \ Server Certificate Setting \ API Gateway Settings \ Windows Services.
But I cannot get any result.
Then I try to write some troubleshooting program to test the comunication and handshake sequence.
I got nothing.
Curl.exe and openssl s_client were ok when send message with Client certificate, but it always failed in C#, my code is like:
X509Certificate2 cert = new X509Certificate2(#"C:\Communicate.pfx", "xxxxx");
HttpClient client = null;
System.Net.Http.WebRequestHandler _internalHandler = new System.Net.Http.WebRequestHandler();
_internalHandler.UseProxy = false;
_internalHandler.ClientCertificates.Add(cert);
_internalHandler.ServerCertificateValidationCallback = ((obj, x509, chain, policyError) =>
{
return true;
});
client = new HttpClient(_internalHandler);
client.BaseAddress = new Uri(string.Format("https://{0}:{1}/", args[0], int.Parse(args[1])));
================================Update======================
I tried to write a TCP requester to test. It succeed. It seems the client certificate selection handler is different between HttpClient\WebRequest and SslStream.
X509Certificate2 cert = new X509Certificate2(#"C:\Communicate.pfx", "xxxxxxxx");
X509Certificate2Collection col = new X509Certificate2Collection();
col.Add(cert);
TcpClient client = new TcpClient(args[0], int.Parse(args[1]));
Stream stream = client.GetStream();
SslStream ssl = new SslStream(stream, false, new RemoteCertificateValidationCallback((a, b, c, d) =>
{
return true;
}),
new LocalCertificateSelectionCallback((sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers)=>
{
return cert;
}));
ssl.AuthenticateAsClient("1.1.1.1", col, System.Security.Authentication.SslProtocols.Tls12, false);
string x = "GET /api/TimebasedProxy/ HTTP/1.1\r\n";
x += "Host:1.1.1.1\r\n";
x += "Content-Type: application/json\r\n";
x += "Connection: Close\r\n\r\n";
byte[] xs = Encoding.UTF8.GetBytes(x);
ssl.Write(xs, 0, xs.Length);
================Update=============
I got the reason:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\SendTrustedIssuerList is set to 1 at the server side.
(https://learn.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings#sendtrustedissuerlist)
But customer is running their server under Windows Server 2012R2, who set SendTrustedIssuerList to 1?
==========================Update=============
IISCrypto did it.
What ever you did with IISCrypto, IISCrypto always sets SendTrustedIssuerList to 1.
It is a bug.
I got the reason:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\SendTrustedIssuerList is set to 1 at the server side.
(https://learn.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings#sendtrustedissuerlist)
But customer is running their server under Windows Server 2012R2, who set SendTrustedIssuerList to 1?
IISCrypto did it.
What ever you did with IISCrypto, IISCrypto always sets SendTrustedIssuerList to 1.
It is a bug.

How to fix SSO connect with cefSharp offline browser to webserver hosted by self-signed PC?

TL;DR: I am grasping for straws here, anybody got a SSO with CefSharp working and can point me to what I am doing wrong? I try to connect to a SSL-SSO page through CefSharp but it wont work - neither does it in Chrome-Browser. With IE it just works. I added the to trusted sites (Proxy/Security), I tried to tried to whitelist-policy the URL for chrome in the registry and tried different CefSharp settings - nothing helped.
I am trying (to no avail) to connect to a SSO enabled page via CefSharp-Offline-browsing.
Browsing with normal IE it just works:
I get 302 answer
the redirected site gives me a 401 (Unauthorized) with NTLM, Negotiate
IE automagically sends the NTLM Auth and receives a NTLM WWW-Authenticate
after some more 302 it ends in 200 and a logged in state on the website
Browsing with Chrome 69.0.3497.100 fails:
I guess this is probably due to the fact that the webserver is setup on a co-workers PC and uses a self-signed cert.
F12-Debugging in IE/Chrome:
In IE I see a 302, followed by two 401 answers, and end on the logged in site.
In chrome I see only 302 and 200 answers and end on the "fallback" login site for user/pw entry.
The main difference in (one of the 302) request headers is NEGOTIATE vs NTLM
// IE:
Authorization: NTLM TlRMT***==
// Chrome:
Authorization: Negotiate TlRMT***==
Upgrade-Insecure-Requests: 1
DNT: 1
No luck to connect through CefSharp so far, I simply land in its RequestHandler.GetAuthCredentials() - I do not want to pass any credentials with that.
What I tried to get it working inside Windows / Chrome:
installed the self-signed cert as "trusted certificate authorities"
added the co-workers host to the Windows Internet Proxy settings as trusted site
added the co-workers host to Software\Policies\Google\Chrome\ registry as
https://dev.chromium.org/administrators/policy-list-3#AuthServerWhitelist
https://dev.chromium.org/administrators/policy-list-3#AuthNegotiateDelegateWhitelist
which all in all did nothing: I still do not get any SSO using Chrome:
What I tried to get it working inside CefSharp:
deriving from CefSharp.Handler.DefaultRequestHandler, overriding
OnSelectClientCertificate -> never gets called
OnCertificateError -> no longer gets called
GetAuthCredentials -> gets called, but I do not want to pass login credentials this way - I already have a working solution for the http:// case when calling the sites normal login-page.
providing a settings object to Cef.Initialize(...) that contains
var settings = new CefSettings { IgnoreCertificateErrors = true, ... };
settings.CefCommandLineArgs.Add ("auth-server-whitelist", "*host-url*");
settings.CefCommandLineArgs.Add ("auth-delegate-whitelist", "*host-url*");
on creation of the browser providing a RequestContext:
var browser = new CefSharp.OffScreen.ChromiumWebBrowser (
"", requestContext: CreateNewRequestContext (webContext.Connection.Name));
CefSharp.RequestContext CreateNewRequestContext (string connName)
{
var subDirName = Helper.Files.FileHelper.MakeValidFileSystemName (connName);
var contextSettings = new RequestContextSettings
{
PersistSessionCookies = false,
PersistUserPreferences = false,
CachePath = Path.Combine (Cef.GetGlobalRequestContext ().CachePath, subDirName),
IgnoreCertificateErrors = true,
};
// ...
return new CefSharp.RequestContext (contextSettings);
}
I am aware that part of those changes are redundant (f.e. 3 ways to set whitelists of which at least 2 should work for CefSharp, not sure about the registry one affecting it) and in case of IgnoreCertificateErrors dangerous and can't stay in. I just want it to work somehow to then trim back what to do to make it work in production.
Research:
https://learn.microsoft.com/en-us/windows/desktop/SecAuthN/microsoft-ntlm
https://www.chromium.org/developers/design-documents/http-authentication
https://www.magpcss.org/ceforum/viewtopic.php?f=6&t=11085 leading to
https://bitbucket.org/chromiumembedded/cef/issues/1150/ntlm-authentication-issue (fixed 2y ago)
https://sysadminspot.com/windows/google-chrome-and-ntlm-auto-logon-using-windows-authentication/
https://productforums.google.com/forum/#!msg/chrome/1594XUaOVKY/8ChGCBrwYUYJ
and others .. still none the wiser.
Question: I am grasping for straws here , anybody got a SSO with CefSharp working and can point me to what I am doing wrong?
TL;DR: I faced (at least) 2 problems: invalid SSL certificates and Kerberos token problems. My test setup has local computers set up with a web-server I call into. These local computers are mostly windows client OS VMs with self-signed certificates. Some are windows servers. The latter worked, the fromer not. With IE both worked.
Browsing to the site in question using https://... lead to CEFsharp encountering the self-signed certificate (which is not part of a trusted chain of certs) - therefore it will call the browsers RequestHandler (if set) and call into its
public override bool OnCertificateError (IWebBrowser browserControl, IBrowser browser,
CefErrorCode errorCode, string requestUrl,
ISslInfo sslInfo, IRequestCallback callback)
{
Log.Logger.Warn (sslInfo.CertStatus.ToString ());
Log.Logger.Warn (sslInfo.X509Certificate.Issuer);
if (CertIsTrustedEvenIfInvalid (sslInfo.X509Certificate))
{
Log.Logger.Warn ("Trusting: " + sslInfo.X509Certificate.Issuer);
if (!callback.IsDisposed)
using (callback)
{
callback?.Continue (true);
}
return true;
}
else
{
return base.OnCertificateError (browserControl, browser, errorCode, requestUrl,
sslInfo, callback);
}
}
For testing purposes I hardcoded certain tests into CertIsTrustedEvenIfInvalid (sslInfo.X509Certificate) that would return true for my test environment - this might be replaced by a simple return false, an UI-Popup presenting the cert and asking the user if she wants to proceed or it might take certain user-provided cert-files into account - dunno yet:
bool CertIsTrustedEvenIfInvalid (X509Certificate certificate)
{
var debug = new Dictionary<string, HashSet<string>> (StringComparer.OrdinalIgnoreCase)
{
["cn"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "some", "data" },
["ou"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "other", "stuff" },
["o"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "..." },
["l"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Atlantis" },
["s"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Outer Space" },
["c"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "whatsnot" },
};
var x509issuer = certificate.Issuer
.Split (",".ToCharArray ())
.Select (part => part.Trim().Split("=".ToCharArray(), 2).Select (p => p.Trim()))
.ToDictionary (t => t.First (), t => t.Last ());
return x509issuer.All (kvp => debug.ContainsKey (kvp.Key) &&
debug[kvp.Key].Contains (kvp.Value));
}
Only if the SSL-Step works, SSO will be tried.
After solving the SSL issue at hand I ran into different behavious of Chrome versus IE/Firefox etc as described here # Choosing an authentication scheme
- the gist of it is:
if multiple auth schemes are reported by the server, IE/Firefox use the first one they know - as delivered by the server (preference by order)
Chrome uses the one which it deems of highest priority (in order: Negotiate -> NTLM -> Digest->Basic) ignoring the servers ordering of alternate schemes.
My servers reported NTLM,Negotiante (that order) - with IE it simply worked.
With Chrome this led to Kerberos tokens being exchanged - which only worked when the web-server was hosted on a Windows Server OS - not for Windows Client OS. Probably some kind of failed configuration for Client-OS computers in the AD used. Not sure though - but against Server OS it works.
Additionaly I implemented the
public override bool GetAuthCredentials (IWebBrowser browserControl, IBrowser browser,
IFrame frame, bool isProxy, string host,
int port, string realm, string scheme,
IAuthCallback callback)
{
// pseudo code - asks for user & pw
(string UserName, string Password) = UIHelper.UIOperation (() =>
{
// UI to ask for user && password:
// return (user,pw) if input ok else return (null,null)
});
if (UserName.IsSet () && Password.IsSet ())
{
if (!callback.IsDisposed)
{
using (callback)
{
callback?.Continue (UserName, Password);
}
return true;
}
}
return base.GetAuthCredentials (browserControl, browser, frame, isProxy,
host, port, realm, scheme, callback);
}
to allow for a fail-back if the SSO did not work out. After providing the AD credentials in this dialog login is possible as well).
For good measure I also whitelisted the hosts to the CEF-Browser context on creation of a new broswer like so:
CefSharp.RequestContext CreateNewRequestContext (string subDirName, string host,
WebConnectionType conType)
{
var contextSettings = new RequestContextSettings
{
PersistSessionCookies = false,
PersistUserPreferences = false,
CachePath = Path.Combine (Cef.GetGlobalRequestContext ().CachePath, subDirName),
};
var context = new CefSharp.RequestContext (contextSettings);
if (conType == WebConnectionType.Negotiate) # just an enum for UserPW + Negotiate
Cef.UIThreadTaskFactory.StartNew (() =>
{
// see https://cs.chromium.org/chromium/src/chrome/common/pref_names.cc for names
var settings = new Dictionary<string, string>
{
["auth.server_whitelist"] = $"*{host}*",
["auth.negotiate_delegate_whitelist"] = $"*{host}*",
// only set-able via policies/registry :/
// ["auth.schemes"] = "ntlm" // "basic", "digest", "ntlm", "negotiate"
};
// set the settings - we *trust* the host with this and allow negotiation
foreach (var s in settings)
if (!context.SetPreference (s.Key, s.Value, out var error))
Log.Logger.Debug?.Log ($"Error setting '{s.Key}': {error}");
});
return context;
}

Categories