Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Can we consume REST with HTTPS self-signed as a Release (not as Debug)?

OsoBOsoB Member ✭✭
edited August 12 in Xamarin.Forms

I'm running the Xamarin tutorial at https://docs.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/webservices-todorest/ and it is working fine in Debug mode. The project implements an InsecureHandler, to permit HTTPS to be used locally with a self-signed certificate in Debug mode. I've read the blog article by @DavidBritch which provides helpful background.

However, this is fine for debugging, but it doesn't appear to work when I run it as a Release, even if I modify #if DEBUG to invoke the insecure handler in Release mode. This is important because I need to be able to demonstrate the application on a physical device connected to our office LAN.

I can't understand why it doesn't work in Release mode, even if I force the insecure handler. I think the application is maintained by David and wonder if anyone, perhaps including David, can enlighten on this. What do I need to do in order to make the insecure handler work in Release mode? I have even tried HTTP traffic, modifying the code (and the API) to support only HTTP on port 5000 and adding android:usesCleartextTraffic in the manifest, but for some reason that returns blank data also.

In RestService.cs it applies the test to determine if we're using Debug. It reads from the URL and deserialises the contents, but in Release mode, nothing is returned, despite overriding that compile-time directive.

        public RestService()
        {
#if DEBUG
            client = new HttpClient(DependencyService.Get<IHttpClientHandlerService>().GetInsecureHandler());
#else
            client = new HttpClient();
#endif
        }
        public async Task<List<TodoItem>> RefreshDataAsync()
        {
            Items = new List<TodoItem>();
            Uri uri = new Uri(string.Format(Constants.RestUrl, string.Empty));
            try
            {
                HttpResponseMessage response = await client.GetAsync(uri);
                if (response.IsSuccessStatusCode)
                {
                    string content = await response.Content.ReadAsStringAsync();
                    Items = JsonConvert.DeserializeObject<List<TodoItem>>(content);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(@"\tERROR {0}", ex.Message);
            }
            return Items;
        }

Meanwhile, as I think I understand it, within HttpClientHandlerService.cs it implements an HttpClient returning a local certificate without errors.

[assembly: Dependency(typeof(HttpClientHandlerService))]
{
    public class HttpClientHandlerService : IHttpClientHandlerService
    {
        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;
        }
    }
}

Best Answer

  • DavidBritchDavidBritch GBXamarin Team Xamurai
    Accepted Answer

    Hi @OsoB

    The first thing to mention is that this is just an Android problem. I confirmed myself that there aren't any problems on iOS - it works equally well in debug and release modes. That narrows down the options of what the problem could be.

    I won't bore you with how I got to where I got to, but I eventually added some exception handling for Java exceptions, and discovered that in release mode a Java.Net.SocketException was being thrown, with an EACCESS (Permission denied) error. This lead me to the Android manifest where I discovered that the internet permission wasn't enabled. After enabling that permission the TodoREST sample started working on Android in release mode.

    I'll get docs/samples/blog posts updated.

