Confiar en todos los certificados con okHttp

111

Para fines de prueba, estoy tratando de agregar una fábrica de sockets a mi cliente okHttp que confía en todo mientras se establece un proxy. Esto se ha hecho muchas veces, pero mi implementación de una fábrica de sockets de confianza parece faltar algo:

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);

No se envían solicitudes desde mi aplicación y no se registran excepciones, por lo que parece que está fallando silenciosamente dentro de okHttp. Tras una mayor investigación, parece que hay una excepción que se está tragando en okHttp Connection.upgradeToTls()cuando se fuerza el apretón de manos. La excepción que me están dando es:javax.net.ssl.SSLException: SSL handshake terminated: ssl=0x74b522b0: SSL_ERROR_ZERO_RETURN occurred. You should never see this.

El siguiente código produce un SSLContextque funciona como un encanto para crear un SSLSocketFactory que no arroja ninguna excepción:

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();
}

El problema es que estoy tratando de eliminar todas las dependencias de Apache HttpClient de mi aplicación por completo. El código subyacente con Apache HttpClient para producir el SSLContextparece bastante sencillo, pero obviamente me falta algo ya que no puedo configurar mi SSLContextpara que coincida con esto.

¿Alguien podría producir una implementación SSLContext que haga lo que me gustaría sin usar Apache HttpClient?

seato
fuente
2
"Para propósitos de prueba, estoy tratando de agregar una fábrica de sockets a mi cliente okHttp que confíe en todo". ¿Qué te hace pensar que esta es una buena idea en primer lugar?
CommonsWare
1
Solo se llama en una red en la que confío por completo.
seato
Esto provoca unjava.net.SocketTimeoutException: Read timed out
IgorGanapolsky

Respuestas:

250

En caso de que alguien caiga aquí, la (única) solución que funcionó para mí es crear lo OkHttpClientque se explica aquí .

Aquí está el código:

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);
  }
}
sonxurxo
fuente
15
¿Por qué SSLy no TLS?
IgorGanapolsky
1
¡Muchas gracias por esto! Falta la documentación sobre la actualización (usa okhttp), este tipo de ejemplos de código me ahorra mucho tiempo una y otra vez.
Warpzit
8
Tenga en cuenta que este enfoque ya no funciona con las versiones actuales de OkHttp. Con 3.1.1 parece completamente roto. Desde 3.1.2 en adelante, X509TrustManager.getAcceptedIssuers()debe devolver una matriz vacía en lugar de null. Para obtener más información, consulte esta confirmación (desplácese hacia abajo y vea las notas en RealTrustRootIndex.java).
jbxbergdev
12
He intentado esto, pero todavía obtengo una Handshake failedexcepción. ¿Alguna sugerencia?
Esteban
1
ejemplo de OkHttpClient: github.com/square/okhttp/blob/master/samples/guide/src/main/…
ilyamuromets
13

El siguiente método está obsoleto

sslSocketFactory(SSLSocketFactory sslSocketFactory)

Considere actualizarlo a

sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)
Jakub N
fuente
13

Actualice OkHttp 3.0, la getAcceptedIssuers()función debe devolver una matriz vacía en lugar de null.

Ervin Zhang
fuente
2
@Override public X509Certificate [] getAcceptedIssuers () {return new X509Certificate [] {}; // StackOverflow}
Ervin Zhang
11

SSLSocketFactory no expone su X509TrustManager, que es un campo que OkHttp necesita para construir una cadena de certificados limpia. En cambio, este método debe utilizar la reflexión para extraer el administrador de confianza. Las aplicaciones deberían preferir llamar a sslSocketFactory (SSLSocketFactory, X509TrustManager), lo que evita dicho reflejo.

Fuente: documentación 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[]{};
        }
    });
Bandara de Tharindu
fuente
Esto no funciona para certificados autofirmados. Provoca un error 401 no autorizado .
IgorGanapolsky
5
¿De donde sslContextviene el?
Anonsage
3
¿Cómo se inicializa sslContext?
kiltek
10

Esta es la solución de sonxurxo en Kotlin, si alguien la necesita.

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()
}
doodla
fuente
7

Hice una función de extensión para Kotlin. Péguelo donde quiera e impórtelo mientras crea 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
}

úsalo así:

val okHttpClient = OkHttpClient.Builder().apply {
    // ...
    if (BuildConfig.DEBUG) //if it is a debug build ignore ssl errors
        ignoreAllSSLErrors()
    //...
}.build()
George Shalvashvili
fuente
De hecho, puede usarlo así: OkHttpClient.Builder () .ignoreAllSSLErrors () .connectTimeout (api.timeout, TimeUnit.SECONDS) .writeTimeout (api.timeout, TimeUnit.SECONDS) etc., después de todo, es un patrón de constructor
Emanuel Moecklin
1

Esta es la solución de Scala si alguien la necesita

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()

}

netta
fuente
-12

¡Nunca debe buscar anular la validación del certificado en el código! Si necesita realizar una prueba, utilice una CA interna / de prueba e instale el certificado raíz de la CA en el dispositivo o emulador. Puede usar BurpSuite o Charles Proxy si no sabe cómo configurar una CA.

Tony Trummer
fuente
37
Oh vamos. ¿Qué pasa si estás trabajando para una empresa y te están obligando a conectarte a su servidor HTTPS a través de VPN? ¿Cómo vas a simular eso en una prueba?
IgorGanapolsky
5
Sí, este es el lavado de cerdo más puro. Solo compilamos esto en compilaciones de prueba, por lo que no hay peligro.
Adam