I'm trying to establish a TCP connection to a remote server using SslStream and TLS 1.2 protocol. The code is as follows:
_tcpClient.Connect(endPoint);
var certificate = new X509Certificate2(_settings.CertificateFilePath, _settings.CertificatePassword, X509KeyStorageFlags.MachineKeySet);
var certificates = new X509CertificateCollection { certificate };
_nStream = _tcpClient.GetStream();
_sslStream = new SslStream(_nStream, false,
(o, x509Certificate, chain, errors) => true,
(o, s, collection, x509Certificate, issuers) =>
{ return collection[0]; }
);
_sslStream.AuthenticateAsClient(_settings.HostIpAddress, certificates, SslProtocols.Tls12, true);
_sslStream.Write(someData, 0, someData.Length);
However, I'm getting an exception:
System.Security.Authentication.AuthenticationException: A call to SSPI
failed, see inner exception. --->
System.ComponentModel.Win32Exception: An unknown error occurred while
processing the certificate
--- End of inner exception stack trace
at System.Net.Security.SslState.CheckThrow(Boolean authSucessCheck)
at System.Net.Security.SslStream.Write(Byte[] buffer, Int32 offset, Int32 count)
I enabled SChannel logging and found this in Windows event log:
The remote server has requested SSL client authentication, but no
suitable client certificate could be found. An anonymous connection
will be attempted.
Then I enabled System.Net loggiing as described here and got this log (I removed some certificate data from it). It looks like the client certificate is OK but for some reason the log says Remote certificate: null although there is clearly some data sent back from the remote server that looks very much like a certificate. And at the very end the log says returned code=CertUnknown. I've no idea where the problem might be (remote server certificate? my code? remote/local server settings?) and would appreciate any help.
Note:
If I change my code to use SSL 3 by specifying SslProtocols.Ssl3 instead of SslProtocols.Tls12 everything works fine. But I really need to use TLS because that's what the remote server owner asks to do.
We made our websockets server to use TLS 1.2.
Added manually "ws.SslConfiguration.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12;" solved the issue.
Related
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.
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.
This is how i use it my local machine. I use this pfx cert to fetch data from external API.
services.AddHttpClient("test", c =>{}).ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
SslProtocols = SslProtocols.Tls12
}
handler.ClientCertificates.Add(newX509Certificate2("pathtopfxcert", "pathtokey"));
return handler;
}
But this piece of code throws an error as below when running inside RHEL Container
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HM47J1331JFT", Request id "0HM47J1331JFT:00000001": An unhandled exception was thrown by the application.
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
How to fix this issue? Should i change the code ?
When used inside a rhel image I needed to set
ENV WEBSITE_PRIVATE_CERTS_PATH="pathtopfxcert"
in the dockerfile and copy .pfx cert to that same path using
COPY localpfxpath.px pathtopfxcert
I have created a C# Web Server and I have some problems with the SSL/TLS certificates.
For the setup I have changed my /etc/hosts file for that any request to example.com will direct
to localhost. After that I have created a TCP server which works quite as expected. For the
HTTPS I added a System.Net.Security.SslStream layer with a default self-signed certificate which
also works as expected.
But for now I want to send a certificate that depends on the location the user requests. My server
code is like this:
var stream = new SslStream(
innerStream: client.GetStream(),
leaveInnerStreamOpen: false,
userCertificateValidationCallback: null,
userCertificateSelectionCallback: LocalCertificateSelection
);
stream.AuthenticateAsServer(
// I am forced to provide a default certificate. null will throw an error
serverCertificate: Settings.Certificate,
clientCertificateRequired: false,
enabledSslProtocols: SslProtocols.None,
checkCertificateRevocation: true
);
private X509Certificate LocalCertificateSelection(
object sender,
string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate,
string[] acceptableIssuers
)
{
// at this point: targetHost == null && acceptableIssuers.Length == 0
return Settings.Certificate;
};
But if I open the page in Firefox or in curl the targetHost and acceptableIssuers from my
certificate selection callback are always empty.
How am I supposed to get the host name to provide the correct certificate?
Edit 1:
My library is targeted for .Net Standard 2.1. Maybe this will help.
Edit 2:
I have added the certificate to the key store of the server (as it was mentioned in a comment), but this wont resolve the problem:
using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(Settings.Certificate);
After that I have captured some packages with Wireshark and I have noticed the following:
After TCP connection is finished:
Client sends a TLS Hello request with a valid SNI extension (here is the host name I want to get)
Debugger break point in my code is called. The SslStream tries to get a valid certificate: targetHost and acceptableIssuers are empty.
Server sends the default certificate with possible invalid host name
I'm trying to call a JBoss service from a C# program and I'm getting an annoyingly vague error.
JbossService proxy = new JbossService();
proxy.Credentials = new NetworkCredential("ME", "thepwd");
proxy.Url = //https url snipped
proxy.CookieContainer = new CookieContainer();
proxy.PreAuthenticate = true;
Console.WriteLine("Calling service...");
queryResponse qr = proxy.query();
Console.WriteLine("Done.");
The exception and inner exception thrown are as follows:
exception : The underlying connection was closed: An unexpected error occurred on a send.
inner exception : Authentication failed because the remote party has closed the transport stream.
I'm not quite sure what this means, other than perhaps that JBoss likes me even less than I like it. I'm calling from the local machine so I don't think it's a networking issue. Has anyone seen this before?
This usually happens when your client cannot verify trust over https with the server (usually because the server certificate is self signed or if it is signed by a root authority not installed on your client machine.
Easy fix (although there are security consequences)....somewhere in your initialization code add the following:
System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { return true;};
Basically this replaces the application wide handling of server certificate validation and causes your application to accept any certificate. If you want to get finer grained, you can examine the certificate and put some logic in the method.
This works for anything based on System.Net, so it should work for Web Services and any thing based on WebRequest.
I haven't used JBOSS. This is how I troubleshoot similar problems, when using Microsoft technologies -- the same issues may be affecting your program:
Firewall settings or network issue (try connecting manually, to rule this out)
Self-service certificate issues:
Check the following certificate values:
Ensure the server's certificate issuer has a valid, matching issuing trusted root Certificate Authority (CA), on the same machine
The server certificate subject name matches the machine name exactly
The machine name the client is accessing matches that defined in the server certificate
An administrator account set (server) certificate thumbprint
Try recreating the SSL Certificate on both servers)
Try creating your own CA cert, add to trusted publishers, and then create an SSL sert based on that