SSL handshake fails in Xamarin - c#

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.

Related

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.

How to call self-signed gRPC service from Xamarin.Forms (iOS and Android)

I'm trying to call a gRPC service built using ASP.NET Core on .NET 5 from a Xamarin.Forms application.
I'm using Grpc.AspNetCore 2.35.0 on the server side and Grpc.Net.Client 2.35.0 within the app library (.NETStandard 2.1).
When trying to call a service from the client, I get an exception:
Exception caught: MyException: An unknown error happened
---> Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed, see inner exception. TlsException: CertificateUnknown", DebugException="System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
---> Mono.Security.Interface.TlsException: CertificateUnknown
at Mono.AppleTls.AppleTlsContext.EvaluateTrust () [0x000bf] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/System/Mono.AppleTls/AppleTlsContext.cs:306
[… SNIP …]
I assume this has to do with the self signed certificate. I'm using the generated dev certificate created using dotnet dev-certs https tool.
I tried to add a custom handler to the created channel as suggested here:
GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
HttpHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
}
});
And also the other suggestion:
AppContext.SetSwitch(
"System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
GrpcChannel.ForAddress("http://localhost:5000");
But both approaches result in another exception (presumably because this will use an older HttpClientHandler implementation that doesn't support HTTP/2.0):
Exception caught: MyException: An unknown error happened
---> Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Request protocol 'HTTP/1.1' is not supported.")
I also tried to circumvent the self-signed certificate by creating a custom CA plus certificate and installing that root certificate in the iOS simulator. But the same result occurred. I'm not sure if it would have made a difference on Android, because I was not able to install the root certificate on the emulator.
How can I use gRPC with Xamarin.Forms against a development server?
I've fixed he above exception changing my implementation to match this : https://techblog.livongo.com/implementing-grpc-in-a-xamarin-mobile-project/ block.
In more details:
I've added references to Grpc.Core, Grpc.Tools, Grpc.Net.Common, Grpc.Net.Client, Grpc.Core.Api;
I've added this code when creating the Channel:
var credentials = CallCredentials.FromInterceptor((context, metadata) => Task.CompletedTask);
return new Channel("<your domai>:<port>", ChannelCredentials.Create(new SslCredentials(), credentials));

Azure MobileApp Client SDK crashes on Xamarin.Forms (Android)

The reproduce the error, I've created a blank mobile app and a new sql database.
Afterwards I've followed the quickstart guide and downloaded the backend and the Xamarin.Forms project.
I've deployed the Web-App to azure and started the Android app using the 5'' KitKat (4.4) XXHDPI Phone (Android 4.4 - API 19) emulator.
As soon as I add an item to the TodoItem - List (you see, it's the default sample app of xamarin mobile apps), the todoTable.InsertAsync call fails.
Unfortunately, there is no detailed error.
It's just a error message telling "An unhandled exception occured".
In the output window, I can see the following error:
03-01 19:48:33.781 I/MonoDroid( 1512): UNHANDLED EXCEPTION:
03-01 19:48:33.793 I/MonoDroid( 1512): Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException: The request could not be completed. (Internal Server Error)
03-01 19:48:33.793 I/MonoDroid( 1512): at Microsoft.WindowsAzure.MobileServices.MobileServiceHttpClient+<ThrowInvalidResponse>d__24.MoveNext () [0x001ec] in <42e24ce875d34485ad11c4f8aebb904a>:0
So I've had a look at the azure mobile app in the azure portal.
Before I've ran the tests, I've enabled almost all the logs.
I can see FREB-Protocols I can see a post request to /tables/TodoItem which failed with http 500. Unfortunately it does not tell my anything about the error itself. Authentification is disabled, so the authentification should not cause any problems.
I've included the whole log here:
https://1drv.ms/b/s!AqCo2Ottp6L6wC7-nOFkDN7cERE6
Does anyone got any idea what could be wrong? Sitting here since far to many hours, guessing what could be wrong.
The Internal Server Error means your service is crashing - that's why it is not producing any logs. You've not shared anything about your backend, but that is where your problem is.
Run the service locally and attach a debugger and step through the backend code. You can find details of running the service locally (attached to a local SQL instance) in the HOWTO documents on learn.microsoft.com.

.NET System.Security.Authentication.AuthenticationException

We are connecting to a specific website using a TLS connection. We are enforcing TLS 1.2 as the only allowed protocol in the client code. In about 99% of all cases connection setup is OK and shows no errors. However from time to time we receive the following exception:
System.Security.Authentication.AuthenticationException: A call to SSPI failed, see inner exception. ---> System.ComponentModel.Win32Exception: The buffers supplied to a function was too small
--- End of inner exception stack trace ---
at System.Net.Security.SslState.EndProcessAuthentication(IAsyncResult result)
at Fw.Connections.Ssl.Private.ClientInstance.AuthenticateClientCallback(IAsyncResult result);
We have tested the remote server with SSL labs and we receive an A+ with no failures or problems reported, it's not a server under our control so the remote environment is not very well known.. We have tested the client code on Windows server 2012 (datacenter) and Windows7 clients. Both show the error from time to time. Is this a known bug in the .NET framework?

Calling REST service via HttpClient from ASP.net 4 Web API 2 project results in exception

Current Situation: I have created an ASP.net project (web project) using the WebAPI 2 template. One of the HTTPGET method now tries to make a request to another REST API using the following code.
[HttpGet]
public string Get()
{
var uri = #"http://api.../...";
var httpClient = new HttpClient();
var response = httpClient.GetAsync(uri).GetAwaiter().GetResult(); // <- exception here
response.EnsureSuccessStatusCode();
string content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return content;
}
At the line, marked with the comment, the following HttpRequestException with the message "Unable to connect to the remote server" (inner exception) is thrown.
System.Net.Http.HttpRequestException was unhandled by user code
HResult=-2146233088
Message=An error occurred while sending the request.
Source=mscorlib
...
InnerException:
HResult=-2146233079
Message=Unable to connect to the remote server
Source=System
StackTrace:
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)
InnerException:
ErrorCode=10060
HResult=-2147467259
Message=A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond ...
NativeErrorCode=10060
Source=System
StackTrace:
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception)
InnerException:
What I tried so far:
I used the exact same URI (via copy & paste) in the browser and the JSON with some data is returned.
Before creating the WebAPI2 project I played around with the RC candidate of ASP.net 5. I created a project based on the ASP.net 5 template "Web API". There I have the exact same code and it is working fine.
Assumption: My assumption is that the config in the ASP.net 4 Web API 2 template has some config that prevents from getting a response. I found some similar question on SO where it seems that some private networking was disabled.
Question: What does prevent the response from being received when using HttpClient in a ASP.net 4 Web API 2 based project?
I was able to find out what the root cause is. The short answer is that the ASP.net 5 template used a proxy, probably one that is configured via Windows group policies, while the ASP.net 4 template directly connected to the other REST service. But because this is prohibited by some firewall it didn't work.
Long answer: In order to check the network traffic of both templates (ASP.net 4 and 5) I used Process Monitor from sysinternals tools. I set a filter to IISExpress.exe and dnx.exe.
For the ASP.net 4 template this looked as follows.
There are only two entries where you can see that IISExpress directly connects to the REST service. In case of ASP.net 5 there are a lot more entries because instead of a direct connection it uses a proxy. I actually assumed that this is a proxy because of its host name. One point to note here is that instead of IISExpress.exe, dnx.exe is building up the connection.
In order to let IISExpress use a proxy for the ASP.net 4 template I added the following lines into the projects web.config. I used the hostname and port as shown in Process Monitor.
<system.net>
<defaultProxy>
<proxy usesystemdefault="True"
proxyaddress="http://proxy.<hostname>:<port>"
bypassonlocal="True"/>
</defaultProxy>
</system.net>
So my assumption is that somewhere in Windows this proxy is configured, probably via group policies(?). Firefox, dnx.exe, and others are using this configuration. But IISExpress ignores this and therefore does not receive an answer from the REST API.
It is happening may be he has not allowed Public to use, there may be some specific domains allowed. As what it seems to me.
You can read this article for better understanding :
http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

Categories