Answers

  • DavidBritchDavidBritch GBXamarin Team Xamurai

    Hi @OsoB

    Here's the correct network stack settings (correctly set for the DEBUG build):

    When I switch to a release build the network stack settings reset to the default:

    I'd recommend trying again with the AndroidClientHandler set as the HttpClient implementation in release mode.

  • OsoBOsoB Member ✭✭
    edited August 13

    @DavidBritch said:
    I'd recommend trying again with the AndroidClientHandler set as the HttpClient implementation in release mode.

    Thanks for the reply @DavidBritch . I'll confess I hadn't realised those HTTP options were unique to Debug and Release, until seeing your reply.

    Changing them hasn't made any difference to its behaviour in Release mode unfortunately. I also removed the compile-time directive to ensure we're using the insecure handler under Release, and then when that didn't have any effect, I reverted it back and tried again.

    Incidentally, mine only provides the Android option in HttpClient implementation, not AndroidClientHandler. They are as follows...

    To summarise, under Debug, my options are the following (and Debug works fine)...

    Under Release they were originally as below, prior to being charged to match the above...

    I changed the Release HttpClient implementation, to be Android. When that didn't have any effect, I also changed SSL/TLS implementation to be Native TLS 1.2+ instead of Default Native TLS 1.2+. That didn't make any difference.

    I'm not sure if I'm missing something obvious here. Incidentally, I did find a workaround this morning, enabling me to install the application on a physical device. I used Debug mode, but started the application "Without Debugging" (Ctrl+F5) rather then "Start Debugging" (F5). There are limitations but it enables me to deploy the application on the device at least for demo purposes in the office. It would be great to get to the bottom of this however, and get Release mode to work.

  • OsoBOsoB Member ✭✭

    Just to add a relevant finding @DavidBritch on this requirement to utilise HTTPS self-signed as a Release, rather than Debug, I've found that it doesn't make any difference to the behaviour, irrespective of which Http stack I use in Debug — it always works fine regardless of whether it's Default, Managed, or Android.

    But Release mode just returns an empty string, irrespective of which Http stack is selected. In other words, Debug always works, while Release never works. There must be something fundamental in the stack that isn't allowing Release to be used without a certificate.

    It's a pity that we need a certificate just to be able to deliver a demo system.

  • DavidBritchDavidBritch GBXamarin Team Xamurai
    Accepted Answer

    Hi @OsoB

    The first thing to mention is that this is just an Android problem. I confirmed myself that there aren't any problems on iOS - it works equally well in debug and release modes. That narrows down the options of what the problem could be.

    I won't bore you with how I got to where I got to, but I eventually added some exception handling for Java exceptions, and discovered that in release mode a Java.Net.SocketException was being thrown, with an EACCESS (Permission denied) error. This lead me to the Android manifest where I discovered that the internet permission wasn't enabled. After enabling that permission the TodoREST sample started working on Android in release mode.

    I'll get docs/samples/blog posts updated.

  • OsoBOsoB Member ✭✭

    @DavidBritch said: I won't bore you with how I got to where I got to, but I eventually added some exception handling for Java exceptions, and discovered that in release mode a Java.Net.SocketException was being thrown, with an EACCESS (Permission denied) error. This lead me to the Android manifest where I discovered that the internet permission wasn't enabled. After enabling that permission the TodoREST sample started working on Android in release mode.

    That's fixed it, thanks for this David as the process has clarified quite a number of amazingly useful points along the way, even if it has had me scratching my head a lot. I just saw your source update in Github. Yes, adding the INTERNET permission resolves it.

    <uses-permission android:name="android.permission.INTERNET" />

    Actually when I first saw your reply, I very quickly added ACCESS_NETWORK_STATE to the manifest because I remembered seeing this included in an earlier Xamarin tutorial I went through. But in fact ACCESS_NETWORK_STATE doesn't make any difference to this problem — it specifically needs INTERNET permission. I'm not sure what the difference between those is, or why the earlier tutorials were specifying it.

    This is the one I've generally been adding to HTTP projects, perhaps not really necessary...

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    Anyway, it's working now and thanks again. It's a great end to the week!

  • OsoBOsoB Member ✭✭
    edited August 15

    Without wanting to prolong this @DavidBritch it raises an important question why android.permission.INTERNET is necessary for this particular application, in contrast with others, such as the Xamarin Tutorial Weather App., where they work in RELEASE mode without it. Is the reason because we're using port 5001?
    https://docs.microsoft.com/en-us/xamarin/get-started/tutorials/web-service/?tabs=vswin

    That in itself makes it perhaps understandable that the setting might be overlooked. The app uses the below API and contains no permissions whatsoever.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.WeatherApp">
        <uses-sdk android:minSdkVersion="21" />
        <application android:label="WeatherApp.Android">
        </application>
    </manifest>
    

    ... and the API declaration.

        public static class Constants
        {
            public static string OpenWeatherMapEndpoint = "https://api.openweathermap.org/data/2.5/weather";
            public static string OpenWeatherMapAPIKey = "INSERT_API_KEY_HERE";
    

    The Xamarin documentation below gives only limited help. It simply adds that android.permission.INTERNET is necessary if the app. must connect to the internet, and yet its own tutorial examples do not include it.

  • DavidBritchDavidBritch GBXamarin Team Xamurai

    Hi @OsoB

    There are different ways of adding permissions in Xamarin.Android apps. Via the manifest is the standard approach. Another way is via an attribute. In the web service tutorial, if you look in AssemblyInfo.cs you'll see:

    [assembly: UsesPermission(Android.Manifest.Permission.Internet)]
    

    I didn't deliberately choose to add the permission via the attribute. It's more that the project template used at the time did this automatically.

  • OsoBOsoB Member ✭✭
    edited August 17

    @DavidBritch said: There are different ways of adding permissions in Xamarin.Android apps. Via the manifest is the standard approach. Another way is via an attribute. In the web service tutorial, if you look in AssemblyInfo.cs you'll see:

    I see what you mean David, thanks. To be honest with you I had forgotten about the attributes in the AssemblyInfo.cs file. Now that you mention it, I can recall seeing them when I began using Xamarin. This is the thing with broad platforms such as this one — there's a certain inevitable amount of 'forgetting' involved in the learning process that we have to accept!

Sign In or Register to comment.