2012-06-18 11 views
6

Pensando che avrei avuto lo stesso problema degli altri, ho riscontrato numerosi problemi simili e potenziali soluzioni, ma senza fortuna.SSL Java: InstallCert riconosce il certificato, ma ancora "impossibile trovare il percorso di certificazione valido" errore?

L'archivio fidato che sto usando è cacerts, che si trova in lib/security di un JRE 1.6.0 di Java (build 1.6.0_20-b02 ... questa potrebbe essere la radice del problema?). Ho anche provato con jssecacerts.

Utilizzando InstallCert (per altri problemi simili pubblicati), posso vedere il mio certificato è in realtà installato e valido (e l'ho rimosso, re-importato, ecc per assicurarsi che sto vedendo i dati giusti) :

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

il check-in keytool e Portecle, ri-importare il CERT (ho provato la generazione da OpenSSL con -showcert, l'esportazione dai browser e scp'ing sopra, ecc) mi dà "che esiste già in questo altro alias qui "tipo di messaggio. Quindi non sembra esserci alcun problema con il modo in cui il certificato sta entrando nello/negli strumento/i.

Forzare esplicitamente i percorsi trustStore nel codice non fa alcuna differenza, e in tutti i casi quello che vedo quando accendo il debug (tramite una setProperty di javax.net.debug su "all") è:

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 

Purtroppo non riesco a consentire l'override del controllo implementando il mio TrustManager - deve effettivamente controllare.

Il certificato che ricevo da l'host ha un certo numero di estensioni (9, per l'esattezza), il che mi fa chiedere se sono in qualche modo parte di questo problema.

Cos'altro posso controllare/provare? Passare a una diversa versione di JRE?

+0

Perché non è possibile implementare il proprio 'TrustManager'? È ancora possibile utilizzare i certificati nel proprio gestore sicuro ed eseguire il controllo. –

+0

@Vivin - Il problema principale che sto cercando di risolvere è all'interno di un livello di applicazione che non controllo direttamente (in questo caso un'applicazione Grails che chiama alcuni servizi Web che sono diventati di recente solo HTTPS), quindi mentre potrei usarlo nel mio scenario locale non posso far valere il suo uso a monte. Che è un dolore, ma ci penserò più tardi - per ora se riesco a farlo funzionare al livello più granulare, almeno mi dice dove andare avanti nella catena di risoluzione. – Bill

+0

Stai eseguendo questo sotto un server di app come GlassFish? Si può verificare se il processo sta eseguendo il binario Java in /usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64 e non in qualche altro binario Java che non gli piace il tuo trustStore (OpenJDK, eccetera). L'output di debug che hai postato è utile, puoi pubblicare di più? – Brad

risposta

0

è ancora possibile verificare il certificato implementando il proprio gestore di fiducia. Mi sono imbattuto in un problema simile here. Ho anche provato ad aggiungere il certificato a cacerts ma inutilmente.

Nel tuo manager di fiducia, è necessario caricare in modo esplicito i certificati.In sostanza quello che dovevo fare era qualcosa di simile:

prima cosa creare un gestore di fiducia che utilizza i file dei certificati effettivi:

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

Poi ho appena registrato 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'unica lo svantaggio è che ho dovuto creare un protocollo specifico (vhttps) per questo particolare certificato.

+0

Difficile credere a tutto ciò che era veramente necessario. C'è un chiaro sapore di non capire il problema di fondo su tutto questo. – EJP

+0

@EJP Non è necessario essere così condiscendenti. Sono dolorosamente consapevole che questa soluzione non è ottimale. Se hai una soluzione migliore, sono felice di sentirlo. Ho provato numerose cose per farlo funzionare, incluso l'aggiunta del certificato (ho persino assicurato di poter risalire fino in fondo alla catena di fiducia) al file cacerts del server. Niente sembrava funzionare. Ho studiato molto questo problema prima di usare finalmente questa soluzione. Non era la mia prima scelta. –

+0

@Vivin - Apprezzo il suggerimento, anche se spero davvero di non dover seguire questa strada. :) Un sacco di altro codice che non controllo direttamente dovrebbe essere cambiato, quindi è uno di quegli scenari in cui continuo a sperare che ci sia un errore da qualche parte dalla mia parte o dal lato della configurazione della macchina. – Bill

0

La mia ipotesi è una di queste cose è successo:

a) si esegue il codice su un server web. Usano spesso il loro negozio di fiducia - quindi sei davvero sicuro che sia cacerts che viene utilizzato quando il codice viene eseguito?

b) Per impostazione predefinita, Java tenterà di verificare la validità dei certificati scaricando e interpretando CRL. Se si è dietro un proxy, il download non riesce e, di conseguenza, l'intero controllo PKIX fallirebbe.

+1

Mentre installa direttamente il certificato del server, (b) non si applica. Buona risposta altrimenti. – EJP

+0

@EJP Oops, hai ragione, l'ho mescolato con l'importazione del certificato di root. Risolverà quello – emboss

+0

All'inizio pensavo che fosse A, poi quando lo riducevo al solo codice di esempio con debug completo e ancora non funzionava mi sentivo male. Apprezzo comunque i suggerimenti. – Bill

0

Lo SSL traccia di debug mostrerà quale cacerts file che si sta utilizzando, a patto che non si caricano manualmente da soli. Chiaramente non stai usando quello che pensi di essere.

+0

Buono a voler controllare, ma non è il caso: "trustStore è: /usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64/jre/lib/security/jssecacerts". Come ho detto, l'ho provato con jssecacerts e cacerts, che include l'applicazione del percorso attraverso le impostazioni di trustStore. – Bill

Problemi correlati