Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 666
MadWalnut Avatar asked Jun 17 '19 21:06

MadWalnut


1 Answers

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.

like image 101
MadWalnut Avatar answered Sep 28 '22 05:09

MadWalnut