55using System . Net . Security ;
66using System . Security . Cryptography . X509Certificates ;
77
8+ using Android . OS ;
9+ using Android . Runtime ;
810using Javax . Net . Ssl ;
911
1012using JavaCertificateException = Java . Security . Cert . CertificateException ;
@@ -25,9 +27,9 @@ public ServerCertificateCustomValidator (Func<HttpRequestMessage, X509Certificat
2527
2628 public ITrustManager [ ] ReplaceX509TrustManager ( ITrustManager [ ] ? trustManagers , HttpRequestMessage requestMessage )
2729 {
28- var originalX509TrustManager = FindX509TrustManager ( trustManagers ) ;
30+ var originalX509TrustManager = FindX509TrustManager ( trustManagers , out int originalTrustManagerIndex ) ;
2931 var trustManagerWithCallback = new TrustManager ( originalX509TrustManager , requestMessage , Callback ) ;
30- return ModifyTrustManagersArray ( trustManagers , original : originalX509TrustManager , replacement : trustManagerWithCallback ) ;
32+ return ModifyTrustManagersArray ( trustManagers , originalTrustManagerIndex , trustManagerWithCallback ) ;
3133 }
3234
3335 private sealed class TrustManager : Java . Lang . Object , IX509TrustManager
@@ -161,27 +163,45 @@ private sealed class AlwaysAcceptingHostnameVerifier : Java.Lang.Object, IHostna
161163
162164 [ DynamicDependency ( nameof ( IX509TrustManager . CheckServerTrusted ) , typeof ( IX509TrustManagerInvoker ) ) ]
163165 [ DynamicDependency ( nameof ( IX509TrustManager . CheckServerTrusted ) , typeof ( X509ExtendedTrustManagerInvoker ) ) ]
164- private static IX509TrustManager FindX509TrustManager ( ITrustManager [ ] trustManagers )
166+ private static IX509TrustManager FindX509TrustManager ( ITrustManager [ ] trustManagers , out int index )
165167 {
166- foreach ( var trustManager in trustManagers ) {
167- if ( trustManager is IX509TrustManager tm )
168- return tm ;
168+ for ( int i = 0 ; i < trustManagers . Length ; i ++ ) {
169+ var trustManager = trustManagers [ i ] ;
170+ if ( trustManager is IX509TrustManager x509TrustManager ) {
171+ index = i ;
172+ return x509TrustManager ;
173+ }
174+
175+ // On API 21-23, the default Java trust manager is TrustManagerImpl from Conscrypt. The class implements X509TrustManager
176+ // but the .NET pattern matching will fail in this case and we need to cast it explicitly.
177+ int apiLevel = ( int ) Build . VERSION . SdkInt ;
178+ if ( apiLevel <= 23 ) {
179+ if ( IsTrustManagerImpl ( trustManager ) ) {
180+ index = i ;
181+ return trustManager . JavaCast < IX509TrustManager > ( ) ;
182+ }
183+ }
169184 }
170185
171186 throw new InvalidOperationException ( $ "Could not find { nameof ( IX509TrustManager ) } in { nameof ( ITrustManager ) } array.") ;
187+
188+ static bool IsTrustManagerImpl ( ITrustManager trustManager )
189+ {
190+ var javaClassName = JNIEnv . GetClassNameFromInstance ( trustManager . Handle ) ;
191+ return javaClassName . Equals ( "com/android/org/conscrypt/TrustManagerImpl" , StringComparison . Ordinal ) ;
192+ }
172193 }
173194
174- private static ITrustManager [ ] ModifyTrustManagersArray ( ITrustManager [ ] trustManagers , IX509TrustManager original , IX509TrustManager replacement )
195+ private static ITrustManager [ ] ModifyTrustManagersArray ( ITrustManager [ ] trustManagers , int originalTrustManagerIndex , IX509TrustManager replacement )
175196 {
176197 var modifiedTrustManagersArray = new ITrustManager [ trustManagers . Length ] ;
177198
178199 for ( int i = 0 ; i < trustManagers . Length ; i ++ ) {
179- if ( trustManagers [ i ] == original ) {
200+ if ( i == originalTrustManagerIndex ) {
180201 modifiedTrustManagersArray [ i ] = replacement ;
181202 } else {
182203 modifiedTrustManagersArray [ i ] = trustManagers [ i ] ;
183204 }
184-
185205 }
186206
187207 return modifiedTrustManagersArray ;
0 commit comments