Issue running SSL-enabled TCP server under mono - c#

I'm developing an application which requires SSL encryption between the client and server, and needs to run under Windows and in Ubuntu under Mono. At the moment, the server and client pair works fine in Windows, but when the client connects to the server, the server returns this error:
Error: The authentication or decryption has failed. at
Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback
(IAsyncResult asyncResult) [0x00000] in :0
I'm loading the certificate like this:
X509Certificate2 cert = new X509Certificate2("server.pfx", "<password>");

Current Mono versions do not support SSL connections as server without client certificates.
Either use a client certificate or apply a patch to the source code.
You can find an example of how to do that in my answer to this question.

Related

Remote certificate rejected when connecting to SQL Server from Linux host

I have been provided a certificate for a SQL Server, which has been successfully installed and activated. I can confirm this from the logs:
The certificate [Cert Hash(sha1) "xxxxxxxxxxxxxxxxxxE5C050F7D75F58E4E2F"] was successfully loaded for encryption.
Connecting to the database using SSMS is successful, simply by encrypting the connection without trusing the server certificate.
I wanted to replicate this using WSL and later on - docker.
I am testing this with a simple .net 6 console application:
var con = new SqlConnection("Server=domain.host.eu,50730;Database=databasename;User Id=user;Password='password';");
await con.OpenAsync();
var version = con.ExecuteScalar<string>("SELECT ##VERSION");
Console.WriteLine(version);
This works, if I add Trust Server Certificate=True; to the connection string. Without it, the connection fails:
Unhandled exception. Microsoft.Data.SqlClient.SqlException (0x80131904): A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: TCP Provider, error: 35 - An internal exception was caught)
---> System.Security.Authentication.AuthenticationException: The remote certificate was rejected by the provided RemoteCertificateValidationCallback.
at System.Net.Security.SslStream.SendAuthResetSignal(ProtocolToken message, ExceptionDispatchInfo exception)
at System.Net.Security.SslStream.CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions)
at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
I wanted to extract the certificate from the pfx:
openssl pkcs12 -in host.domain.eu.pem.pfx -clcerts -nokeys -out host.domain.eu.crt
sudo cp host.domain.eu.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
Unfortunately, this fails with the same error messages and I don't know where I went wrong. I can only assume, that my handling of the certs on Linux is wrong.
If you're connecting to something unimportant, I was experiencing this error connecting from my dotnetcore API, to a microsoft sql server contained in a docker container (all locally) for dev work. My solution was putting encrypt=False at the end of my connection string like so (within appsettings.json):
"DefaultConnection": "server=localhost;database=newcomparer;trusted_connection=false;User Id=sa;Password=reallyStrongPwd123;Persist Security Info=False;Encrypt=False"
I double blindsided myself and got distracted by 2 mishaps at the same time.
After importing a certificate into the SQL Configuration Manager, you can choose from 2 nigh on identical entries. The difference beeing one having a friendly name of host.domain (FQDN), while the other has no friendly name.
Naturally I have not double checked and just stayed with the one without the FQDN as the friendly name, which was pre-selected after the import and looked valid.
The second mistake was to not realize, that WSL was not part of our domain and could not resolve the database name. So I used to the FQDN to reach it. This caused the mismatch between host, which the database instance expected and host.domain, which the client from inside WSL used.
After this I borrowed an Ubuntu vm inside our domain and verified, that using only the host,port schema in the connection string works.
And to make it explicit and work inside wsl I switched the certificates around to require the FQDN as the friendly namae while connecting, so connections can be made from inside the domain, as well as "outside" (from wsl).
tl;dr: One possible explanation for the exception The remote certificate was rejected by the provided RemoteCertificateValidationCallback. is, that the instance name in the connection string and the expected name from the instance do not match.

Azure Cognitive Speech TTS API does not work on Windows 8, 8.1, Server 2012, Server 2012R2 since 2022-01

