2012-06-18 17 views
6

Pensando que abordaría el mismo problema que otras personas, he estado pasando por numerosos problemas similares y posibles soluciones, pero sin suerte.SSL de Java - InstallCert reconoce el certificado, pero sigue siendo un error "no se puede encontrar la ruta de certificación válida"?

El almacén de confianza que estoy utilizando es cacerts, ubicado en lib/security de un Java 1.6.0 JRE (compilación 1.6.0_20-b02 ... ¿podría ser esta la raíz del problema?). También he intentado con jssecacerts.

Usando InstallCert (por otros problemas similares publicados), puedo ver que mi certificado de hecho está instalado y es válido (y lo he eliminado, vuelto a importar, etc. para asegurarme de que estoy viendo los datos correctos) :

java InstallCert <my host name> 
Loading KeyStore jssecacerts... 
Opening connection to <my host name>:443... 
Starting SSL handshake... 
No errors, certificate is already trusted 

el registro herramienta de claves y Portecle, volver a importar el CERT (he intentado generar a partir de openssl con -showcert, la exportación de los navegadores y scp'ing la vuelta, etc) me da "que ya existe en virtud de este otro alias aquí "tipo de mensaje. Por lo tanto, no parece haber ningún problema con la forma en que el certificado ingresa a la (s) herramienta (s).

Obligar a la confianza explícita Guardar las rutas en el código no hace ninguna diferencia, y en todos los casos lo que termino viendo cuando activo la depuración (a través de setProperty de javax.net.debug a "todos") es:

main, SEND TLSv1 ALERT: fatal, description = certificate_unknown 
main, WRITE: TLSv1 Alert, length = 2 [Raw write]: length = 7 0000: 15 
03 01 00 02 02 2E        ....... main, called 
closeSocket() main, handling exception: 
javax.net.ssl.SSLHandshakeException: 
sun.security.validator.ValidatorException: PKIX path building failed: 
sun.security.provider.certpath.SunCertPathBuilderException: unable to 
find valid certification path to requested target 

por desgracia, no puede permitir que anulando la comprobación mediante la implementación de mi propia TrustManager - tiene que comprobar realmente.

El certificado que recibo del host tiene varias extensiones (9, para ser exactos), lo que me hace preguntarme si de alguna manera son parte de este problema.

¿Qué más puedo verificar/probar? Cambiar a una versión diferente de JRE?

+0

¿Por qué no puede implementar su propio 'TrustManager'? Todavía puede usar los certificados en su administrador de confianza y realizar el control. –

+0

@Vivin - El problema principal que estoy tratando de resolver está dentro de una capa de aplicación que no controlo directamente (en este caso, una aplicación Grails que llama a algunos servicios web que recientemente se han convertido en HTTPS solamente), así que mientras yo podía usarla en mi propio escenario local, no puedo aplicar su uso en sentido ascendente. Lo cual es un problema, pero lo abordaré más adelante, por ahora, si puedo hacerlo funcionar al nivel más detallado, al menos me dice a dónde ir a continuación en la cadena de resolución. – Bill

+0

¿Está ejecutando esto en un servidor de aplicaciones como GlassFish? Puede verificar si el proceso está ejecutando el binario de Java en /usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64 y no en otro binario de Java que no le gusta su trustStore (OpenJDK, etc.) La salida de depuración que publicó es útil, ¿puede publicar más? – Brad

Respuesta

0

Aún puede verificar el certificado implementando su propio administrador de confianza. Me encontré con un problema similar here. También intenté agregar el certificado al cacerts pero fue en vano.

En su administrador de confianza, debe cargar explícitamente los certificados.Esencialmente lo que tenía que hacer era algo como esto:

Primero crear un gestor de confianza que utiliza los archivos de certificados reales:

public class ValicertX509TrustManager implements X509TrustManager { 

    X509TrustManager pkixTrustManager; 

    ValicertX509TrustManager() throws Exception { 

     String valicertFile = "/certificates/ValicertRSAPublicRootCAv1.cer"; 
     String commwebDRFile = "/certificates/DR_10570.migs.mastercard.com.au.crt"; 
     String commwebPRODFile = "/certificates/PROD_10549.migs.mastercard.com.au.new.crt"; 

     Certificate valicert = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(valicertFile)); 
     Certificate commwebDR = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebDRFile)); 
     Certificate commwebPROD = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebPRODFile)); 

     KeyStore keyStore = KeyStore.getInstance("JKS"); 
     keyStore.load(null, "".toCharArray()); 
     keyStore.setCertificateEntry("valicert", valicert); 
     keyStore.setCertificateEntry("commwebDR", commwebDR); 
     keyStore.setCertificateEntry("commwebPROD", commwebPROD); 

     TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); 
     trustManagerFactory.init(keyStore); 

     TrustManager trustManagers[] = trustManagerFactory.getTrustManagers(); 

     for(TrustManager trustManager : trustManagers) { 
      if(trustManager instanceof X509TrustManager) { 
       pkixTrustManager = (X509TrustManager) trustManager; 
       return; 
      } 
     } 

     throw new Exception("Couldn't initialize"); 
    } 

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 
     pkixTrustManager.checkServerTrusted(chain, authType); 
    } 

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 
     pkixTrustManager.checkServerTrusted(chain, authType); 
    } 

    public X509Certificate[] getAcceptedIssuers() { 
     return pkixTrustManager.getAcceptedIssuers(); 
    } 
} 

