2010-02-18 18 views
16

Sto effettuando l'integrazione con un account commerciante denominato CommWeb e sto inviando un messaggio SSL al proprio URL (https://migs.mastercard.com.au/vpcdps). Quando provo a inviare il messaggio, ottengo la seguente eccezione:Creazione percorso PKIX non riuscita durante la connessione SSL

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

Il codice (che non ho scritto, e che già esiste nel nostro codice di base) che esegue il post è:

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 documentazione per l'integrazione dell'account commerciante non dice nulla sui certificati. Hanno fatto fornire qualche esempio di codice JSP che sembra accettare ciecamente certificati:

<%! // 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(); 
... 
... 
%> 

nostra webapp ha un archivio di chiavi, e ho provato ad aggiungere il certificato (che ho esportato da Firefox) utilizzando il comando keytool, ma che non ha lavoro e ho ottenuto lo stesso errore. Ho provato soluzioni sul web (importando la chiave e usando System.setProperty) ma sembra un po 'goffo e non ha funzionato (mi ha dato un NoSuchAlgorithmError). Qualsiasi aiuto è apprezzato!

+0

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

risposta

13

Evidentemente il certificato CA classe 3 valicert non si trova nel truststore predefinito (che è probabilmente il file cacerts nella directory lib/security di JRE, ma vedere lo JSSE documentation per l'intera storia).

È possibile aggiungere questo certificato al file cacerts, ma non è consigliabile. Invece, penso che dovresti creare il tuo file truststore (che può essere una copia del file cacerts) e aggiungere il valicert root ca a questo. Quindi selezionare questo file con la proprietà di sistema javax.net.ssl.trustStore.

+0

Ci proverò Domani. Per ora, ho avuto modo di lavorare creando un nuovo factory socket che implementa 'SecureProtocolSocketFactory' da' commons.httpclient'. Accetta ciecamente il certificato. Tuttavia, voglio cambiare questo e farlo funzionare nel modo giusto. Ti farò sapere cosa succede. Grazie! –

+0

Ho intenzione di andare avanti e accettare la tua soluzione e aggiungere la mia come commento. Sono stato in grado di capirlo solo dopo aver esaminato la documentazione che mi hai indicato! –

+0

Greg, puoi spiegarmi "aggiungi la radice valicert ca a questo". Che cosa significa e come dovrebbe essere fatto? – Less

7

Credo di dover aggiornare questa risposta con quello che ho effettivamente fatto. Utilizzando la documentazione fornita da GregS, ho creato un gestore di fiducia per valicert. Nel gestore di fiducia, carico il video del certificato:

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

Ora, usando questo gestore di fiducia, ho dovuto creare una fabbrica di presa:

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

Ora ho solo registrare un nuovo protocollo:

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

L'unico svantaggio è che ho dovuto creare un protocollo specifico (vhttps) per questo particolare certificato.

Problemi correlati