Pregunta

Parece una pregunta estándar, pero no pude encontrar indicaciones claras en cualquier lugar.

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

[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

Como lo entiendo, tengo que usar herramienta de claves y java decir que es en Aceptar para permitir esta conexión.

Todas las instrucciones para solucionar este problema asume que soy plenamente competentes con herramienta de claves, tales como

generar la clave privada para el servidor y la importación en almacén de claves

¿Hay alguien que podría publicar instrucciones detalladas?

Estoy corriendo Unix, por lo escritura del golpe sería lo mejor.

No está seguro de si es importante, pero el código ejecutado en JBoss.

¿Fue útil?

Solución

Usted tiene básicamente dos opciones: añadir el certificado autofirmado para su almacén de confianza JVM o configurar el cliente para

Opción 1

Exportar el certificado de su navegador e importarlos en su almacén de confianza JVM (para establecer una cadena de confianza):

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

Opción 2

Desactivar validación del 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 # 2 en absoluto . La desactivación del gestor de confianza vence algunas partes de SSL y te hace vulnerable al hombre en el centro. Prefiero la opción # 1 o, mejor aún, tener el uso del servidor un certificado "real" firmado por una CA conocida

Otros consejos

Me persiguió por este problema a un proveedor certificado que no es parte de la JVM por defecto de confianza como anfitriones de JDK 8u74. El proveedor es www.identrust.com , pero que no era el dominio que estaba tratando de conectarse. Ese dominio había conseguido su certificado de este proveedor. Ver ¿la cubierta de la raíz cruz confianza por la lista por defecto en el JDK / JRE - lea abajo un par de entradas. También vea Qué navegadores y sistemas operativos son compatibles con Let de Cifrar .

Por lo tanto, con el fin de conectar con el dominio que estaba interesado en, que tenía un certificado emitido desde identrust.com hice los siguientes pasos. Básicamente, que tenía que conseguir el certificado identrust.com (DST Root CA X3) de fiar por la JVM. Yo era capaz de hacer que el uso de Apache 4.5 HttpComponents este modo:

1: Obtener el certificado de indettrust en Certificado de Cadena Instrucciones de descarga . Haga clic en el enlace DST CA raíz X3 .

2: Guardar la cadena en un archivo llamado "horario de verano entidad emisora ??raíz X3.pem". Asegúrese de añadir las líneas "----- BEGIN CERTIFICATE -----" y "----- END CERTIFICATE -----" en el archivo al principio y al final.

3: Crear un archivo de almacén de claves de Java, 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: Copiar los cacerts.jks resultantes de almacén de claves en el directorio de recursos de la aplicación Java / (experto)

.

5: Utilice el código siguiente para cargar este archivo y adjuntarlo a la Apache 4.5 HttpClient. Esto va a resolver el problema para todos los dominios que tienen los certificados emitidos a partir indetrust.com util Oracle incluye el certificado en el almacén de claves de JRE por omisión.

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 el proyecto se basa entonces los cacerts.jks se copiará en la ruta de clase y se carga desde allí. No lo hice, en este punto en el tiempo, la prueba contra otros sitios SSL, pero si el código anterior "cadenas" en este certificado entonces van a trabajar también, pero de nuevo, no sé.

Referencia: costumbre SSL contexto y ¿Cómo se acepta un certificado autofirmado con un Java HttpsURLConnection?

Apache 4.5 HttpClient soportes aceptar certificados con firma:

SSLContext sslContext = SSLContexts.custom()
    .loadTrustMaterial(new TrustSelfSignedStrategy())
    .build();
SSLConnectionSocketFactory socketFactory =
    new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> reg =
    RegistryBuilder.<ConnectionSocketFactory>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 utilizará el TrustSelfSignedStrategy, registra con un administrador de conexión personalizado, entonces hace un HTTP GET que utilizan ese administrador de conexión.

Estoy de acuerdo con aquellos que cantan "no hacer esto en la producción", sin embargo, hay casos de uso para la aceptación de los certificados de firma propia producción fuera; los usamos en las pruebas de integración automatizadas, por lo que estamos usando SSL (como en la producción), incluso cuando no se ejecuta en el hardware de producción.

En lugar de establecer la fábrica toma por defecto (que la OMI es una cosa mala) - esto sólo afectará a la conexión actual en lugar de cada conexión SSL intenta 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();

Confianza todos los certificados SSL: - Puede derivación SSL si desea probar en el servidor de prueba. Pero no use este código para la 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) { 
    }
}

}

Por favor llame a esta función en onCreate () en la actividad o en su clase de aplicación.

NukeSSLCerts.nuke();

Esto puede ser usado para voleo en Android.

Si 'ellos' están utilizando un certificado autofirmado que es a ellos a tomar las medidas necesarias para hacer su utilizable servidor. Específicamente que los medios que proporciona el certificado a usted fuera de línea de una manera digna de confianza. Así que conseguir que hagan eso. A continuación, importe que en su almacén de confianza utilizando la herramienta de claves como se describe en la Guía de referencia de JSSE. Ni siquiera pensar en la insegura TrustManager publicado aquí.

Editar Para el beneficio de los diecisiete (!) Downvoters, y numerosos comentaristas abajo, que claramente no han leído realmente lo que he escrito aquí, esto es no a jeremiad contra certificados de firma propia. No hay nada malo con certificados firmados cuando se implementa correctamente. Pero, de la manera correcta para ponerlas en práctica es tener el certificado expedido segura a través de un proceso fuera de línea, en lugar de a través del canal autenticado que van a ser utilizados para autenticar. Sin duda, esto es obvio? Sin duda, es obvio para cualquier organización consciente de la seguridad que he trabajado, de los bancos con miles de sucursales a mis propias empresas. El lado del cliente código base 'solución' de confiar en todos certificados, incluidos los certificados de firma propia firmados por absolutamente nadie, o cualquier erigirse en una CA cuerpo arbitrario, es ipso facto no seguro. Sólo está jugando en la seguridad. No tiene sentido. Usted está teniendo una, a prueba de manipulación, a prueba de respuesta privada, la conversación de inyección de prueba con ... alguien. Cualquiera. Un hombre en el medio. Un imitador. Cualquiera. Es posible que también acaba de utilizar texto sin formato.

La respuesta aceptada está muy bien, pero me gustaría añadir algo a esto como yo estaba usando IntelliJ en Mac y no podía conseguir que funcione utilizando la variable de ruta JAVA_HOME.

Resulta inicial de Java era diferente cuando se ejecuta la aplicación desde IntelliJ.

Para averiguar exactamente dónde está, sólo se puede hacer System.getProperty("java.home") ya que es donde los certificados de confianza se leen de.

No es una mejor alternativa a confiar en todos los certificados: Crear un TrustStore que confía en concreto un certificado determinado y utilizar esto para crear un SSLContext desde el que obtener la SSLSocketFactory al conjunto de la HttpsURLConnection. Aquí está el código completo:

File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());

Puede cargar alternativamente, la KeyStore directamente desde un archivo o recuperar el certificado X.509 desde cualquier fuente de confianza.

Tenga en cuenta que con este código, no se utilizarán los certificados en cacerts. Este HttpsURLConnection particular, sólo confiar en este certificado específico.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top