SslStream won't send client certificate - c#

I have googled a lot regarding this issue but nowhere have I found an answer that works.
I am using Nuget package QuickFix to connect to Bloomberg servers. With a network analyzer I can see the TLS handshaking process but when the server requests a certificate the client sends none resulting in exceptions "Call to SSPI failed." and "The message received was unexpected or badly formatted".
My QuickFix config contains these SSL-related settings:
SocketConnectHost=xxx.xxx.xxx.xxx
SocketConnectPort=yyy
SSLEnable=Y
SSLCertificate=name of certificate
SSLProtocols=Tls12
When executing below code all parameters to AuthenticateAsClient have the proper values including a certificate downloaded from Bloomberg and installed to my Windows certificate storage. And yes, Bloomberg servers support TLS 1.2.
public Stream CreateClientStreamAndAuthenticate(Stream innerStream)
{
SslStream sslStream = new SslStream(innerStream, false, ValidateServerCertificate, SelectLocalCertificate);
try
{
// Setup secure SSL Communication
X509CertificateCollection clientCertificates = GetClientCertificates();
sslStream.AuthenticateAsClient(socketSettings_.ServerCommonName,
clientCertificates,
socketSettings_.SslProtocol,
socketSettings_.CheckCertificateRevocation);
}
catch (System.Security.Authentication.AuthenticationException ex)
{
log_.OnEvent("Unable to perform authentication against server: " + ex.Message);
}
return sslStream;
}
Why is my certificate never sent? Would be grateful for any assistance.

Related

In C# what is the point of using SslStream.AuthenticateAsServer() on the server side?

I am trying to listen for a secured TCP connection on a port. I was going through the server code example on microsoft docs. I am pasting here for quick reference:
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, checkCertificateRevocation: true);
// Display the properties and settings for the authenticated stream.
DisplaySecurityLevel(sslStream);
DisplaySecurityServices(sslStream);
DisplayCertificateInformation(sslStream);
DisplayStreamProperties(sslStream);
My doubt is why server is authenticating itself? Or am I missing something here.
Perhaps you're missing the "As". That call tells the SslStream that it should prepare for the TLS handshake (where authentication happens), and that it is taking the server role in the handshake.
So it's really "start the authentication phase, as the server".

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.

SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection

