Довіряйте всім сертифікатам okHttp


111

Для тестування я намагаюся додати фабрику сокетів до свого клієнта okHttp, який довіряє всім під час встановлення проксі. Це робилося вже багато разів, але моє впровадження довірчої фабрики розеток чогось не вистачає:

class TrustEveryoneManager implements X509TrustManager {
    @Override
    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }

    @Override
    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }

    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}
OkHttpClient client = new OkHttpClient();

final InetAddress ipAddress = InetAddress.getByName("XX.XXX.XXX.XXX"); // some IP
client.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ipAddress, 8888)));

SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = new TrustManager[]{new TrustEveryoneManager()};
sslContext.init(null, trustManagers, null);
client.setSslSocketFactory(sslContext.getSocketFactory);

Жоден запит не надсилається з мого додатка, і жодні винятки не реєструються, тому здається, що воно не вдається мовчки в межах OkHttp. При подальшому розслідуванні, здається, що у OkHttp пролунає виняток, Connection.upgradeToTls()коли рукостискання вимушене. Виняток, який мені дають:javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

Наступний код створює функцію, SSLContextяка працює як шарм у створенні SSLSocketFactory, який не кидає жодних винятків:

protected SSLContext getTrustingSslContext() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
    final SSLContextBuilder trustingSSLContextBuilder = SSLContexts.custom()
            .loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true; // Accepts any ssl cert whether valid or not.
                }
            });
    return trustingSSLContextBuilder.build();
}

Проблема полягає в тому, що я намагаюся повністю видалити всі залежності Apache HttpClient зі свого додатка. Код, що лежить в основі Apache HttpClient, SSLContextздається достатньо простим, але я, очевидно, чогось не вистачаю, оскільки не можу налаштувати своє, SSLContextщоб відповідати цьому.

Чи зможе хто-небудь створити реалізацію SSLContext, яка робить те, що я хотів, не використовуючи Apache HttpClient?


2
"Для тестування я намагаюся додати фабрику сокетів до свого клієнта okHttp, який все довіряє" - що змушує вас думати, що це в першу чергу хороша ідея?
CommonsWare

1
Викликається лише в мережу, якій я повністю довіряю.
seato

Це спричиняє:java.net.SocketTimeoutException: Read timed out
ІгорГанапольський

Відповіді:


250

На всякий випадок, коли хтось потрапить сюди, (єдине) рішення, яке працювало на мене, створює OkHttpClientподібне, пояснене тут .

Ось код:

private static OkHttpClient getUnsafeOkHttpClient() {
  try {
    // Create a trust manager that does not validate certificate chains
    final TrustManager[] trustAllCerts = new TrustManager[] {
        new X509TrustManager() {
          @Override
          public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
          }

          @Override
          public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
          }

          @Override
          public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[]{};
          }
        }
    };

    // Install the all-trusting trust manager
    final SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
    // Create an ssl socket factory with our all-trusting manager
    final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]);
    builder.hostnameVerifier(new HostnameVerifier() {
      @Override
      public boolean verify(String hostname, SSLSession session) {
        return true;
      }
    });

    OkHttpClient okHttpClient = builder.build();
    return okHttpClient;
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

15
Чому SSLі ні TLS?
ІгорГанапольський

1
Дякую за це! Документації щодо модернізації (використовує okhttp) не вистачає, подібний зразок коду економить мені стільки разів знову і знову.
Warpzit

8
Зауважте, цей підхід більше не працює з поточними версіями OkHttp. З 3.1.1 він здається повністю розбитим. З 3.1.2 далі X509TrustManager.getAcceptedIssuers()повинен повернути порожній масив замість null. Для отримання додаткової інформації дивіться цю комісію (прокрутіть униз і перегляньте нотатки під RealTrustRootIndex.java).
jbxbergdev

12
Я спробував це, але все одно отримую Handshake failedвиняток. Будь-які пропозиції?
Естебан


13

Наступний метод застарілий

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Розглянемо її оновлення

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)

13

Оновіть OkHttp 3.0, getAcceptedIssuers()функція повинна повертати порожній масив замість null.


