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

В целях тестирования я пытаюсь добавить сокет factory в мой okHttp-клиент, который доверяет всем, пока установлен прокси-сервер. Это было сделано много раз, но моя реализация доверенного сокета factory, кажется, что-то не хватает:

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?

Ответ 1

На всякий случай, когда кто-то падает сюда, решение (только), которое сработало для меня, создает 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);
  }
}

Ответ 2

Следующий метод устарел

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Рассмотрите возможность обновления до

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)

Ответ 3

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

Ответ 4

  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[]{};
        }
    });

Ответ 5

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

Ответ 6

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

}

Ответ 7

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