Acepte el certificado ssl autofirmado del servidor en el cliente Java

Parece una pregunta estándar, pero no pude encontrar direcciones claras en ninguna parte.

Tengo un código de Java que intenta conectarme a un servidor con un certificado probablemente autofirmado (o caducado). El código informa el siguiente error:

[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught when processing request: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 

Según lo entiendo, tengo que usar keytool y decirle a java que está bien permitir esta conexión.

Todas las instrucciones para solucionar este problema asumen que soy completamente competente con keytool, como

generar clave privada para el servidor e importarla en el almacén de claves

¿Hay alguien que pueda publicar instrucciones detalladas?

Estoy ejecutando Unix, por lo que bash script sería lo mejor.

No estoy seguro si es importante, pero el código se ejecutó en jboss.

Básicamente tiene dos opciones aquí: agregue el certificado autofirmado a su almacén de confianza de JVM o configure su cliente para

Opción 1

Exporte el certificado de su navegador e impórtelo en su almacén de confianza de JVM (para establecer una cadena de confianza):

 \bin\keytool -import -v -trustcacerts -alias server-alias -file server.cer -keystore cacerts.jks -keypass changeit -storepass changeit 

opcion 2

Deshabilitar Validación de Certificado:

 // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) { } } }; // Install the all-trusting trust manager try { SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (GeneralSecurityException e) { } // Now you can access an https URL without having the certificate in the truststore try { URL url = new URL("https://hostname/index.html"); } catch (MalformedURLException e) { } 

Tenga en cuenta que no recomiendo la Opción n. ° 2 en absoluto . Deshabilitar el administrador de confianza derrota algunas partes de SSL y te hace vulnerable al hombre en los ataques intermedios. Prefiera la Opción n. ° 1 o, mejor aún, haga que el servidor use un certificado “real” firmado por una AC conocida.

Perseguí este problema a un proveedor de certificados que no forma parte de los hosts confiables de JVM a partir de JDK 8u74 . El proveedor es http://www.identrust.com , pero ese no era el dominio al que estaba tratando de conectarme. Ese dominio obtuvo su certificado de este proveedor. Consulte ¿Confiará la cobertura de raíz cruzada por la lista predeterminada en el JDK / JRE? – lee un par de entradas. También vea Qué navegadores y sistemas operativos son compatibles con Let’s Encrypt .

Entonces, para conectarme al dominio que me interesaba, que tenía un certificado emitido por identrust.com , hice los siguientes pasos. Básicamente, tenía que obtener el certificado identrust.com ( DST Root CA X3 ) para que la JVM confiara en él. Pude hacer eso usando Apache HttpComponents 4.5 así:

1: Obtenga el certificado de indettrust en las instrucciones de descarga de la cadena de certificados . Haga clic en el enlace DST Root CA X3 .

2: guarde la cadena en un archivo llamado “DST Root CA X3.pem”. Asegúrese de agregar las líneas “—– BEGIN CERTIFICATE —–” y “—– END CERTIFICATE —–” en el archivo al principio y al final.

3: crea un archivo java keystore, cacerts.jks con el siguiente comando:

 keytool -import -v -trustcacerts -alias IdenTrust -keypass yourpassword -file dst_root_ca_x3.pem -keystore cacerts.jks -storepass yourpassword 

4: copie el almacén de claves cacerts.jks resultante en el directorio de recursos de su aplicación java / (maven).

5: utilice el siguiente código para cargar este archivo y adjuntarlo al Apache 4.5 HttpClient. Esto resolverá el problema para todos los dominios que tengan certificados emitidos desde indetrust.com oracle incluye el certificado en el almacén de claves predeterminado de JRE.

 SSLContext sslcontext = SSLContexts.custom() .loadTrustMaterial(new File(CalRestClient.class.getResource("/cacerts.jks").getFile()), "yourpasword".toCharArray(), new TrustSelfSignedStrategy()) .build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf) .build(); 

Cuando se construye el proyecto, los cacerts.jks se copiarán en el classpath y se cargarán desde allí. En este momento, no probé contra otros sitios ssl, pero si el código anterior “encadena” en este certificado, entonces también funcionarán, pero nuevamente, no lo sé.

