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
2628public ITrustManager [ ] ReplaceX509TrustManager ( ITrustManager [ ] ? trustManagers , HttpRequestMessage requestMessage )
2729{
28- var originalX509TrustManager = FindX509TrustManager ( trustManagers ) ;
30+ var originalX509TrustManager = FindX509TrustManager ( trustManagers , out int originalTrustManagerIndex ) ;
2931var trustManagerWithCallback = new TrustManager ( originalX509TrustManager , requestMessage , Callback ) ;
30- return ModifyTrustManagersArray ( trustManagers , original : originalX509TrustManager , replacement : trustManagerWithCallback ) ;
32+ return ModifyTrustManagersArray ( trustManagers , originalTrustManagerIndex , trustManagerWithCallback ) ;
3133}
3234
3335private 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
171186throw 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{
176197var modifiedTrustManagersArray = new ITrustManager [ trustManagers . Length ] ;
177198
178199for ( int i = 0 ; i < trustManagers . Length ; i ++ ) {
179- if ( trustManagers [ i ] == original ) {
200+ if ( i == originalTrustManagerIndex ) {
180201modifiedTrustManagersArray [ i ] = replacement ;
181202} else {
182203modifiedTrustManagersArray [ i ] = trustManagers [ i ] ;
183204}
184-
185205}
186206
187207return modifiedTrustManagersArray ;
0 commit comments