12

I am trying to make HTTPS calls to site that has 2 SSL certificates: a self-signed certificate and a certificate that was signed by the the first certificate. When I use an HttpClient to send a request to the site, the console logs an untrusted chain, shows both certificates, then print a long stack trace of that is caused by java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

I have installed both certificates on my phone and navigating Chrome to the site shows a trusted connection (it had an untrusted connection warning before I installed the certificates). I believe the issue is that the App refuses to trust self-signed certificates. I do not have access to the server and thus have no influence on its certificates, so installing a certificate signed by a trusted CA is not viable.


Solutions I've tried that have not worked.

ServicePointManager.ServerCertificateValidationCallback doesn't seem to run.

I have tried using my own function for ServicePointManager.ServerCertificateValidationCallback, but the delegate I give it never seems to run. I have the following code in my MainActivity.OnCreate method, but the console never logs the message:

System.Net.ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
  Console.WriteLine($"****************************************************************************************************");

  return true;
};

HttpClientHandler.ServerCertificateCustomValidationCallback throws an exception.

I have tried using an HttpClientHandler and settings its ServerCertificateCustomValidationCallback, but I just get the message:

System.NotImplementedException: The method or operation is not implemented. at System.Net.Http.HttpClientHandler.set_ServerCertificateCustomValidationCallback (System.Func`5[T1,T2,T3,T4,TResult] value).

Setup code:

HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
HttpClient client = new HttpClient(handler);
Aaron T
  • 1,092
  • 2
  • 10
  • 25
  • Are you using the managed provider or the AndroidClientHandler and/or which SSL/TLS: BoringSSL or managed? – SushiHangover Feb 15 '19 at 18:49
  • I'm just using a regular `HttpClient` with its default settings (except when I tried to use an HttpClientHandler). – Aaron T Feb 15 '19 at 18:53
  • I'm looking for the Android build setting for the project (Project Options / Build / Android Build / General tab), if you can still using the managed provider, use the AndroidClientHandler and clean/rebuild/retest – SushiHangover Feb 15 '19 at 18:58
  • HttpClient implementation: Android. SSL/TLS implementation: Native TLS 1.2+. I changed the HttpClient implementation to Managed and when I tried the call got the exception `System.Net.WebException: Unable to read data from the transport connection: Connection reset by peer.`. – Aaron T Feb 15 '19 at 19:04
  • And SSL/TLS setting? – SushiHangover Feb 15 '19 at 19:05
  • `Native TLS 1.2+` – Aaron T Feb 15 '19 at 19:51
  • This worked for me on Android 5 https://stackoverflow.com/a/72280826/12100045 – OAkbas Jun 17 '23 at 19:52

1 Answers1

20

I was able to get this to work in both Android and iOS.

iOS was easy, just override ServicePointManager.ServerCertificateValidationCallback:

ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;

For Android I used Bruno Caceiro's answer from a similar question and a created Dependency Service.

In my Xamarin Forms project I added a simple interface:

public interface IHTTPClientHandlerCreationService
{
  HttpClientHandler GetInsecureHandler();
}

And in my Xamarin Android project I implemented the interface:

[assembly: Dependency(typeof(HTTPClientHandlerCreationService_Android))]
namespace MyApp.Droid
{
  public class HTTPClientHandlerCreationService_Android : CollateralUploader.Services.IHTTPClientHandlerCreationService
  {
    public HttpClientHandler GetInsecureHandler()
    {
      return new IgnoreSSLClientHandler();
    }
  }

  internal class IgnoreSSLClientHandler : AndroidClientHandler
  {
    protected override SSLSocketFactory ConfigureCustomSSLSocketFactory(HttpsURLConnection connection)
    {
      return SSLCertificateSocketFactory.GetInsecure(1000, null);
    }

    protected override IHostnameVerifier GetSSLHostnameVerifier(HttpsURLConnection connection)
    {
      return new IgnoreSSLHostnameVerifier();
    }
  }

  internal class IgnoreSSLHostnameVerifier : Java.Lang.Object, IHostnameVerifier
  {
    public bool Verify(string hostname, ISSLSession session)
    {
      return true;
    }
  }
}

Shared code to correctly set up the HttpClient:

switch (Device.RuntimePlatform)
{
  case Device.Android:
    this.httpClient = new HttpClient(DependencyService.Get<Services.IHTTPClientHandlerCreationService>().GetInsecureHandler());
    break;
  default:
    ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
    this.httpClient = new HttpClient(new HttpClientHandler());
    break;
}
Aaron T
  • 1,092
  • 2
  • 10
  • 25
  • 1
    where you write this code it got me compiler error !! – Amr Kamal May 01 '19 at 11:28
  • This worked for me. Excellent reply. Thank you for the answer. – navraj Apr 17 '20 at 22:10
  • It's probably in MainActivity.cs file in Android project – Rohan Sampat Jul 29 '20 at 11:25
  • 1
    I get this warning: 'SSLCertificateSocketFactory.GetInsecure(int, SSLSessionCache)' is obsolete: 'deprecated'. Also for loading images this solution seems not to work. Only JSON calls are working. – QuanDar Aug 06 '20 at 13:46
  • I followed this solution but still get error Trust anchor for certification path not found. Do I i need to install certificate on the test device for this to work? if so, would someone show me how? Is this solution only work for self-signed certificate? how about actual certificate? – HaiNguyen Oct 07 '20 at 15:42