Ahora, el uso de este gestor de confianza, tuve que crear una fábrica de sockets:

public class ValicertSSLProtocolSocketFactory implements ProtocolSocketFactory { 

    private SSLContext sslContext = null; 

    public ValicertSSLProtocolSocketFactory() { 
     super(); 
    } 

    private static SSLContext createValicertSSLContext() { 
     try { 
      ValicertX509TrustManager valicertX509TrustManager = new ValicertX509TrustManager(); 
      SSLContext context = SSLContext.getInstance("TLS"); 
      context.init(null, new ValicertX509TrustManager[] { valicertX509TrustManager}, null); 
      return context; 
     } 

     catch(Exception e) { 
      Log.error(Log.Context.Net, e); 
      return null; 
     } 
    } 

    private SSLContext getSSLContext() { 
     if(this.sslContext == null) { 
      this.sslContext = createValicertSSLContext(); 
     } 

     return this.sslContext; 
    } 

    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException { 
     return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort); 
    } 

    public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException { 
     if(params == null) { 
      throw new IllegalArgumentException("Parameters may not be null"); 
     } 

     int timeout = params.getConnectionTimeout(); 
     SocketFactory socketFactory = getSSLContext().getSocketFactory(); 

     if(timeout == 0) { 
      return socketFactory.createSocket(host, port, localAddress, localPort); 
     } 

     else { 
      Socket socket = socketFactory.createSocket(); 
      SocketAddress localAddr = new InetSocketAddress(localAddress, localPort); 
      SocketAddress remoteAddr = new InetSocketAddress(host, port); 
      socket.bind(localAddr); 
      socket.connect(remoteAddr, timeout); 
      return socket; 
     } 
    } 

    public Socket createSocket(String host, int port) throws IOException { 
     return getSSLContext().getSocketFactory().createSocket(host, port); 
    } 

    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { 
     return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose); 
    } 

    public boolean equals(Object obj) { 
     return ((obj != null) && obj.getClass().equals(ValicertSSLProtocolSocketFactory.class)); 
    } 

    public int hashCode() { 
     return ValicertSSLProtocolSocketFactory.class.hashCode(); 
    } 
} 

Entonces me acaba de registrar un nuevo protocolo:

Protocol.registerProtocol("vhttps", new Protocol("vhttps", new ValicertSSLProtocolSocketFactory(), 443)); 
PostMethod postMethod = new PostMethod(url); 
for (Map.Entry<String, String> entry : params.entrySet()) { 
    postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue())); 
} 

HttpClient client = new HttpClient(); 
int status = client.executeMethod(postMethod); 
if (status == 200) { 
    StringBuilder resultBuffer = new StringBuilder(); 
    resultBuffer.append(postMethod.getResponseBodyAsString()); 
    return new HttpResponse(resultBuffer.toString(), ""); 
} else { 
    throw new IOException("Invalid response code: " + status); 
} 

El único La desventaja es que tuve que crear un protocolo específico (vhttps) para este certificado en particular.

+0

Difícil de creer todo lo que era realmente necesario. Hay un sabor distintivo de no entender el problema subyacente sobre todo esto. – EJP

+0

@EJP No es necesario ser tan condescendiente. Soy muy consciente de que esta solución no es óptima. Si tienes una mejor solución, estoy feliz de escucharla. Probé varias cosas para que esto funcione, incluso agregué el certificado (incluso me aseguré de poder seguir la cadena de confianza) hasta el archivo de cacerts del servidor. Nada parecía funcionar. Investigué mucho sobre este problema antes de usar finalmente esta solución. No fue mi primera elección. –

+0

@Vivin: agradezco la sugerencia, aunque realmente espero no tener que seguir esta ruta. :) Muchos otros códigos que no controlo directamente deberían cambiarse, por lo que es uno de esos escenarios en los que sigo esperando que haya un error en algún lado de mi lado o en el lado de la configuración de la máquina. – Bill

0

Mi conjetura es ninguna de estas cosas sucedieron:

a) El usuario ejecuta el código en un servidor web. A menudo usan su propia tienda de confianza, así que, ¿de verdad está seguro de que se está usando cacerts cuando se ejecuta su código?

b) De forma predeterminada, Java intentará verificar la validez de los certificados mediante la descarga e interpretación de CRL. Si está detrás de un proxy, la descarga falla y, como consecuencia, fallará toda la comprobación de PKIX.

+1

Como está instalando el certificado del servidor directamente, (b) no se aplica. Buena respuesta de lo contrario. – EJP

+0

@EJP Vaya, tiene razón, lo mezclé con la importación del certificado raíz en su lugar. Arreglará eso. – emboss

+0

Al principio pensé que A era, luego, cuando lo reduje al solo código de muestra con errores completos y aún fallaba, me sentí abatido. Apreciar las sugerencias sin embargo. – Bill

0

El rastreo de depuración de SSL mostrará qué archivo de cacerts está utilizando, siempre que no lo cargue manualmente. Claramente, no estás usando el que crees que eres.

+0

Es una buena idea revisarlo, pero no es el caso: "trustStore es: /usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64/jre/lib/security/jssecacerts". Como mencioné, lo probé con jssecacerts y cacerts, que incluye aplicar la ruta a través de la configuración de trustStore. – Bill

Cuestiones relacionadas