Thursday, 30 April 2020

Connecting to localhost over HTTPS from simulators and emulators (revisited)

Last year, I wrote about how to connect to localhost over HTTPS from iOS simulators and Android emulators. Connecting to localhost web services that run over HTTP is straight forward, however it’s more work when the web service runs over HTTPS. It typically involves:

  • Creating a self-signed developer certificate on your machine.
  • Choosing the network stack to use.
  • Specifying the address of your local machine.
  • Bypassing the local development certificate check.

Connect to local web services from iOS simulators and Android emulators documents how to carry out the above steps. However, I recently discovered that the approach for bypassing the local development certificate check on iOS no longer worked.

At the time of writing the original blog post, the CoreFX implementation of HttpClient was in most Mono profiles, but not in iOS. The recommendation for iOS was to use the managed network stack, and the ServicePointManager API. However. since then iOS support for the CoreFX HttpClient implementation has dropped, which hooks into the NSUrlSession network stack. This ultimately means that now, on both iOS and Android, the approach for bypassing the local development certificate check is identical.

SSL errors can be ignored on iOS and Android for local HTTPS web services by setting the HttpClientHandler.ServerCertificateCustomValidationCallback property to a callback that ignores the result of the certificate security check for the localhost certificate:

public HttpClientHandler GetInsecureHandler()
{
    HttpClientHandler handler = new HttpClientHandler();
    handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
    {
        if (cert.Issuer.Equals("CN=localhost"))
            return true;
        return errors == System.Net.Security.SslPolicyErrors.None;
    };
    return handler;
}
This method must be in a class in a platform project, even if the HttpClient object is constructed in a shared project. The HttpClientHandler object returned by the GetInsecureHandler method should then be passed as an argument to the HttpClient constructor:

HttpClient client = new HttpClient(GetInsecureHandler());
The advantage of using this approach is that it hooks into the native network stacks on both iOS and Android (NSUrlSession on iOS, AndroidClientHandler on Android).

For a full sample that demonstrates this approach in Xamarin.Forms, see my GitHub repo.

No comments:

Post a comment