CertificateException, выброшенное в пользовательском X509TrustManager, не может быть обработано

Мое приложение Xamarin.Android отправляет запросы веб-службе, работающей по протоколу https. Сертификат может быть самоподписанным, поэтому мне нужно выполнить проверку сертификата самостоятельно. Я настроил свой собственный TrustManager. Но из того, что я нашел, метод CheckServerTrusted должен бросить CertificateException если сертификат не является доверенным. Если я это сделаю, то мое приложение вылетает. CertificateException нигде не обрабатывается.

var aUrl = new URL(url);
var connection = (HttpURLConnection)aUrl.OpenConnection();

try
{
    var httpsConn = connection as HttpsURLConnection;
    if (httpsConn != null)
    {
        var tm = new CustomX509TrustManager();
        var sslCtx = SSLContext.GetInstance("TLSv1.2");
        sslCtx.Init(null, new ITrustManager[] { tm }, null);

        httpsConn.HostnameVerifier = new CustomHostnameVerifier();
        httpsConn.SSLSocketFactory = sslCtx.SocketFactory;
    }

    System.Diagnostics.Debug.WriteLine("Connecting ...");

    //connection.Connect();
    await connection.ConnectAsync();

    System.Diagnostics.Debug.WriteLine("Never reached");
}
catch (Exception ex)
{
    System.Diagnostics.Debug.WriteLine($"Exception!\n{ex}");
}
finally
{
    System.Diagnostics.Debug.WriteLine("Never reached");
}
System.Diagnostics.Debug.WriteLine("Never reached");

CustomX509TrustManager так просто, как может быть:

class CustomX509TrustManager : Java.Lang.Object, IX509TrustManager
{
    public void CheckClientTrusted(Java.Security.Cert.X509Certificate[] chain, string authType)
    {
    }

    public void CheckServerTrusted(Java.Security.Cert.X509Certificate[] certificates, string authType)
    {
        throw new Java.Security.Cert.CertificateException();
    }

    Java.Security.Cert.X509Certificate[] IX509TrustManager.GetAcceptedIssuers()
    {
        return new Java.Security.Cert.X509Certificate[0];
    }
}

В connection.ConnectAsync() происходит связь с удаленным сервером, и сертификат проверяется в CustomX509TrustManager.CheckServerTrusted. Это создает исключение CertificateException. Но это исключение не отражено в моем методе try/catch в вызывающем методе. Приложение вылетает с этой трассировкой стека:

UNHANDLED EXCEPTION:
Java.Security.Cert.CertificateException: Exception of type 'Java.Security.Cert.CertificateException' was thrown.
  at SimpleWebRequest.MainActivity+CustomX509TrustManager.CheckServerTrusted (Java.Security.Cert.X509Certificate[] certificates, System.String authType) [0x00001] in D:\Temp\SimpleWebRequest\SimpleWebRequest\MainActivity.cs:162 
  at Javax.Net.Ssl.IX509TrustManagerInvoker.n_CheckServerTrusted_arrayLjava_security_cert_X509Certificate_Ljava_lang_String_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_chain, System.IntPtr native_authType) [0x00028] in /Users/builder/data/lanes/4009/9578cdcd/source/monodroid/src/Mono.Android/platforms/android-25/src/generated/Javax.Net.Ssl.IX509TrustManager.cs:129 
  at (wrapper dynamic-method) System.Object:5109f213-8693-4899-a3db-70ed54baf307 (intptr,intptr,intptr,intptr)
  --- End of managed Java.Security.Cert.CertificateException stack trace ---

Я попробовал несколько вещей со странными результатами.

Если я использую connection.Connect() вместо ConnectAsyncтогда catch блок запущен, но finally Блок не является, а также код после этого больше не работает. С ConnectAsync, catch Блок тоже не запускается.

Если я получу TrustManager по умолчанию и установите его в SSLSocketFactory как я сделал с моим, то это работает. Сертификаты отклоняются, и исключение может быть перехвачено. Так что я не думаю, что у меня есть только улов на неправильной нити.

Если я использую свой собственный TrustManager, но я делегирую вызов CheckServerTrusted к TrustManager по умолчанию, то он терпит неудачу, как если бы я сам выбрасывал исключение.

Так что я думаю, что я делаю что-то не так в моем CustomX509TrustManager или как я передаю это SSLSocketFactory,

К сожалению, при поиске пользовательских TrustMangager большинство людей просто хотят игнорировать все ошибки сертификата. Это работает для меня, проблема возникает, если я проверяю сертификат и хочу отклонить его.

1 ответ

У меня такая же проблема. После нескольких часов попыток и ошибок я обнаружил, что исключение не обрабатывается только в режиме отладки. Если приложение создано в режиме выпуска, перехватывается исключение.

Но из того, что я нашел, метод CheckServerTrusted должен генерировать CertificateException, если сертификат не является доверенным. Если я это сделаю, то мое приложение вылетает. CertificateException нигде не обрабатывается.

Я думаю, что проблема именно в том, как вы справляетесь с CheckServerTrusted метод, вы кодировали так:

public void CheckServerTrusted(Java.Security.Cert.X509Certificate[] certificates, string authType)
{
    throw new Java.Security.Cert.CertificateException();
}

Это всегда новый необработанный бросок CertificateException Когда ваш CheckServerTrusted метод называется.

Я думаю, что вы можете проверить этот блог: самоподписанные сертификаты и Xamarin.Android. Из того, что я вижу, CheckServerTrusted Этот метод можно использовать для переноса диспетчера доверия в другой диспетчер доверия, чтобы разрешить проверку других сертификатов. Когда вы настраиваете этот метод, вам не нужно выбрасывать такое исключение.

Другие вопросы по тегам