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.
Hmm, obtengo un certificado remoto con ambos. Lo intentaré con algunos clientes diferentes –
Desde OSX, ambos funcionan, desde Linux, solo ssl3. Se verá en la configuración del servidor. –
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