3

I am writing an application that needs to read from a REST api that is only available over https. I am running into the issue where the request fails in Mono.Security, with the message: "The authentication or decryption has failed."

I did my research and found that Mono by default doesn't have any trusted certificates. All the sources I found said that I could use

ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; });

within the Main() and OnCreate() methods in the iOS and Droid projects respectively to override that check and allow any ssl cert. Even with that workaround, I'm still getting the same error. I have stepped through the code and confirmed that the above line is executed when running on iOS and Android.

My code works perfectly when accessing non-https APIs. This is a PCL, not shared, project.

I referred to these questions/resources before asking:

  • Ignore SSL certificate errors in Xamarin.Forms (PCL)
  • stackoverflow.com/questions/2675133/c-sharp-ignore-certificate-errors/2675183#2675183
  • bugzilla.xamarin.com/show_bug.cgi?id=6501
  • stackoverflow.com/questions/12287528/webclient-ssl-exception-with-android-4-and-mono-for-android
  • www.mono-project.com/docs/faq/security/

Here is the code so far:

public class PawPrintsDataConnection
{
    private string response = "";
    private Task<string> StartWebRequest(string url)
    {

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.ContentType = "application/json";
        request.Method = "GET";

        Task<WebResponse> task = Task.Factory.FromAsync (request.BeginGetResponse, asyncResult => request.EndGetResponse (asyncResult), (object)null);
        return task.ContinueWith (t => ReadStreamFromResponse (t.Result));

    }
    private string ReadStreamFromResponse(WebResponse response)
    {
        using (Stream responseStream = response.GetResponseStream ())
        using (StreamReader sr = new StreamReader (responseStream)) {
            string strContent = sr.ReadToEnd ();
            return strContent;
        }
    }

    public string getRawResponse(){
        var task = StartWebRequest(string.Format (@"https://pawprints.rit.edu/v1/petitions?key={0}&limit={1}", "apikey", 50));

        this.response = task.Result;
        return response;
    }
}


public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
    protected override void OnCreate (Bundle bundle)
    {
        ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; });

        base.OnCreate (bundle);

        global::Xamarin.Forms.Forms.Init (this, bundle);

        LoadApplication (new App ());
    }
}
static void Main (string[] args)
    {
        ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; });
        // if you want to use a different Application Delegate class from "AppDelegate"
        // you can specify it here.
        UIApplication.Main (args, null, "AppDelegate");
        //ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

    }

In my research, I discovered a bug on the Xamarin bugzilla that may be relevant, but I'm not sure that it applies to the version I'm using. I'm very new to Xamarin dev, so I'm not familiar with things like which version of Mono.security is included. https://bugzilla.xamarin.com/show_bug.cgi?id=26658

If it's helpful, here is the relevant portion of the exception:

System.AggregateException: One or more errors occurred ---> System.Exception: One or more errors occurred ---> System.Exception: Error: SendFailure (Error writing headers) ---> System.Exception: Error writing headers ---> System.Exception: The authentication or decryption has failed. ---> System.Exception: The authentication or decryption has failed.


at Mono.Security.Protocol.Tls.RecordProtocol.ProcessAlert (AlertLevel alertLevel, AlertDescription alertDesc) [0x00013] in ///Library/Frameworks/Xamarin.iOS.framework/Versions/8.6.1.26/src/mono/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/RecordProtocol.cs:654
at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x000dc] in ///Library/Frameworks/Xamarin.iOS.framework/Versions/8.6.1.26/src/mono/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/RecordProtocol.cs:377
Community
  • 1
  • 1
Nathan Castle
  • 310
  • 4
  • 9

1 Answers1

5

You're accessing pawprints.rit.edu right ?

Then the certificate for the site (and it's root CA) are fine, i.e. iOS would accept it (and Xamarin.iOS delegate the trust decision to iOS). IOW setting the delegate does not help you (it's for the certificate only and that's fine).

The issue here is that the server is configured to allow only a small subset of TLS 1.0 cipher suites. None of them compatible with Mono's current SSL/TLS implementation used by HttpWebRequest.

Your best alternative is to use a HttpClient and the CFNetworkHandler (for iOS) or a 3rd party handle (e.g. ModernHttpClient would work for both iOS and Android). That will use the native (from the OS) SSL/TLS implementation which has support for those cipher suites (and much better performance).

poupou
  • 43,413
  • 6
  • 77
  • 174