I use a HttpClient
to communicate with my server as shown below:
private static readonly HttpClientHandler Handler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
AllowAutoRedirect = true,
};
private static readonly HttpClient Client = new HttpClient(Handler, false);
The problem is that I get an System.AggregateException
exception on Android. The exception details can be seen in the screenshot below:
App is working perfectly fine on iOS though.
Server is using Let's Encrypt
certificate and the problem has only surfaced since a week ago after renewing the certificate.
I have already checked anything regarding certificate validity (Expiry, DNS names, etc.) and all seems to be OK.
This is a long-standing issue #6351 in Xamarin.Android, caused by LetsEncrypt's root expiring and them moving to a new root.
Below is a copy of my post in that issue explaining the situation and the workarounds. See other posts in that thread for details on the workarounds.
Scott Helme has a fantastic write-up of the situation. Go and read that first, then I'll describe how (I think) this applies to xamarin-android.
I'm going to copy the key diagram from that article (source):
The red chain is what used to happen: the IdenTrust DST Root CA X3 is an old root certificate which is trusted pretty much everywhere, including on Android devices from 2.3.6 onwards. This is what LetsEncrypt used to use as their root, and it meant that everyone trusted them. However, this IdenTrust DST Root CA X3 recently expired, which means that a bunch of devices won't trust anything signed by it. LetsEncrypt needed to move to their own root certificate.
The blue chain is the ideal new one -- the ISRG Root X1 is LetsEncrypt's own root certificate, which is included on Android 7.1.1+. Android devices >= 7.1.1 will trust certificates which have been signed by this ISRG Root X1.
However, the problem is that old pre-7.1.1 Android devices don't know about ISRG Root X1, and don't trust this.
The workaround which LetsEncrypt is using is that old Android devices don't check whether the root certificate has expired. They therefore by default serve a chain which includes LetsEncrypt's root ISRG Root X1 certificate (which up-to-date devices trust), but also include a signature from that now-expired IdenTrust DST Root CA X3. This means that old Android devices trust the chain (as they trust the IdenTrust DST Root CA X3, and don't check whether it's expired), and newer devices also trust the chain (as they're able to work out that even though the root of the chain has expired, they still trust that middle ISRG Root X1 certificate as a valid root in its own right, and therefore trust it).
This is the green path, the one which LetsEncrypt currently serves by default.
However, the BoringSSL library used by xamarin-android isn't Android's SSL library. It 1) Doesn't trust the IdenTrust DST Root CA X3 because it's expired, and 2) Isn't smart enough to figure out that it does trust the ISRG Root X1 which is also in the chain. So if you serve the green chain in the image above, it doesn't trust it. Gack.
The options therefore are:
AndroidClientHandler
as described previously. This should fix Android >= 2.3.6.--preferred-chain "ISRG Root X1"
. This means that BoringSSL ignores the IdenTrust DST Root CA X3 entirely, and roots to the ISRG Root X1. This again will only work on Android devices which trust the ISRG Root X1, i.e. 7.1.1+.