Skip to content

Commit

Permalink
Allow X509TrustManagerWithValidationCallback to be trimmed away if Se…
Browse files Browse the repository at this point in the history
…rverCertificateCustomValidationCallback isn't set

Also remove changes from AndroidClientHandler.Legacy.cs since we don't care about enabling it for legacy
  • Loading branch information
akoeplinger committed Apr 25, 2022
1 parent 7c6da63 commit 765c26e
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ void AppendEncoding (string encoding, ref List <string>? list)

// SSL context must be set up as soon as possible, before adding any content or
// headers. Otherwise Java won't use the socket factory
SetupSSL (httpConnection as HttpsURLConnection, request);
SetupSSL (httpConnection as HttpsURLConnection);
if (request.Content != null)
AddHeaders (httpConnection, request.Content.Headers);
AddHeaders (httpConnection, request.Headers);
Expand Down Expand Up @@ -893,7 +893,7 @@ void AppendEncoding (string encoding, ref List <string>? list)
return null;
}

void SetupSSL (HttpsURLConnection? httpsConnection, HttpRequestMessage requestMessage)
void SetupSSL (HttpsURLConnection? httpsConnection)
{
if (httpsConnection == null)
return;
Expand All @@ -917,19 +917,16 @@ void SetupSSL (HttpsURLConnection? httpsConnection, HttpRequestMessage requestMe
var tmf = ConfigureTrustManagerFactory (keyStore);

if (tmf == null) {
// If there are no trusted certs, no custom trust manager factory or custom certificate validation callback
// If there are no trusted certs and no custom trust manager
// there is no point in changing the behavior of the default SSL socket factory
if (!gotCerts && ServerCertificateCustomValidationCallback == null)
if (!gotCerts)
return;

tmf = TrustManagerFactory.GetInstance (TrustManagerFactory.DefaultAlgorithm);
tmf?.Init (gotCerts ? keyStore : null); // only use the custom key store if the user defined any trusted certs
tmf?.Init (keyStore);
}

ITrustManager[]? trustManagers =
ServerCertificateCustomValidationCallback == null
? tmf?.GetTrustManagers ()
: X509TrustManagerWithValidationCallback.Inject(tmf?.GetTrustManagers (), requestMessage, ServerCertificateCustomValidationCallback);
ITrustManager[]? trustManagers = tmf?.GetTrustManagers ();

var context = SSLContext.GetInstance ("TLS");
context?.Init (kmf?.GetKeyManagers (), trustManagers, null);
Expand Down
26 changes: 20 additions & 6 deletions src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,22 @@ public CookieContainer CookieContainer

public bool CheckCertificateRevocationList { get; set; } = false;

public Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool>? ServerCertificateCustomValidationCallback { get; set; }
X509TrustManagerWithValidationCallback.Helper? _callbackTrustManagerHelper = null;

Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool>? _serverCertificateCustomValidationCallback;

public Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool>? ServerCertificateCustomValidationCallback
{
get
{
return _serverCertificateCustomValidationCallback;
}
set
{
_callbackTrustManagerHelper = value != null ? new X509TrustManagerWithValidationCallback.Helper (value) : null;
_serverCertificateCustomValidationCallback = value;
}
}

// See: https://developer.android.com/reference/javax/net/ssl/SSLSocket#protocols
public SslProtocols SslProtocols { get; set; } =
Expand Down Expand Up @@ -1026,17 +1041,16 @@ void SetupSSL (HttpsURLConnection? httpsConnection, HttpRequestMessage requestMe
if (tmf == null) {
// If there are no trusted certs, no custom trust manager factory or custom certificate validation callback
// there is no point in changing the behavior of the default SSL socket factory
if (!gotCerts && ServerCertificateCustomValidationCallback == null)
if (!gotCerts && _callbackTrustManagerHelper == null)
return;

tmf = TrustManagerFactory.GetInstance (TrustManagerFactory.DefaultAlgorithm);
tmf?.Init (gotCerts ? keyStore : null); // only use the custom key store if the user defined any trusted certs
}

ITrustManager[]? trustManagers =
ServerCertificateCustomValidationCallback == null
? tmf?.GetTrustManagers ()
: X509TrustManagerWithValidationCallback.Inject(tmf?.GetTrustManagers (), requestMessage, ServerCertificateCustomValidationCallback);
ITrustManager[]? trustManagers = tmf?.GetTrustManagers ();

trustManagers = _callbackTrustManagerHelper?.Inject (trustManagers, requestMessage);

var context = SSLContext.GetInstance ("TLS");
context?.Init (kmf?.GetKeyManagers (), trustManagers, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,31 @@ namespace Xamarin.Android.Net
{
internal sealed class X509TrustManagerWithValidationCallback : Java.Lang.Object, IX509TrustManager
{
internal sealed class Helper
{
Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool> _serverCertificateCustomValidationCallback;

public Helper (Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool> serverCertificateCustomValidationCallback)
{
_serverCertificateCustomValidationCallback = serverCertificateCustomValidationCallback;
}

public ITrustManager[] Inject (
ITrustManager[]? trustManagers,
HttpRequestMessage requestMessage)
{
IX509TrustManager? x509TrustManager = trustManagers?.OfType<IX509TrustManager> ().FirstOrDefault ();
IEnumerable<ITrustManager> otherTrustManagers = trustManagers?.Where (manager => manager != x509TrustManager) ?? Enumerable.Empty<ITrustManager> ();
var trustManagerWithCallback = new X509TrustManagerWithValidationCallback (x509TrustManager, requestMessage, _serverCertificateCustomValidationCallback);
return otherTrustManagers.Prepend (trustManagerWithCallback).ToArray ();
}
}

private readonly IX509TrustManager? _internalTrustManager;
private readonly HttpRequestMessage _request;
private readonly Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool> _serverCertificateCustomValidationCallback;

public static ITrustManager[] Inject(
ITrustManager[]? trustManagers,
HttpRequestMessage requestMessage,
Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool> serverCertificateCustomValidationCallback)
{
IX509TrustManager? x509TrustManager = trustManagers?.OfType<IX509TrustManager> ().FirstOrDefault ();
IEnumerable<ITrustManager> otherTrustManagers = trustManagers?.Where (manager => manager != x509TrustManager) ?? Enumerable.Empty<ITrustManager> ();
var trustManagerWithCallback = new X509TrustManagerWithValidationCallback (x509TrustManager, requestMessage, serverCertificateCustomValidationCallback);
return otherTrustManagers.Prepend (trustManagerWithCallback).ToArray ();
}

public X509TrustManagerWithValidationCallback(
private X509TrustManagerWithValidationCallback (
IX509TrustManager? internalTrustManager,
HttpRequestMessage request,
Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool> serverCertificateCustomValidationCallback)
Expand Down Expand Up @@ -58,7 +67,7 @@ public void CheckServerTrusted (JavaX509Certificate[] javaChain, string authType
}

if (!_serverCertificateCustomValidationCallback (_request, certificate, chain, sslPolicyErrors)) {
throw new JavaCertificateException("The remote certificate was rejected by the provided RemoteCertificateValidationCallback.");
throw new JavaCertificateException ("The remote certificate was rejected by the provided RemoteCertificateValidationCallback.");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Java.Interop\JavaObjectExtensionsTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Xamarin.Android.Net\AndroidClientHandlerTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Xamarin.Android.Net\HttpClientIntegrationTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Xamarin.Android.Net\ServerCertificateCustomValidationCallbackTest.cs" />
<Compile Condition=" '$(TargetFramework)' == 'net6.0-android$(AndroidLatestStableApiLevel)' " Include="$(MSBuildThisFileDirectory)Xamarin.Android.Net\ServerCertificateCustomValidationCallbackTest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Xamarin.Android.RuntimeTests\MainActivity.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Xamarin.Android.RuntimeTests\MyIntent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Xamarin.Android.RuntimeTests\NonJavaObject.cs" />
Expand Down

0 comments on commit 765c26e

Please sign in to comment.