Referencia: contexto SSL personalizado y ¿Cómo acepto un certificado autofirmado con Java HttpsURLConnection?

Apache HttpClient 4.5 admite la aceptación de certificados autofirmados:

 SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(new TrustSelfSignedStrategy()) .build(); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); Registry reg = RegistryBuilder.create() .register("https", socketFactory) .build(); HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg); CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build(); HttpGet httpGet = new HttpGet(url); CloseableHttpResponse sslResponse = httpClient.execute(httpGet); 

Esto construye una fábrica de sockets SSL que usará TrustSelfSignedStrategy , lo registra con un administrador de conexión personalizado y luego hace un HTTP GET usando ese administrador de conexión.

Estoy de acuerdo con quienes dicen “no hagas esto en producción”, sin embargo, hay casos de uso para aceptar certificados autofirmados fuera de la producción; los utilizamos en pruebas de integración automatizadas, de modo que estamos usando SSL (como en producción) incluso cuando no se está ejecutando en el hardware de producción.

En lugar de establecer la fábrica de socket predeterminada (que IMO es algo malo), esto afectará la conexión actual en lugar de cada conexión SSL que intente abrir:

 URLConnection connection = url.openConnection(); // JMD - this is a better way to do it that doesn't override the default SSL factory. if (connection instanceof HttpsURLConnection) { HttpsURLConnection conHttps = (HttpsURLConnection) connection; // Set up a Trust all manager TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) { } } }; // Get a new SSL context SSLContext sc = SSLContext.getInstance("TLSv1.2"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); // Set our connection to use this SSL context, with the "Trust all" manager in place. conHttps.setSSLSocketFactory(sc.getSocketFactory()); // Also force it to trust all hosts HostnameVerifier allHostsValid = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; // and set the hostname verifier. conHttps.setHostnameVerifier(allHostsValid); } InputStream stream = connection.getInputStream(); 

Confíe en todos los certificados SSL: puede omitir SSL si desea realizar una prueba en el servidor de prueba. Pero no use este código para producción.

 public static class NukeSSLCerts { protected static final String TAG = "NukeSSLCerts"; public static void nuke() { try { TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { X509Certificate[] myTrustedAnchors = new X509Certificate[0]; return myTrustedAnchors; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) {} @Override public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String arg0, SSLSession arg1) { return true; } }); } catch (Exception e) { } } 

}

Llame a esta función en la función Crear () en Actividad o en su Clase de aplicación.

 NukeSSLCerts.nuke(); 

Esto se puede usar para Volley en Android.

Si ‘ellos’ están usando un certificado autofirmado, depende de ellos tomar los pasos necesarios para hacer que su servidor sea utilizable. Específicamente eso significa proporcionarle su certificado fuera de línea de manera confiable. Entonces haz que hagan eso. A continuación, importe eso en su almacén de confianza utilizando la herramienta de claves como se describe en la Guía de referencia de JSSE. Ni siquiera pienses en el inseguro TrustManager publicado aquí.

EDITAR Para beneficio de los diecisiete (!) Downvoters, y de numerosos comentaristas a continuación, que claramente no han leído realmente lo que he escrito aquí, esto no es una amenaza contra los certificados autofirmados. No hay nada de malo con los certificados autofirmados cuando se implementan correctamente. Sin embargo, la forma correcta de implementarlos es tener el certificado entregado de forma segura a través de un proceso fuera de línea, en lugar de hacerlo a través del canal no autenticado que se usará para autenticarse. Sin duda, esto es obvio? Ciertamente, es evidente para todas las organizaciones de seguridad con las que he trabajado, desde bancos con miles de sucursales hasta mis propias compañías. La “solución” basada en el código del lado del cliente de confiar en todos los certificados, incluidos los certificados autofirmados firmados por absolutamente nadie, o cualquier entidad arbitraria que se establezca como CA, ipso facto no es segura. Es solo jugar en seguridad. No tiene sentido. Está teniendo una conversación privada, a prueba de manipulaciones, a prueba de respuestas, a prueba de inyecciones con … alguien. Nadie. Un hombre en el medio. Un imitador. Nadie. También puedes usar texto simple.