I found an issue of Azure Cognitive Speech TTS Service;
Azure Cognitive Speech TTS API does not work on Windows 8,/8.1/Server 2012/Server2012R2 since 2022-01.
I made a program with Microsoft Cognitive Services Speech SDK and it worked well on Windows 8/8.1/Server2012/Server2012R2 as well as Windows 10/Server 2019.
I confirmed that there were logs that it has worked correctly until 2021-11-30 at least.
However, it does not work today.
So, I made a simple sample with Microsoft.CognitiveServices.Speech.csharp nuget package.
My sample code is as the following;
public static async Task SynthesisToAudioFileAsync(string text, string outputFileName)
{
var config = SpeechConfig.FromSubscription("xxxxx", "westus2");
config.SpeechSynthesisLanguage = "ko-KR";
config.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Audio24Khz48KBitRateMonoMp3);
config.SpeechSynthesisVoiceName = "ko-KR-SunHiNeural";
using (var fileOutput = AudioConfig.FromWavFileOutput(outputFileName))
{
using (var synthesizer = new SpeechSynthesizer(config, fileOutput))
{
var result = await synthesizer.SpeakTextAsync(text);
if (result.Reason == ResultReason.SynthesizingAudioCompleted)
{
Console.WriteLine($"Speech synthesized to [{outputFileName}]");
}
else if (result.Reason == ResultReason.Canceled)
{
var cancellation = SpeechSynthesisCancellationDetails.FromResult(result);
Console.WriteLine($"CANCELED: Reason={cancellation.Reason}");
if (cancellation.Reason == CancellationReason.Error)
{
Console.WriteLine($"CANCELED: ErrorCode={cancellation.ErrorCode}");
Console.WriteLine($"CANCELED: ErrorDetails=[{cancellation.ErrorDetails}]");
Console.WriteLine($"CANCELED: Did you update the subscription info?");
}
}
else
{
Console.Write(result.Reason.ToString());
}
}
}
}
It works well on Windows 10/Server2019 with no error.
However, it does not work on Windows 8,/8.1/Server 2012/Server2012R2.
Of course, I executed windows update fully.
The error message is as the following;
CANCELED: Reason=Error
CANCELED: ErrorCode=ConnectionFailure
CANCELED: ErrorDetails=[Connection failed (no connection to the remote host). In
ternal error: 11. Error details: Code: 0. USP state: 2. Received audio size: 0 b
ytes.]
CANCELED: Did you update the subscription info?
I suspect that it may be TLS 1.2 issue and tried as the followings;
I changed Microsoft.CognitiveServices.Speech.csharp nuget package to several versions - 1.14, 1.13 and 1.19(latest version) but it did not work.
I tried to enable TLS 1.2 on Windows 8.1/Server2012 according to many google search results;
https://github.com/MicrosoftDocs/memdocs/blob/main/memdocs/configmgr/core/plan-design/security/includes/update-net-framework-to-support-tls-1-2.md
https://hide.me/en/knowledgebase/how-to-enable-tls-1-1-tls-1-2-in-windows-7-and-8/
https://learn.microsoft.com/en-us/mem/configmgr/core/plan-design/security/enable-tls-1-2-client
and other many postings.
But, it did not work.
I downloaded Cognitive-Speech-TTS sample (Cognitive-Speech-TTS-master.zip) and run C# sample in Old folder.
https://github.com/Azure-Samples/Cognitive-Speech-TTS
It does not use Azure SDK and implements https rest API in low level. So I can review and change codes.
It also worked on Windows 10 but did not work on Windows 81./Server2012 as the following error message;
Starting TTSSample request code execution.
Unhandled Exception: System.AggregateException: One or more errors occurred. --->
System.Net.Http.HttpRequestException: An error occurred while sending the request. --->
System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel.
at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context)
at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)
I confirmed that token API (https://westus2.api.cognitive.microsoft.com/sts/v1.0/issueToken) was called well but speak API (https://westus2.tts.speech.microsoft.com/cognitiveservices/v1) was not called with error message.
According to error message, it seems to TLS 1.2 issue.
So, I added to the following line;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;
The error message became changed as the following on Windows 10 as well as Windows 8.1/Server2012
Starting Authtentication
Unhandled Exception: System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. --->
System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host --->
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
It was expected behavior because I forced TLS 1.1.
However, the error occurred in authentication step, contrary to above result - above test passed authentication.
So, TLS 1.2 may be enabled on my Windows 81./server2012 because token Uri may require TLS 1.2 according to above testing result.
I tired with speech recognition C# sample in cognitive-services-speech-sdk-master.
https://github.com/Azure-Samples/cognitive-services-speech-sdk
It worked well Windows 81./Server2012 as well as Windows 10.
MS doc said that Azure Cognitive Service requires TLS 1.2.
If TLS 1.2 is not available on my Windows 8.1/Server2012, all Azure Cognitive Service API must fail.
However, only TTS API fails.
Beside, it has worked until 2021-11 at least.
I've tried almost everything I could, but failed.
Finally I suspect that there may be some changes in Azure Cognitive TTS system at the end of 2021 and it may make the issue related to TLS connection from Windows 8/8.1/Server2012/Server2012R2.
In fact, the issue was reported on my customer's machines and upgrading Windows OS to Windows10/Server2019 is not an option because of many reasons.
So, I have to find a workaround on Windows 8/8.1/Server2012/Server2012R2.
Could you please let me know how I can solve the issue?
Best regards.
P.S.
I captured network packets of Cognitive-Speech-TTS C# sample using WireShark and confirmed that TLS 1.2 was used.
First handshaking to retrieve token(westus2.api.cognitive.microsoft.com - 20.51.8.244) succeeded but second handshaking to Azure Cognitive TTS Service(westus2.tts.speech.microsoft.com - 20.51.12.193) failed with the following message;
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Handshake Failure).
I tested on Windows 10 and all TLS 1.2 handshaking succeeded with same test console program.
I added the following code to ignore certificate validation but it did not work;
ServicePointManager.ServerCertificateValidationCallback += (object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors) =>
{
return true;
};
I found actual reason.
It was TLS cipher suite issue of Azure TTS Service API Server.
I executed sslscan to westus2.tts.speech.microsoft.com and the result was as the following;
westus2.tts.speech.microsoft.com
Preferred TLSv1.2 128 bits ECDHE-RSA-AES128-GCM-SHA256 Curve 25519 DHE 253
Accepted TLSv1.2 256 bits ECDHE-RSA-AES256-GCM-SHA384 Curve 25519 DHE 253
Accepted TLSv1.2 256 bits ECDHE-RSA-CHACHA20-POLY1305 Curve 25519 DHE 253
I compared packet capturing result of WireShark between Windows 8.1 and Windows 10.
The result of Windows 10 has ECDHE-RSA-AES128-GCM-SHA256 and ECDHE-RSA-AES256-GCM-SHA384 but the result of Windows 8.1 does not.
So, Azure TTS API cannot work on Windows8/8.1/Server2012/Server2012R2 and it must be fixed by MS.

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.

