2010-02-18 11 views
16

Estoy integrando con una cuenta de comerciante llamada CommWeb y estoy enviando una publicación SSL a su URL (https://migs.mastercard.com.au/vpcdps). Cuando trato de enviar el mensaje, me sale el siguiente excepción:Construcción de rutas PKIX fallida al hacer la conexión SSL

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 

El código (que no he escrito, y que ya existe en nuestra base de código) que realiza el cargo es:

public static HttpResponse sendHttpPostSSL(String url, Map<String, String> params) throws IOException { 
    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); 
    } 
} 

La documentación para la integración de la cuenta comercial no dice nada sobre los certificados. Nos proporcionaron algo de código JSP de ejemplo que parece aceptar ciegamente certificados:

<%! // Define Static Constants 
    // *********************** 
public static X509TrustManager s_x509TrustManager = null; 
public static SSLSocketFactory s_sslSocketFactory = null; 

static { 
     s_x509TrustManager = new X509TrustManager() { 
     public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[] {}; } 
     public boolean isClientTrusted(X509Certificate[] chain) { return true; } 
     public boolean isServerTrusted(X509Certificate[] chain) { return true; } 
    }; 

    java.security.Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); 
    try { 
     SSLContext context = SSLContext.getInstance("TLS"); 
     context.init(null, new X509TrustManager[] { s_x509TrustManager }, null); 
     s_sslSocketFactory = context.getSocketFactory(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
     throw new RuntimeException(e.getMessage()); 
    } 
} 

... 
... 
      // write output to VPC 
      SSLSocket ssl = (SSLSocket)s_sslSocketFactory.createSocket(s, vpc_Host, vpc_Port, true); 
      ssl.startHandshake(); 
      os = ssl.getOutputStream(); 
      // get response data from VPC 
      is = ssl.getInputStream(); 
... 
... 
%> 

Nuestra aplicación web tiene un almacén de claves, y yo tratamos de añadir el certificado (que he exportado desde Firefox) usando el comando keytool, pero que no lo hicieron trabajo y obtuve el mismo error. He intentado con soluciones en la web (importando la clave y usando System.setProperty) pero parece un poco torpe y no funcionó (me dio un NoSuchAlgorithmError). ¡Cualquier ayuda es apreciada!

+0

http://stackoverflow.com/questions/21076179/pkix-path-building-failed-and-unable-to-find-valid-certification-path-to-requ/36427118#36427118 – MagGGG

Respuesta

13

Evidentemente, el certificado valicert class 3 CA no está en su almacén de confianza predeterminado (que probablemente sea el archivo cacerts en su directorio JRE lib/security, pero consulte el JSSE documentation para la historia completa).

Puede agregar este certificado al archivo cacerts, pero no lo recomiendo. En cambio, creo que debe crear su propio archivo de almacén de confianza (que puede ser una copia del archivo de cacerts) y agregar la raíz de valicert ca a esto. A continuación, apunte a este archivo con la propiedad del sistema javax.net.ssl.trustStore.

+0

Voy a probar esto mañana. Por ahora, lo hice funcionar creando una nueva fábrica de sockets que implementa 'SecureProtocolSocketFactory' desde' commons.httpclient'. Ciegamente acepta el certificado. Sin embargo, quiero cambiar esto y hacerlo funcionar de la manera correcta. Te dejaré saber qué pasa. ¡Gracias! –

+0

Voy a seguir adelante y aceptar su solución y agregar la mía como un comentario. ¡Pude resolverlo solo después de mirar la documentación que me indicaste! –

+0

Greg, ¿me puede explicar "agregar la raíz de valicert ca a esto". ¿Qué significa y cómo debería hacerse? – Less

7

Me imagino que debería actualizar esta respuesta con lo que realmente hice. Utilizando la documentación provista por GregS, creé un administrador de confianza para valicert. En el administrador de confianza, me carga los archivos de certificado:

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(); 
    } 
} 

Ahora acabo 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); 
} 

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

Cuestiones relacionadas