Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebException SecureChannelFailure when invoking web service over https from Xamarin Droid app

I'm creating a Xamarin.Droid app with Visual Studio (C#).

I have a RESTful web service (WebAPI) from which I want to retrieve some data in a Xamarin Droid project, using HttpWebRequest. The fact that it's WebAPI and HttpWebRequest is not the key as I had the exact same problem (described below) with some older WSE 3.0 web services and the use of HttpClient in the generated proxies. In the case of those WSE 3.0 web services we solved the problem by extending the generated proxy class to use ModernHttpClient instead. We didn't realize this problem manifests outside of HttpClient. I now think we must be doing something wrong, as opposed to blaming HttpClient.

In the current iteration, connecting to WebAPI, I'm using HttpWebRequest. Whether using HttpWebRequest or HttpClient, everything works fine as long as I'm connecting via http. As soon as I try to connect via https the request fails. For clarification, the web server hosting the web service does have a valid certificate (it's not expired, and has a valid CA.)

I have not been able to find any solution to this problem, despite a ton of Googling. I can barely find references to other people having this problem, much less a solution, which makes me think there must be something wrong in what I'm doing.

Basically, you can this method to a Xamarin.Forms Droid project and call it. If url is using https then request.GetResponse() throws a WebException: "Error: SecureChannelFailure (The authentication or decryption has failed.)". If url is using plain http then the call executes cleanly and returns the expected data.

using System;
using System.IO;
using System.Net;
using Android.App;
using Android.Content.PM;
using Android.OS;
// ...
private string FetchData()
{
    // Public test server that's handy for testing JSON code.
    //    http://jsonplaceholder.typicode.com
    string url = "https://jsonplaceholder.typicode.com/posts/1";

    // Create an HTTP web request using the URL:
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    request.Timeout = 10 * 1000; // 10 seconds
    request.Accept = "application/json";
    request.Method = "GET";

    string result = "";
    try
    {
        using (WebResponse response = request.GetResponse())
        {
            result = "Presumably, do something with response.GetResponseStream()";
        }
    }
    catch (Exception ex)
    {
        result = $"{ex.GetType().Name}: {ex.Message}";
    }
    return result;
}

The basic pattern there comes from a Xamarin "recipe": Call a REST Web Service. I changed it to be synchronous because when I change request.GetResponse() to request.GetResponseAsync() (and async-await) then the call either times out, or hangs indefinitely, obfuscating the underlying WebException (SecureChannelFailure).

This is a simple HTTP GET, and any browser, or REST/SOAP utility (e.g. "Simple REST Client" for Chrome) can be used to build and send that exact same request, and it works fine with both http and https. Similarly, I can take that code above and paste it into a Windows Console application and it works fine with both http and https.

Server cert is all green

For the record, love it or hate it, I have also tried adding a delegate of { return true; } to the System.Net.ServicePoint.ServerCertificateAuthenticationCallback event. To me that wouldn't be a solution anyway, but it might help diagnose the problem. Too bad a breakpoint set in that delegate is never tripped, indicating my code never even gets to that point.

It seems impossible to me that invoking web services through https is not supported in a Xamarin Droid app, so I'm going on the assumption that I'm doing something wrong. What could be causing this failure in Xamarin Droid, when it works fine in a browser, and in a plain Windows Console app?

like image 338
JMD Avatar asked Sep 06 '25 23:09

JMD


1 Answers

if you call a HTTP url and get this exception , be sure set redirect to false:

request.AllowAutoRedirect = false;

if you call a HTTPS url then put these lines before your request code:

ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
like image 177
Saeed Mousavi Avatar answered Sep 09 '25 23:09

Saeed Mousavi