SSL handshake fails in Xamarin

I'm trying to access my RESTful API to retrieve data from a MySQL database. Everything is set up and works perfectly on my C# WPF project. But when using the exact same code in Xamarin Forms (built for Android) I cannot get a successful SSL handshake with my server.
Server details
Let's Encrypt SSL certificate (definitely valid)
Apache BasicAuth (.htaccess)
HTTPS only (Rewrite HTTP on), so port 443
REST API: php-crud-api (by mevdschee) to access MariaDB 10.3
I'm using Flurl.Http (uses HttpClient) to establish the connection, but get an exception on jsonReader.Wait():
var jsonReader = "https://example.com/my_api/api.php/records/my_table?order=id,desc&size=10"
.WithHeader("Accept", "application/json")
.WithBasicAuth("username", "password")
.GetJsonAsync<JsonRootObject>();
// Wait for completion.
jsonReader.Wait();
This is my AggregateException:
System.AggregateException: One or more errors occurred. (Call failed. An error occurred while sending the request
GET https://example.com/my_api/api.php/records/my_table?order=id,desc&size=10) ---> Flurl.Http.FlurlHttpException: Call failed. An error occurred while sending the request
GET https://example.com/my_api/api.php/records/my_table?order=id,desc&size=10 ---> System.Net.Http.HttpRequestException: An error occurred while sending the request
---> System.Net.WebException: Error: TrustFailure (Authentication failed, see inner exception.) ---> System.Security.Authentication.AuthenticationException:
Authentication failed, see inner exception. ---> Mono.Btls.MonoBtlsException: Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED
at /Users/builder/jenkins/workspace/xamarin-android-d16-1/xamarin-android/external/mono/external/boringssl/ssl/handshake_client.c:1132
at Mono.Btls.MonoBtlsContext.ProcessHandshake () [0x00038] in <74989b9daab94528ac2c4b7da235b9c5>:0
at Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake (Mono.Net.Security.AsyncOperationStatus status, System.Boolean renegotiate) [0x000a1] in <74989b9daab94528ac2c4b7da235b9c5>:0
at (wrapper remoting-invoke-with-check) Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake(Mono.Net.Security.AsyncOperationStatus,bool)
at Mono.Net.Security.AsyncHandshakeRequest.Run (Mono.Net.Security.AsyncOperationStatus status) [0x00006] in <74989b9daab94528ac2c4b7da235b9c5>:0
at Mono.Net.Security.AsyncProtocolRequest.ProcessOperation (System.Threading.CancellationToken cancellationToken) [0x000ff] in <74989b9daab94528ac2c4b7da235b9c5>:0
at Mono.Net.Security.AsyncProtocolRequest.StartOperation (System.Threading.CancellationToken cancellationToken) [0x0008b] in <74989b9daab94528ac2c4b7da235b9c5>:0
What I've tried / what I know
Code works perfectly in WPF
Exact same code (copy-paste) does not work in Xamarin Forms (tested on Android Pie)
does not work in emulator nor dedicated device
accessing the REST API via browser works and delivers results as expected (tested on my PC, the emulator in Chrome and my dedicated Android)
changing the HttpClient implementation or SSL/TLS implementation under Project properties -> Android Options -> Advanced does not help
Why is my SSL handshake failing? What is Xamarin doing differently than WPF?
It turned out to be an Apache configuration error. I checked my domain and noticed an error. This was fixed by adding the certificate's chain-file in my Apache Directives:
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
The last line was the missing one. Weird that browsers and WPF trusted the certificate even without the chain-file. Anyway, now it also works on Android.
In the meantime I've been using this piece of code to disable the SSL validation in my app:
// This disables the SSL certificate validity check.
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
That code is unsafe and should only be used for testing.
your Flurl.Http can fail due to the usage of 2 different project types (.net standard vs class library - I had exactly the same problem with http client I wrote)
try a different http client for .net standard.
in any case, you can write your own http client and debug - that is my recommendation.
if you still stuck - write back I'll help.

Calling HTTPS JBoss service from C# code throws an authentication error

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

Categories