I'm trying to access gmail emails using imap and the code is failing at the ssl handshake without showing me any errors. Really appreciate if anyone could please help with this. I've built this using xunit, .NET Core 2.1. I'm using MailKit Nuget
public GMailHandler(string mailServer, int port, bool ssl, string login, string password)
//mailServer = imap.gmail.com
//port = 993
//ssl = true
{
if (ssl)
Client.Connect(mailServer, port);
else
Client.Connect(mailServer, port);
Client.Authenticate(login, password);
Client.Inbox.Open(FolderAccess.ReadOnly);
}
Here's a copy & paste from the MailKit FAQ:
Q: Why do I get "MailKit.Security.SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection." when I try to Connect?
When you get an exception with that error message, it usually means that you are encountering
one of the following scenarios:
1. The mail server does not support SSL on the specified port.
There are 2 different ways to use SSL/TLS encryption with mail servers.
The first way is to enable SSL/TLS encryption immediately upon connecting to the
SMTP, POP3 or IMAP server. This method requires an "SSL port" because the standard
port defined for the protocol is meant for plain-text communication.
The second way is via a STARTTLS command (aka STLS for POP3) that is optionally
supported by the server.
Below is a table of the protocols supported by MailKit and the standard plain-text ports
(which either do not support any SSL/TLS encryption at all or only via the STARTTLS
command extension) and the SSL ports which require SSL/TLS encryption immediately upon a
successful connection to the remote host.
|Protocol|Standard Port|SSL Port|
|:------:|:-----------:|:------:|
| SMTP | 25 or 587 | 465 |
| POP3 | 110 | 995 |
| IMAP | 143 | 993 |
It is important to use the correct SecureSocketOptions for the port that you are connecting to.
If you are connecting to one of the standard ports above, you will need to use SecureSocketOptions.None,
SecureSocketOptions.StartTls or SecureSocketOptions.StartTlsWhenAvailable.
If you are connecting to one of the SSL ports, you will need to use SecureSocketOptions.SslOnConnect.
You could also try using SecureSocketOptions.Auto which works by choosing the appropriate option to use
by comparing the specified port to the ports in the above table.
2. The mail server that you are connecting to is using an expired (or otherwise untrusted) SSL certificate.
Often times, mail servers will use self-signed certificates instead of using a certificate that
has been signed by a trusted Certificate Authority. Another potential pitfall is when locally
installed anti-virus software replaces the certificate in order to scan web traffic for viruses.
When your system is unable to validate the mail server's certificate because it is not signed
by a known and trusted Certificate Authority, the above error will occur.
You can work around this problem by supplying a custom RemoteCertificateValidationCallback
and setting it on the client's ServerCertificateValidationCallback
property.
In the simplest example, you could do something like this (although I would strongly recommend against it in
production use):
using (var client = new SmtpClient ()) {
client.ServerCertificateValidationCallback = (s,c,h,e) => true;
client.Connect (hostName, port, SecureSocketOptions.Auto);
// ...
}
Most likely you'll want to instead compare the certificate's Thumbprint
property to a known value that you have verified at a prior date.
You could also use this callback to prompt the user (much like you have probably seen web browsers do)
as to whether or not the certificate should be trusted.
3. A Certificate Authority CRL server for one or more of the certificates in the chain is temporarily unavailable.
Most Certificate Authorities are probably pretty good at keeping their CRL and/or OCSP servers up 24/7, but occasionally
they do go down or are otherwise unreachable due to other network problems between you and the server. When this happens,
it becomes impossible to check the revocation status of one or more of the certificates in the chain.
To ignore revocation checks, you can set the
CheckCertificateRevocation
property of the IMAP, POP3 or SMTP client to false before you connect:
using (var client = new SmtpClient ()) {
client.CheckCertificateRevocation = false;
client.Connect (hostName, port, SecureSocketOptions.Auto);
// ...
}
4. The server does not support the same set of SSL/TLS protocols that the client is configured to use.
MailKit attempts to keep up with the latest security recommendations and so is continuously removing older SSL and TLS
protocols that are no longer considered secure from the default configuration. This often means that MailKit's SMTP,
POP3 and IMAP clients will fail to connect to servers that are still using older SSL and TLS protocols. Currently,
the SSL and TLS protocols that are not supported by default are: SSL v2.0, SSL v3.0, and TLS v1.0.
You can override MailKit's default set of supported
SSL and TLS protocols
by setting the value of the SslProtocols
property on your SMTP, POP3 or IMAP client.
For example:
using (var client = new SmtpClient ()) {
// Allow SSLv3.0 and all versions of TLS
client.SslProtocols = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
client.Connect ("smtp.gmail.com", 465, true);
// ...
}
await client.ConnectAsync(_emailConfig.SmtpServer, _emailConfig.Port, false);
just set the "useSsl" option to false in the client configuration
Disbaled Avast antivorus software .
This works for me
I solved a similar problem by going through the protocols. As a result, I found out that the MS Exchange server uses Tls 1.0 for backward compatibility. I explicitly set the protocol and the connection passed.
var client = new ImapClient();
client.SslProtocols = System.Security.Authentication.SslProtocols.Tls;
client.Connect("servername", 993, SecureSocketOptions.SslOnConnect);

"Call to SSPI failed" when connecting to OpenSSL Server

