[Mono.Android] Fix ServerCertificateCustomValidator (#8594)
Fixes: https://github.com/dotnet/runtime/issues/95506 In Release configuration the `X509ExtendedTrustManagerInvoker` class is trimmed and so the `trustManager is IX509TrustManager tm` pattern matching doesn't work. This PR addresses the problem in two ways: * an internal X509 trust manager is now required - it can't silently work with a null internal trust manager anymore * `[DynamicDependency]` attribute to prevent trimming of the invoker classes for the `IX509TrustManager` interface and for the `X509ExtendedTrustManager` abstract class
This commit is contained in:
Родитель
351bfa3f09
Коммит
940f059b82
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
@ -31,12 +32,12 @@ namespace Xamarin.Android.Net
|
|||
|
||||
private sealed class TrustManager : Java.Lang.Object, IX509TrustManager
|
||||
{
|
||||
private readonly IX509TrustManager? _internalTrustManager;
|
||||
private readonly IX509TrustManager _internalTrustManager;
|
||||
private readonly HttpRequestMessage _request;
|
||||
private readonly Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool> _serverCertificateCustomValidationCallback;
|
||||
|
||||
public TrustManager (
|
||||
IX509TrustManager? internalTrustManager,
|
||||
IX509TrustManager internalTrustManager,
|
||||
HttpRequestMessage request,
|
||||
Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool> serverCertificateCustomValidationCallback)
|
||||
{
|
||||
|
@ -50,7 +51,7 @@ namespace Xamarin.Android.Net
|
|||
var sslPolicyErrors = SslPolicyErrors.None;
|
||||
|
||||
try {
|
||||
_internalTrustManager?.CheckServerTrusted (javaChain, authType);
|
||||
_internalTrustManager.CheckServerTrusted (javaChain, authType);
|
||||
} catch (JavaCertificateException) {
|
||||
sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors;
|
||||
}
|
||||
|
@ -158,33 +159,29 @@ namespace Xamarin.Android.Net
|
|||
public bool Verify (string? hostname, ISSLSession? session) => true;
|
||||
}
|
||||
|
||||
private static IX509TrustManager? FindX509TrustManager(ITrustManager[]? trustManagers)
|
||||
[DynamicDependency(nameof(IX509TrustManager.CheckServerTrusted), typeof(IX509TrustManagerInvoker))]
|
||||
[DynamicDependency(nameof(IX509TrustManager.CheckServerTrusted), typeof(X509ExtendedTrustManagerInvoker))]
|
||||
private static IX509TrustManager FindX509TrustManager(ITrustManager[] trustManagers)
|
||||
{
|
||||
if (trustManagers is null)
|
||||
return null;
|
||||
|
||||
foreach (var trustManager in trustManagers) {
|
||||
if (trustManager is IX509TrustManager tm)
|
||||
return tm;
|
||||
}
|
||||
|
||||
return null;
|
||||
throw new InvalidOperationException($"Could not find {nameof(IX509TrustManager)} in {nameof(ITrustManager)} array.");
|
||||
}
|
||||
|
||||
private static ITrustManager[] ModifyTrustManagersArray (ITrustManager[] trustManagers, IX509TrustManager? original, IX509TrustManager replacement)
|
||||
private static ITrustManager[] ModifyTrustManagersArray (ITrustManager[] trustManagers, IX509TrustManager original, IX509TrustManager replacement)
|
||||
{
|
||||
var modifiedTrustManagersCount = original is null ? trustManagers.Length + 1 : trustManagers.Length;
|
||||
var modifiedTrustManagersArray = new ITrustManager [modifiedTrustManagersCount];
|
||||
|
||||
modifiedTrustManagersArray [0] = replacement;
|
||||
int nextIndex = 1;
|
||||
var modifiedTrustManagersArray = new ITrustManager [trustManagers.Length];
|
||||
|
||||
for (int i = 0; i < trustManagers.Length; i++) {
|
||||
if (trustManagers [i] == original) {
|
||||
continue;
|
||||
modifiedTrustManagersArray [i] = replacement;
|
||||
} else {
|
||||
modifiedTrustManagersArray [i] = trustManagers [i];
|
||||
}
|
||||
|
||||
modifiedTrustManagersArray [nextIndex++] = trustManagers [i];
|
||||
}
|
||||
|
||||
return modifiedTrustManagersArray;
|
||||
|
|
|
@ -516,6 +516,40 @@ namespace UnnamedProject {
|
|||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PreserveIX509TrustManagerSubclasses ([Values(true, false)] bool hasServerCertificateCustomValidationCallback)
|
||||
{
|
||||
var proj = new XamarinAndroidApplicationProject { IsRelease = true };
|
||||
proj.AddReferences ("System.Net.Http");
|
||||
proj.MainActivity = proj.DefaultMainActivity.Replace (
|
||||
"base.OnCreate (bundle);",
|
||||
"base.OnCreate (bundle);\n" +
|
||||
(hasServerCertificateCustomValidationCallback
|
||||
? "var handler = new Xamarin.Android.Net.AndroidMessageHandler { ServerCertificateCustomValidationCallback = (message, certificate, chain, errors) => true };\n"
|
||||
: "var handler = new Xamarin.Android.Net.AndroidMessageHandler();\n") +
|
||||
"var client = new System.Net.Http.HttpClient (handler);\n" +
|
||||
"client.GetAsync (\"https://microsoft.com\").GetAwaiter ().GetResult ();");
|
||||
|
||||
using (var b = CreateApkBuilder ()) {
|
||||
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
|
||||
var assemblyPath = BuildTest.GetLinkedPath (b, true, "Mono.Android.dll");
|
||||
|
||||
using (var assembly = AssemblyDefinition.ReadAssembly (assemblyPath)) {
|
||||
Assert.IsTrue (assembly != null);
|
||||
|
||||
var types = new[] { "Javax.Net.Ssl.X509ExtendedTrustManager", "Javax.Net.Ssl.IX509TrustManagerInvoker" };
|
||||
foreach (var typeName in types) {
|
||||
var td = assembly.MainModule.GetType (typeName);
|
||||
if (hasServerCertificateCustomValidationCallback) {
|
||||
Assert.IsNotNull (td, $"{typeName} shouldn't have been linked out");
|
||||
} else {
|
||||
Assert.IsNull (td, $"{typeName} should have been linked out");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoNotErrorOnPerArchJavaTypeDuplicates ([Values(true, false)] bool enableMarshalMethods)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче