Come risolvere l'errore javax.net.ssl.SSLHandshakeException?


101

Mi sono connesso con VPN per configurare l'API dell'inventario per ottenere l'elenco dei prodotti e funziona bene. Una volta ottenuto il risultato dal servizio web, mi collego all'interfaccia utente. Inoltre ho integrato PayPal con la mia applicazione per effettuare il checkout espresso quando effettuo una chiamata per il pagamento, sto riscontrando questo errore. Uso servlet per il processo di back-end. Qualcuno può dire come risolvere questo problema?

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

1
quello che vorrei sapere, quale obiettivo esatto era in quel caso ... Ottieni un'eccezione, ma non ottieni informazioni sul bersaglio, che potrebbe essere diverso da quello che ti aspetti .. Ho questo caso, sono sicuro che il mio link ha un certificato e ricevo ancora questa eccezione
ante.sabo

Risposte:


151

Innanzitutto, devi ottenere il certificato pubblico dal server a cui stai tentando di connetterti. Ciò può essere fatto in vari modi, come contattare l'amministratore del server e richiederlo, utilizzare OpenSSL per scaricarlo o, poiché questo sembra essere un server HTTP, connettersi ad esso con qualsiasi browser, visualizzare le informazioni di sicurezza della pagina e salvando una copia del certificato. (Google dovrebbe essere in grado di dirti esattamente cosa fare per il tuo browser specifico.)

Ora che hai il certificato salvato in un file, devi aggiungerlo al truststore della tua JVM. In $JAVA_HOME/jre/lib/security/JRE o $JAVA_HOME/lib/securityJDK, c'è un file denominato cacerts, che viene fornito con Java e contiene i certificati pubblici delle ben note autorità di certificazione. Per importare il nuovo certificato, esegui keytool come utente che dispone dell'autorizzazione per scrivere in cacerts:

keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>

Molto probabilmente ti chiederà una password. La password predefinita fornita con Java è changeit. Quasi nessuno lo cambia. Dopo aver completato questi passaggi relativamente semplici, comunicherai in modo sicuro e con la certezza di parlare con il server giusto e solo con il server giusto (a condizione che non perdano la loro chiave privata).


12
Avrò bisogno di un po 'più di dettagli di "non funziona". Prova ad aggiornare la tua domanda con ciò che hai provato e alcuni risultati di errore. Sfortunatamente, è molto passata l'ora di andare a letto, quindi forse qualcun altro sarà in grado di rispondere alle tue domande. Anche questa è una situazione molto comune. È possibile trovare molte informazioni in linea, inclusi i documenti di keytool .
Ryan Stewart

Fondamentalmente ho bisogno di includere il certificato PayPal. Ho fatto i tuoi passaggi e cert aggiunto anche nella posizione appropriata. quindi eseguo l'app dice lo stesso errore?
selladurai

In realtà funziona bene nessun problema con il browser e ho collegato la VPN per ottenere l'elenco dell'inventario in quel momento effettuo il pagamento con PayPal, si dice che è un errore SSL ma utilizzo dati offline o dati hot coded significa che funziona.
selladurai

4
@selladurai: non dovresti aver bisogno di importare un certificato per paypal. A meno che il tuo file cacerts non sia stato danneggiato o modificato, dovrebbe contenere tutti i certificati radice attendibili e il certificato di paypal dovrebbe essere ricondotto a uno di questi. Potresti provare a ottenere una copia dei certificati che sai essere buoni e provare invece quello. Se il problema persiste, potresti non essere effettivamente connesso a PayPal.
Ryan Stewart

@ RyanStewart, devo importarlo in qualche modo in glassfish? Perché ho aggiunto il file .cer al mio cacert nella mia home directory java ma glassfish non sembra utilizzarlo, quindi ricevo ancora l'errore.
Ced

15

Ora ho risolto questo problema in questo modo,

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream; 

// Create a trust manager that does not validate certificate chains like the default 

TrustManager[] trustAllCerts = new TrustManager[]{
        new X509TrustManager() {

            public java.security.cert.X509Certificate[] getAcceptedIssuers()
            {
                return null;
            }
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
        }
};

// 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 (Exception e) 
{
    System.out.println(e);
}

Ovviamente questa soluzione dovrebbe essere utilizzata solo in scenari in cui non è possibile installare i certificati richiesti utilizzando, keytoolad esempio, test locali con certificati temporanei.


52
Wow, esiterei a definirlo una "correzione". In pratica hai appena disattivato la sicurezza. Sì, i dati saranno comunque crittografati, ma non hai alcuna garanzia di parlare con chi ti aspetti di parlare. La soluzione corretta è ottenere la chiave pubblica del server di destinazione e importarla nel truststore della JVM che effettua la connessione.
Ryan Stewart

1
vedere la mia risposta per una soluzione appropriata
Ryan Stewart

Esempio di cosa? La mia risposta dovrebbe contenere tutti i passaggi necessari.
Ryan Stewart


3
Il codice mostrato qui sembra utile se stai scrivendo un test molto semplice su un server di sviluppo, ma non ci farei affidamento per la produzione.
Jason D

12

Ogni volta che proviamo a connetterci all'URL,

se il server sull'altro sito è in esecuzione sul protocollo https e ci impone di comunicare tramite le informazioni fornite nel certificato, abbiamo la seguente opzione:

1) richiedi il certificato (scarica il certificato), importa questo certificato in trustore. Gli usi java del trustore predefinito possono essere trovati in \ Java \ jdk1.6.0_29 \ jre \ lib \ security \ cacerts, quindi se proviamo a connetterci alla connessione URL verrebbe accettata.

2) Nei normali casi aziendali, potremmo connetterci a URL interni nelle organizzazioni e sappiamo che sono corretti. In tali casi, ci si fida che sia l'URL corretto. In tali casi sopra, è possibile utilizzare codice che non imporrà di memorizzare il certificato per connettersi a un determinato URL.

per il punto n. 2 dobbiamo seguire i seguenti passaggi:

1) scrivi sotto il metodo che imposta HostnameVerifier per HttpsURLConnection che restituisce true per tutti i casi, il che significa che ci fidiamo di trustStore.

  // trusting all certificate 
 public void doTrustToCertificates() throws Exception {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }
                }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                }
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

2) scrivi sotto il metodo, che chiama doTrustToCertificates prima di provare a connettersi all'URL

    // connecting to URL
    public void connectToUrl(){
     doTrustToCertificates();//  
     URL url = new URL("https://www.example.com");
     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     System.out.println("ResponseCode ="+conn.getResponseCode());
   }

Questa chiamata restituirà il codice di risposta = 200 significa che la connessione è riuscita.

Per maggiori dettagli e un esempio di esempio, puoi fare riferimento a URL .


1
Google Play Store rifiuterà questo. Vedranno che stai ignorando X509Certificate durante la scansione dell'APK.
Oliver Dixon

0

SSLHandshakeException può essere risolto in 2 modi.

  1. Incorporando SSL

    • Ottieni l'SSL (chiedendo all'amministratore del sistema di origine, può anche essere scaricato dal comando openssl o qualsiasi browser scarica i certificati)

    • Aggiungi il certificato nel truststore (cacerts) situato in JRE / lib / security

    • fornire la posizione del truststore negli argomenti vm come "-Djavax.net.ssl.trustStore ="

  2. Ignorare SSL

    Per questo # 2, visita la mia altra risposta su un altro sito Web di stackoverflow : Come importare la verifica SSL Ignora gli errori del certificato SSL con Java


-1

Credo che tu stia cercando di connetterti a qualcosa utilizzando SSL ma che qualcosa stia fornendo un certificato che non è verificato dalle autorità di certificazione root come verisign .. In sostanza, per impostazione predefinita, le connessioni protette possono essere stabilite solo se la persona che tenta di connettersi sa le chiavi della controparte o qualche altro verndor come verisign possono intervenire e dire che la chiave pubblica fornita è effettivamente corretta.

La fiducia di TUTTI i sistemi operativi una manciata di autorità di certificazione e emittenti di certificati più piccoli deve essere certificata da uno dei grandi certificatori che fanno una catena di certificatori se capisci cosa intendo ...

Comunque tornando al punto .. Ho avuto un problema simile durante la programmazione di un'applet java e un server java (Spero che un giorno scriverò un post sul blog completo su come ho avuto tutta la sicurezza per funzionare :))

In sostanza quello che dovevo fare era estrarre le chiavi pubbliche dal server e memorizzarle in un keystore all'interno della mia applet e quando mi sono connesso al server ho usato questo key store per creare un trust factory e quel trust factory per creare ssl connessione. Esistono anche procedure alteranti come l'aggiunta della chiave all'host attendibile della JVM e la modifica del truststore predefinito all'avvio.

L'ho fatto circa due mesi fa e non ho il codice sorgente su di me in questo momento .. usa Google e dovresti essere in grado di risolvere questo problema. Se non puoi rispedirmi un messaggio e posso fornirti il ​​codice sorgente pertinente per il progetto .. Non so se questo risolve il tuo problema poiché non hai fornito il codice che causa queste eccezioni. Inoltre stavo lavorando con applet, pensavo di non capire perché non funzionasse su Serverlet ...

PS Non riesco a ottenere il codice sorgente prima del fine settimana poiché SSH esterno è disabilitato nel mio ufficio :(


1
PS Ho trovato questo codice java online per estrarre e memorizzare le chiavi pubbliche del mio server, quindi continua a cercare su Google o aspetta fino al domenica
Osama Javed

-8

Ora ho risolto questo problema in questo modo,

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream;
// Create a trust manager that does not validate certificate chains like the 
default TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
    }
};
// 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 (Exception e) {
    System.out.println(e);
}

4
"Nessuna necessità di implementazione" è completamente e assolutamente sbagliato. Hai appena reso insicura la tua connessione SSL. Non farlo.
Marchese di Lorne
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.