When calling AuthenticateAsClient(), I receive the error "Call to SSPI failed." with the inner exception "The message received was unexpected or badly formatted".
I've seen this to be a semi-popular problem, but I haven't been able to find a working solution. Here is where I'm at after scouring articles for a few hours:
Using my private key, the server issued me a signed client cert
Using open ssl, I combined the two into a pfx using:
openssl pkcs12 -in My-Client-Cert.pem -inkey ssl-client-privatekey.pem -export -out private-key-pair.pfx
I imported the cert to the personal folder on LocalMachine. I was able to see that cert recognized that a private key was present.
The private key is not password protected
I imported the CA Root for the service I'm talking to into both my Trusted Root Certification Authorities and Personal stores.
In app code, I pull the cert by thumbnail. I can see in the debugger that both private and public keys are present.
I initialize a TcpClient
I then initialize an SslStream using the the TcpClient
I call AuthenticateAsClient()
I receive the error message
For the hostname parameter of AuthenticateAsClient() I have also tried using the CN of the CA root and also the CN of the client cert with the same result.
I verified that the service I'm trying to talk to does work just fine when I connect to it using openssl s_client connect
Here is the code for the connection:
private void Setup(string hostname, int port, X509Certificate2Collection certs)
{
try
{
// create the socket
var clientSocket = new TcpClient(hostname, port);
_sslStream = new SslStream(
clientSocket.GetStream(),
false,
ValidateServerCertificate,
null);
_sslStream.AuthenticateAsClient(
"VirtuCrypt Root CA - Test", // hostname here must match the name on the server certificate
certs,
SslProtocols.Tls11,
false);
Debug.WriteLine("Connected!");
}
catch(Exception ex)
{
Debug.WriteLine(ex.Message);
if(ex.InnerException != null)
Debug.WriteLine(ex.InnerException.Message);
}
}
Pulling my hair out... any thoughts?
UPDATE:
I spun up Wireshark and noticed that all communication to and from the host was done over TCP, and none of it was done over TLS. Not sure if that's something I should be concerned with despite the fact that I am requesting TLS12
UPDATE2:
I'm looking at Wireshark again and I fixed the decode so that it shows the TLS communications in addition to the TCP connection. Now that I can see the handshake, I can see that no client cert is actually being presented. I'm providing that cert to SslStream, so I don't know why it's not being transmitted.
Ok after finally making friends with Wireshark and breaking about 4 coffee mugs chasing my tail, here's what I found out...
The CA root that was given to us was a pem file that actually included the CA root and 3 intermediate certs.
I had to break those into separate files and import the CA root into our trusted root CA store and the 3 intermediate certs into the intermediate CA store
I hadn't noticed before, but our client cert that was installed in the personal store was saying it didn't have enough info to be validated, but what was really going on is that it was failing validation because we didn't have the CA certs installed correctly as described above. Once I installed those separately, this message went away
It appears that .Net will not send your client cert unless it can be validated. In my 2nd edit in the OP, I mentioned that I could see the client cert was not being sent at all. This was the actual problem. Once I installed these CA certs, the cert started getting sent and everything else was good.

System.IO.IOException: Authentication failed because the remote party has closed the transport stream

I'm creating a webservice ( in .NET 3.5) to obtain SSL certificate information of a remote server based on the domain name and i have a client which is a console application to call the service. presently for me there are 11 domain names. i am able to obtain the SSL information for all server except one.
my code is:
using (TcpClient client = new TcpClient())
{
client.Connect(strDNSEntry, 443);
SslStream ssl = new SslStream(client.GetStream(), false);
try
{
ssl.AuthenticateAsClient(strDNSEntry); //Error thrown here
}
catch (AuthenticationException e)
{
log.Debug(e.Message);
ssl.Close();
client.Close();
return cert;
}
catch (Exception e)
{
log.Debug(e.Message);
ssl.Close();
client.Close();
return cert;
}
cert = new X509Certificate2(ssl.RemoteCertificate);
ssl.Close();
client.Close();
return cert;
}
the error is
"[System.IO.IOException] = {"Authentication failed because the remote
party has closed the transport stream."}".
When I examined during debugging at SSLStream I found the error as "
This operation is only allowed using a successfully authenticated
context".
Is that because I don't have enough credentials is it throwing the error or any issues with the service? Also I tried to do Remote desktop conncetion to that server, I'm unable to coonect.
Perhaps you can use "Windows Communication Foundation" WCF framework, and create a Service Class, then configure Client/Server TCP bindings parameters correctly with SSL transport configuration.
If you try to do this, I will give you a test case later.

Categories