Skip to content

AndroidMessageHandler - RemoteCertificateNameMismatch after redirect #7650

@akravch

Description

@akravch

Android application type

Classic Xamarin.Android (MonoAndroid12.0, etc.), Android for .NET (net6.0-android, etc.)

Affected platform version

Xamarin Android 13, .NET 7.0.100

Description

AndroidMessageHandler incorrectly validates SSL certificate after a redirect to another domain if and only if ServerCertificateCustomValidationCallback is set.
Consider the following code:

// Redirects to facebook.com
const string url = "https://cutt.ly/n0HotDr";
using var handler = new AndroidMessageHandler()
{
    ServerCertificateCustomValidationCallback = (message, certificate, chain, errors) => errors == SslPolicyErrors.None
};
using var client = new HttpClient(handler);
await client.GetAsync(url);

When I put a breakpoint inside the lambda above, I can see that I hit it twice:

  1. First time I see that the cutt.ly certificate is being validated and the HttpRequestMessage.RequestUri points to cutt.ly as well - that's expected.
  2. Second time we validate a certificate for a domain we got redirected to (e.g. Facebook), but the HttpRequestMessage.RequestUri still points to cutt.ly -- and the errors parameter has SslPolicyErrors.RemoteCertificateNameMismatch value.

If I change AndroidMessageHandler to the managed handler, it works fine. Also, if I continue to use AndroidMessageHandler, but simply don't set ServerCertificateCustomValidationCallback, it works fine as well.
So it looks like the problem is somewhere inside the custom trust manager the handler uses when the validation callback is not null: https://github.com/xamarin/xamarin-android/blob/2a10e9696cf51a467db58e5c73a4fab764507482/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs#L1093 I believe the request URI here is not updated after a redirect, but it should: https://github.com/xamarin/xamarin-android/blob/2a10e9696cf51a467db58e5c73a4fab764507482/src/Mono.Android/Xamarin.Android.Net/ServerCertificateCustomValidator.cs#L85

Steps to Reproduce

  1. Create a Xamarin.Android app a .NET 6/7 Android app
  2. Use AndroidMessageHandler to send a request that will be redirected to anotehr domain

Expected:
No issues
Actual:
An exception with the name mismatch

Did you find any workaround?

  • Using a managed handler (not fully reliable with .NET 6/7 too).
  • Not assigning ServerCertificateCustomValidationCallback. In my case I only use it in production builds to log the errors if there are any, so I was able to work around this with a DelegatingHandler and a few try/catch-es. For those who use the callback to apply some additional validation this may be a bigger deal.

Relevant log output

Java.Security.Cert.CertificateException: Exception_WasThrown, Java.Security.Cert.CertificateException
   at Xamarin.Android.Net.ServerCertificateCustomValidator.TrustManager.CheckServerTrusted(X509Certificate[] , String )
   at Javax.Net.Ssl.IX509TrustManagerInvoker.n_CheckServerTrusted_arrayLjava_security_cert_X509Certificate_Ljava_lang_String_(IntPtr , IntPtr , IntPtr , IntPtr )
   at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPLL_V(_JniMarshal_PPLL_V , IntPtr , IntPtr , IntPtr , IntPtr )
  --- End of managed Java.Security.Cert.CertificateException stack trace ---
java.security.cert.CertificateException: The remote certificate was rejected by the provided RemoteCertificateValidationCallback.
	at xamarin.android.net.ServerCertificateCustomValidator_TrustManager.n_checkServerTrusted(Native Method)
	at xamarin.android.net.ServerCertificateCustomValidator_TrustManager.checkServerTrusted(ServerCertificateCustomValidator_TrustManager.java:42)
	at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:254)
	at com.android.org.conscrypt.ConscryptEngine.verifyCertificateChain(ConscryptEngine.java:1644)
	at com.android.org.conscrypt.NativeCrypto.ENGINE_SSL_read_direct(Native Method)
	at com.android.org.conscrypt.NativeSsl.readDirectByteBuffer(NativeSsl.java:568)
	at com.android.org.conscrypt.ConscryptEngine.readPlaintextDataDirect(ConscryptEngine.java:1095)
	at com.android.org.conscrypt.ConscryptEngine.readPlaintextData(ConscryptEngine.java:1079)
	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:876)
	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:747)
	at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:712)
	at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.processDataFromSocket(ConscryptEngineSocket.java:849)
	at com.android.org.conscrypt.ConscryptEngineSocket$SSLInputStream.access$100(ConscryptEngineSocket.java:722)
	at com.android.org.conscrypt.ConscryptEngineSocket.doHandshake(ConscryptEngineSocket.java:238)
	at com.android.org.conscrypt.ConscryptEngineSocket.startHandshake(ConscryptEngineSocket.java:217)
	at com.android.okhttp.internal.io.RealConnection.connectTls(RealConnection.java:196)
	at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:153)
	at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
	at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
	at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
	at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
	at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:302)
	at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:245)
	at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
	at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:131)
	at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:90)
	at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:30)

Metadata

Metadata

Assignees

Labels

Area: HTTPIssues with sockets / HttpClient.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions