Skip to content

Commit 940f059

Browse files
simonrozsivaljonathanpeppers
authored andcommitted
[Mono.Android] Fix ServerCertificateCustomValidator (#8594)
Fixes: dotnet/runtime#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
1 parent 351bfa3 commit 940f059

2 files changed

Lines changed: 47 additions & 16 deletions

File tree

‎src/Mono.Android/Xamarin.Android.Net/ServerCertificateCustomValidator.cs‎

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
usingSystem;
22
usingSystem.Collections.Generic;
3+
usingSystem.Diagnostics.CodeAnalysis;
34
usingSystem.Net.Http;
45
usingSystem.Net.Security;
56
usingSystem.Security.Cryptography.X509Certificates;
@@ -31,12 +32,12 @@ public ITrustManager[] ReplaceX509TrustManager (ITrustManager[]? trustManagers,
3132

3233
privatesealedclassTrustManager:Java.Lang.Object,IX509TrustManager
3334
{
34-
privatereadonlyIX509TrustManager?_internalTrustManager;
35+
privatereadonlyIX509TrustManager_internalTrustManager;
3536
privatereadonlyHttpRequestMessage_request;
3637
privatereadonlyFunc<HttpRequestMessage,X509Certificate2?,X509Chain?,SslPolicyErrors,bool>_serverCertificateCustomValidationCallback;
3738

3839
publicTrustManager(
39-
IX509TrustManager?internalTrustManager,
40+
IX509TrustManagerinternalTrustManager,
4041
HttpRequestMessagerequest,
4142
Func<HttpRequestMessage,X509Certificate2?,X509Chain?,SslPolicyErrors,bool>serverCertificateCustomValidationCallback)
4243
{
@@ -50,7 +51,7 @@ public void CheckServerTrusted (JavaX509Certificate[] javaChain, string authType
5051
varsslPolicyErrors=SslPolicyErrors.None;
5152

5253
try{
53-
_internalTrustManager?.CheckServerTrusted(javaChain,authType);
54+
_internalTrustManager.CheckServerTrusted(javaChain,authType);
5455
}catch(JavaCertificateException){
5556
sslPolicyErrors|=SslPolicyErrors.RemoteCertificateChainErrors;
5657
}
@@ -158,33 +159,29 @@ private sealed class AlwaysAcceptingHostnameVerifier : Java.Lang.Object, IHostna
158159
publicboolVerify(string?hostname,ISSLSession?session)=>true;
159160
}
160161

161-
privatestaticIX509TrustManager?FindX509TrustManager(ITrustManager[]?trustManagers)
162+
[DynamicDependency(nameof(IX509TrustManager.CheckServerTrusted),typeof(IX509TrustManagerInvoker))]
163+
[DynamicDependency(nameof(IX509TrustManager.CheckServerTrusted),typeof(X509ExtendedTrustManagerInvoker))]
164+
privatestaticIX509TrustManagerFindX509TrustManager(ITrustManager[]trustManagers)
162165
{
163-
if(trustManagersisnull)
164-
returnnull;
165-
166166
foreach(vartrustManagerintrustManagers){
167167
if(trustManagerisIX509TrustManagertm)
168168
returntm;
169169
}
170170

171-
returnnull;
171+
thrownewInvalidOperationException($"Could not find {nameof(IX509TrustManager)} in {nameof(ITrustManager)} array.");
172172
}
173173

174-
privatestaticITrustManager[]ModifyTrustManagersArray(ITrustManager[]trustManagers,IX509TrustManager?original,IX509TrustManagerreplacement)
174+
privatestaticITrustManager[]ModifyTrustManagersArray(ITrustManager[]trustManagers,IX509TrustManageroriginal,IX509TrustManagerreplacement)
175175
{
176-
varmodifiedTrustManagersCount=originalisnull?trustManagers.Length+1:trustManagers.Length;
177-
varmodifiedTrustManagersArray=newITrustManager[modifiedTrustManagersCount];
178-
179-
modifiedTrustManagersArray[0]=replacement;
180-
intnextIndex=1;
176+
varmodifiedTrustManagersArray=newITrustManager[trustManagers.Length];
181177

182178
for(inti=0;i<trustManagers.Length;i++){
183179
if(trustManagers[i]==original){
184-
continue;
180+
modifiedTrustManagersArray[i]=replacement;
181+
}else{
182+
modifiedTrustManagersArray[i]=trustManagers[i];
185183
}
186184

187-
modifiedTrustManagersArray[nextIndex++]=trustManagers[i];
188185
}
189186

190187
returnmodifiedTrustManagersArray;

‎src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,40 @@ public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool
516516
}
517517
}
518518

519+
[Test]
520+
publicvoidPreserveIX509TrustManagerSubclasses([Values(true,false)]boolhasServerCertificateCustomValidationCallback)
521+
{
522+
varproj=newXamarinAndroidApplicationProject{IsRelease=true};
523+
proj.AddReferences("System.Net.Http");
524+
proj.MainActivity=proj.DefaultMainActivity.Replace(
525+
"base.OnCreate (bundle);",
526+
"base.OnCreate (bundle);\n"+
527+
(hasServerCertificateCustomValidationCallback
528+
?"var handler = new Xamarin.Android.Net.AndroidMessageHandler { ServerCertificateCustomValidationCallback = (message, certificate, chain, errors) => true };\n"
529+
:"var handler = new Xamarin.Android.Net.AndroidMessageHandler();\n")+
530+
"var client = new System.Net.Http.HttpClient (handler);\n"+
531+
"client.GetAsync (\"https://microsoft.com\").GetAwaiter ().GetResult ();");
532+
533+
using(varb=CreateApkBuilder()){
534+
Assert.IsTrue(b.Build(proj),"Build should have succeeded.");
535+
varassemblyPath=BuildTest.GetLinkedPath(b,true,"Mono.Android.dll");
536+
537+
using(varassembly=AssemblyDefinition.ReadAssembly(assemblyPath)){
538+
Assert.IsTrue(assembly!=null);
539+
540+
vartypes=new[]{"Javax.Net.Ssl.X509ExtendedTrustManager","Javax.Net.Ssl.IX509TrustManagerInvoker"};
541+
foreach(vartypeNameintypes){
542+
vartd=assembly.MainModule.GetType(typeName);
543+
if(hasServerCertificateCustomValidationCallback){
544+
Assert.IsNotNull(td,$"{typeName} shouldn't have been linked out");
545+
}else{
546+
Assert.IsNull(td,$"{typeName} should have been linked out");
547+
}
548+
}
549+
}
550+
}
551+
}
552+
519553
[Test]
520554
publicvoidDoNotErrorOnPerArchJavaTypeDuplicates([Values(true,false)]boolenableMarshalMethods)
521555
{

0 commit comments

Comments
 (0)