2
@Override public X509Certificate [] getAcceptedIssuers () {повернути новий X509Certificate [] {}; // StackOverflow}
Ервін Чжан

11

SSLSocketFactory не виставляє свого X509TrustManager, що є полем, потрібному OkHttp для створення чистого ланцюга сертифікатів. Цей метод замість цього повинен використовувати відображення для вилучення менеджера довіри. Програми повинні віддавати перевагу виклику sslSocketFactory (SSLSocketFactory, X509TrustManager), що дозволяє уникнути такого відображення.

Джерело: Документація OkHttp

OkHttpClient.Builder builder = new OkHttpClient.Builder();

builder.sslSocketFactory(sslContext.getSocketFactory(),
    new X509TrustManager() {
        @Override
        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[]{};
        }
    });

Це не працює для самопідписаних сертифікатів. Викликає 401 Несанкціоновану помилку.
ІгорГанапольський

5
Звідки береться sslContext?
Anonsage

3
Як ви ініціалізуєте sslContext?
kiltek

9

Це рішення Sonxurxo в Котліні, якщо комусь це потрібно.

private fun getUnsafeOkHttpClient(): OkHttpClient {
    // Create a trust manager that does not validate certificate chains
    val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
        override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        }

        override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
        }

        override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
    })

    // Install the all-trusting trust manager
    val sslContext = SSLContext.getInstance("SSL")
    sslContext.init(null, trustAllCerts, java.security.SecureRandom())
    // Create an ssl socket factory with our all-trusting manager
    val sslSocketFactory = sslContext.socketFactory

    return OkHttpClient.Builder()
        .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
        .hostnameVerifier { _, _ -> true }.build()
}

7

Я зробив функцію розширення для Котліна. Вставте його куди завгодно та імпортуйте під час створення OkHttpClient.

fun OkHttpClient.Builder.ignoreAllSSLErrors(): OkHttpClient.Builder {
    val naiveTrustManager = object : X509TrustManager {
        override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
        override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
        override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
    }

    val insecureSocketFactory = SSLContext.getInstance("TLSv1.2").apply {
        val trustAllCerts = arrayOf<TrustManager>(naiveTrustManager)
        init(null, trustAllCerts, SecureRandom())
    }.socketFactory

    sslSocketFactory(insecureSocketFactory, naiveTrustManager)
    hostnameVerifier(HostnameVerifier { _, _ -> true })
    return this
}

використовуйте його так:

val okHttpClient = OkHttpClient.Builder().apply {
    // ...
    if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
        ignoreAllSSLErrors()
    //...
}.build()

Ви можете використовувати його як це: OkHttpClient.Builder () .ignoreAllSSLErrors () .connectTimeout (api.timeout, TimeUnit.SECONDS) .writeTimeout (api.timeout, TimeUnit.SECONDS) і т.д., це шаблон будівельник в кінці кінців
Емануель Moecklin

1

Це рішення Scala, якщо комусь це потрібно

def anUnsafeOkHttpClient(): OkHttpClient = {
val manager: TrustManager =
  new X509TrustManager() {
    override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String) = {}

    override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String) = {}

    override def getAcceptedIssuers = Seq.empty[X509Certificate].toArray
  }
val trustAllCertificates =  Seq(manager).toArray

val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCertificates, new java.security.SecureRandom())
val sslSocketFactory = sslContext.getSocketFactory()
val okBuilder = new OkHttpClient.Builder()
okBuilder.sslSocketFactory(sslSocketFactory, trustAllCertificates(0).asInstanceOf[X509TrustManager])
okBuilder.hostnameVerifier(new NoopHostnameVerifier)
okBuilder.build()

}


-12

Ніколи не слід шукати, щоб замінити перевірку сертифікату в коді! Якщо вам потрібно зробити тестування, використовуйте внутрішній / тестовий ЦС та встановіть кореневий сертифікат CA на пристрій чи емулятор. Ви можете використовувати BurpSuite або Charles Proxy, якщо не знаєте, як налаштувати CA.


37
О, давай. Що робити, якщо ви працюєте в компанії, і вони змушують вас підключитися до їх сервера HTTPS через VPN. Як ти змоделюєш це в тесті?
ІгорГанапольський

5
Я це найчистіша свиняча мийка. Ми збираємо це лише у тестових складах, тому небезпеки немає.
Адам
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.