2012-08-23 14 views
13

Uso de Apache HttpClient 4.2.1. Utilizando el código copiado del ejemplo de acceso basada en la formaApache HTTPClient SSLPeerUnverifiedException

http://hc.apache.org/httpcomponents-client-ga/examples.html

consigo una excepción al acceder a un formulario de acceso protegido por SSL:

Getting library items from https://appserver.gtportalbase.com/agileBase/AppController.servlet?return=blank 
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated 
Closing http connection 
at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397) 
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128) 
at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572) 
at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180) 
at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294) 
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640) 
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479) 
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906) 
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805) 
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:784) 

El certificado por lo que yo puedo decir está muy bien (ver el URL antes del seguimiento de la pila), no caducado: los navegadores no se quejan.

He intentado importar el certificado en mi almacén de claves a la

How to handle invalid SSL certificates with Apache HttpClient?

sin cambios. Creo que puede crear un SSLContext personalizado para obligar a Java a ignorar el error, pero prefiero corregir la causa porque no quiero abrir ningún agujero de seguridad.

¿Alguna idea?

Respuesta

20

EDIT Me doy cuenta de que esta respuesta fue aceptada hace mucho tiempo y también ha sido actualizada 3 veces, pero fue (al menos en parte) incorrecta, así que aquí hay un poco más acerca de esta excepción. Disculpas por los inconvenientes.

javax.net.ssl.SSLPeerUnverifiedException: Peer no autenticado

Esto es por lo general una excepción lanzada cuando el servidor remoto no envió un certificado en absoluto. Sin embargo, hay un caso límite, que se encuentra cuando se utiliza Apache HTTP Client, debido a la forma en que se implementó en esta versión, y debido a la forma en que se implementa sun.security. ssl.SSLSocketImpl.getSession().

Al utilizar Apache HTTP Client, esta excepción también se producirá cuando el certificado remoto no sea de confianza, lo que arrojaría con más frecuencia "sun.security.validator.ValidatorException: PKIX path building failed".

Las razones por las que esto ocurre es porque Apache HTTP Client intenta obtener el SSLSession y el certificado de igual antes de hacer cualquier otra cosa.

Como recordatorio, hay 3 ways of initiating the handshake with an SSLSocket:

  • llamando startHandshake que comienza explícitamente apretones de manos, o
  • cualquier intento de leer o escribir datos de la aplicación de esta toma provoca un apretón de manos implícita o
  • una llamada a getSession intenta configurar una sesión si no hay una sesión válida en ese momento, y se realiza un handshake implícito.

Aquí hay 3 ejemplos, todos contra un anfitrión con un certificado que no es de confianza (usando javax.net.ssl.SSLSocketFactory, no el Apache uno).

Ejemplo 1:

SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); 
    SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 
      443); 
    sslSocket.startHandshake(); 

Esto arroja "javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed" (como se esperaba).

Ejemplo 2:

SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); 
    SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 
      443); 
    sslSocket.getInputStream().read(); 

Esto también lanza "javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed" (como se esperaba).

Ejemplo 3:

SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); 
    SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 
      443); 
    SSLSession sslSession = sslSocket.getSession(); 
    sslSession.getPeerCertificates(); 

Esto, sin embargo, tiros javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated.

Esta es la lógica implementada en Apache HTTP Client's AbstractVerifier utilizada por su org.apache.http.conn.ssl.SSLSocketFactory en la versión 4.2.1. Versiones posteriores make an explicit call to startHandshake(), según informes en issue HTTPCLIENT-1346.

En última instancia, parece provenir de la implementación de sun.security. ssl.SSLSocketImpl.getSession(), que coge potenciales IOException s lanzados al llamar startHandshake(false) (método interno), sin tirar más lejos. Esto podría ser un error, aunque esto no debería tener un impacto de seguridad masivo, ya que el SSLSocket seguirá cerrado de todos modos.

Ejemplo 4:

SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); 
    SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 
      443); 
    SSLSession sslSession = sslSocket.getSession(); 
    // sslSession.getPeerCertificates(); 
    sslSocket.getInputStream().read(); 

Afortunadamente, esto todavía va a tirar "javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed", cada vez que en realidad intenta utilizar que SSLSocket (sin escapatoria allí por conseguir la sesión sin obtener el certificado de pares).


Cómo solucionar este

Al igual que cualquier otro problema con los certificados que no son de confianza, que es una cuestión de asegurarse de que el almacén de confianza que está utilizando contiene las anclas de confianza necesarios (es decir, la CA certificados que emitieron la cadena que está intentando verificar, o posiblemente el certificado de servidor real para casos excepcionales).

Para solucionar esto, debe importar el certificado de CA (o posiblemente el certificado del servidor en sí) a su almacén de confianza. Usted puede hacer esto:

  • en su tienda JRE confianza, por lo general el archivo cacerts (que no es necesariamente la mejor, ya que afectaría todas las aplicaciones usando ese JRE),
  • en una copia local de su almacén de confianza (que puede configurar usando las opciones -Djavax.net.ssl.trustStore=...),
  • creando un SSLContext específico para esa conexión (como se describe en this answer). (Algunos sugieren usar un gestor de confianza que no hace nada, pero esto haría que su conexión vulnerables a los ataques MITM.)

respuesta inicial

javax.net.ssl.SSLPeerUnverifiedException: Peer no autenticado

Esto no tiene nada que ver con los certificados de confianza o que tenga que crear una costumbre SSLContext: esto se debe al hecho de que el servidor no está enviando ningún certificado en absoluto.

Este servidor no está visiblemente configurado para admitir TLS correctamente. Esta falla (no obtendrá un certificado remoto):

openssl s_client -tls1 -showcerts -connect appserver.gtportalbase.com:443 

Sin embargo, SSLv3 parece funcionar:

openssl s_client -ssl3 -showcerts -connect appserver.gtportalbase.com:443 

Si sabe quién dirige este servidor, valdría la pena contactarlos para solucionar este problema. Los servidores realmente deberían soportar TLSv1 al menos hoy en día.

Mientras tanto, una forma de solucionar este problema sería crear su propio org.apache.http.conn.ssl.SSLSocketFactory y utilizarlo para esta conexión con el cliente Apache Http.

Esta fábrica necesitaría crear un SSLSocket como de costumbre, use sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); antes de devolver ese socket, para deshabilitar TLS, que de lo contrario estaría habilitado de forma predeterminada.

+0

Hmm, obtengo un certificado remoto con ambos. Lo intentaré con algunos clientes diferentes –

+0

Desde OSX, ambos funcionan, desde Linux, solo ssl3. Se verá en la configuración del servidor. –

+0

Esto podría tener algo que ver con las suites de cifrado. En su cliente OSX, '' openssl Ciphers DEFAULT' tiene las mismas suites de cifrado que en su cliente Linux? Algunos que estarían en la lista OSX pero no en Linux? – Bruno

0

Cree un contexto personalizado para que pueda registrar por qué el certificado no es válido. O simplemente depurarlo.

Cuestiones relacionadas