Доверять всем сертификатам с помощью 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. При дальнейшем исследовании выяснилось, что Connection.upgradeToTls()при принудительном рукопожатии в okHttp поглощается исключение . Исключение, которое мне дают: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
IgorGanapolsky

Ответы:


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 07

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 () {return new 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 Несанкционированный доступ.
IgorGanapolsky 05

5
Откуда sslContextвзялось?
Anonsage

3
Как инициализировать sslContext?
kiltek

10

Это решение 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

Вы никогда не должны пытаться переопределить проверку сертификата в коде! Если вам нужно провести тестирование, используйте внутренний / тестовый ЦС и установите корневой сертификат ЦС на устройство или эмулятор. Вы можете использовать BurpSuite или Charles Proxy, если не знаете, как настроить CA.


37
Да ладно тебе. Что делать, если вы работаете в компании, и они заставляют вас подключаться к их серверу HTTPS через VPN. Как вы собираетесь смоделировать это в тесте?
Игорь Ганапольский

5
Я. это чистейшая фигня. Мы компилируем это только в тестовых сборках, поэтому опасности нет.
Адам
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.