From d5d574eb95391887ad90cfb8eaae7ed5f8abee74 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 26 May 2017 14:40:50 +0200 Subject: [PATCH] A collection of assorted enhancements to AndroidClientHandler The changes are result of a bug (https://bugzilla.xamarin.com/show_bug.cgi?id=55477) followup and are based on code and ideas from Jeremy Cook : * If `UseProxy == false` instantiate Java URLConnection that doesn't use the system proxy. This is important for code that accesses local resources from a device otherwise configured to use a proxy. * Allow for insecure SSL requests: let the code bypass SSL host name and certificate checks. This is necessary for code that connects to devices with self-signed or otherwise "invalid" SSL certificates as well as ones which do not have valid DNS entries set up for them. The implementation makes it possible to use a custom host name verifier and also to provide a custom socket factory. --- .../AndroidClientHandler.cs | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs index 036634954fb..357dc2c5556 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs @@ -219,6 +219,20 @@ string EncodeUrl (Uri url) return bldr.ToString (); } + /// + /// Returns a custom host name verifier for a HTTPS connection. By default it returns null and + /// thus the connection uses whatever host name verification mechanism the operating system defaults to. + /// Override in your class to define custom host name verification behavior. The overriding class should + /// not set the property directly on the passed + /// + /// + /// Instance of IHostnameVerifier to be used for this HTTPS connection + /// HTTPS connection object. + protected virtual IHostnameVerifier GetSSLHostnameVerifier (HttpsURLConnection connection) + { + return null; + } + /// /// Creates, configures and processes an asynchronous request to the indicated resource. /// @@ -241,7 +255,19 @@ string EncodeUrl (Uri url) }; while (true) { URL java_url = new URL (EncodeUrl (redirectState.NewUrl)); - URLConnection java_connection = java_url.OpenConnection (); + URLConnection java_connection; + if (UseProxy) + java_connection = java_url.OpenConnection (); + else + java_connection = java_url.OpenConnection (Java.Net.Proxy.NoProxy); + + var httpsConnection = java_connection as HttpsURLConnection; + if (httpsConnection != null) { + IHostnameVerifier hnv = GetSSLHostnameVerifier (httpsConnection); + if (hnv != null) + httpsConnection.HostnameVerifier = hnv; + } + if (ConnectTimeout != TimeSpan.Zero) java_connection.ConnectTimeout = checked ((int)ConnectTimeout.TotalMilliseconds); @@ -729,11 +755,33 @@ void AppendEncoding (string encoding, ref List list) return httpConnection; } + /// + /// Configure and return a custom for the passed HTTPS . If the class overriding the method returns anything but the default + /// null, the SSL setup code will not call the nor the + /// methods used to configure a custom trust manager which is + /// then used to create a default socket factory. + /// Deriving class must perform all the key manager and trust manager configuration to ensure proper + /// operation of the returned socket factory. + /// + /// Instance of SSLSocketFactory ready to use with the HTTPS connection. + /// HTTPS connection to return socket factory for + protected virtual SSLSocketFactory ConfigureCustomSSLSocketFactory (HttpsURLConnection connection) + { + return null; + } + void SetupSSL (HttpsURLConnection httpsConnection) { if (httpsConnection == null) return; + SSLSocketFactory socketFactory = ConfigureCustomSSLSocketFactory (httpsConnection); + if (socketFactory != null) { + httpsConnection.SSLSocketFactory = socketFactory; + return; + } + KeyStore keyStore = KeyStore.GetInstance (KeyStore.DefaultType); keyStore.Load (null, null); bool gotCerts = TrustedCerts?.Count > 0;