Jak rozwiązać błąd javax.net.ssl.SSLHandshakeException?

101

Połączyłem się z VPN, aby skonfigurować interfejs API inwentaryzacji, aby uzyskać listę produktów i działa dobrze. Gdy otrzymam wynik z usługi internetowej i powiążę się z interfejsem użytkownika. A także zintegrowałem PayPal z moją aplikacją do ekspresowej płatności, kiedy dzwonię do płatności, napotykam ten błąd. Używam serwletu do procesów zaplecza. Czy ktoś może powiedzieć, jak rozwiązać ten problem?

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
selladurai
źródło
1
co chciałbym wiedzieć, który dokładny cel był w tym przypadku ... Dostajesz wyjątek, ale nie dostajesz informacji o celu, który może różnić się od tego, czego oczekujesz .. Mam taki przypadek, na pewno mój link ma certyfikat i nadal otrzymuję ten wyjątek
ante.sabo

Odpowiedzi:

151

Najpierw musisz uzyskać certyfikat publiczny z serwera, z którym próbujesz się połączyć. Można to zrobić na różne sposoby, na przykład kontaktując się z administratorem serwera i prosząc o to, używając OpenSSL do pobrania lub, ponieważ wygląda na to, że jest to serwer HTTP, łącząc się z nim za pomocą dowolnej przeglądarki, przeglądając informacje o zabezpieczeniach strony i zapisanie kopii certyfikatu. (Google powinien być w stanie powiedzieć dokładnie, co zrobić dla Twojej konkretnej przeglądarki).

Po zapisaniu certyfikatu w pliku należy dodać go do zaufanego magazynu maszyny JVM. W $JAVA_HOME/jre/lib/security/przypadku JRE lub $JAVA_HOME/lib/securityJDK znajduje się plik o nazwie cacerts, który jest dostarczany z Javą i zawiera publiczne certyfikaty dobrze znanych urzędów certyfikacji. Aby zaimportować nowy certyfikat, uruchom keytool jako użytkownik, który ma uprawnienia do zapisu w cacerts:

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

Najprawdopodobniej poprosi Cię o hasło. Domyślne hasło dostarczone z Javą to changeit. Prawie nikt tego nie zmienia. Po wykonaniu tych stosunkowo prostych kroków będziesz komunikować się bezpiecznie i mając pewność, że rozmawiasz z właściwym serwerem i tylko z właściwym serwerem (o ile nie stracą klucza prywatnego).

Ryan Stewart
źródło
12
Potrzebuję trochę więcej szczegółów niż „nie działa”. Spróbuj zaktualizować swoje pytanie, podając to, co wypróbowałeś i jakieś błędy. Niestety, moja pora snu już dawno minęła, więc może ktoś inny będzie mógł odpowiedzieć na Twoje pytania. To również jest bardzo powszechna sytuacja. Wiele informacji na jej temat można znaleźć w Internecie, w tym w dokumentacji narzędzia keytool .
Ryan Stewart,
Zasadniczo muszę dołączyć certyfikat PayPal. Zrobiłem twoje kroki i dodałem również certyfikat w odpowiednim miejscu. potem uruchamiam aplikację, która mówi, że ten sam błąd?
selladurai
Właściwie działa dobrze, przeglądarka nie ma problemu i połączyłem się z VPN, aby uzyskać listę zapasów w tym czasie, gdy dokonuję płatności przez PayPal, mówi się, że błąd SSL, ale używam danych offline lub danych zakodowanych na gorąco oznacza, że ​​działa.
selladurai
4
@selladurai: Nie musisz importować certyfikatu dla PayPal. O ile plik cacerts nie został uszkodzony lub zmodyfikowany, powinien zawierać wszystkie zaufane certyfikaty główne, a certyfikat paypal powinien być możliwy do prześledzenia do jednego z nich. Możesz spróbować zdobyć kopię cacerts, o których wiesz, że są dobre, i spróbować zamiast tego. Jeśli problem nie ustąpi, być może nie łączysz się z systemem PayPal.
Ryan Stewart
@RyanStewart czy muszę jakoś zaimportować go do Glassfish? Ponieważ dodałem plik .cer do mojego cacert w moim katalogu domowym Java, ale glassfish nie wydaje się go używać, więc nadal otrzymuję błąd.
Ced
15

Teraz rozwiązałem ten problem w ten sposób,

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

Oczywiście to rozwiązanie powinno być stosowane tylko w scenariuszach, w których nie jest możliwe zainstalowanie wymaganych certyfikatów przy użyciu keytoolnp. Testów lokalnych z tymczasowymi certyfikatami.

selladurai
źródło
52
Wow, wahałbym się nazwać to „poprawką”. Właściwie właśnie wyłączyłeś zabezpieczenia. Tak, dane będą nadal zaszyfrowane, ale nie masz gwarancji, że rozmawiasz z kim chcesz rozmawiać. Prawidłowym rozwiązaniem jest uzyskanie klucza publicznego z serwera docelowego i zaimportowanie go do zaufanego magazynu maszyny JVM nawiązującej połączenie.
Ryan Stewart,
1
zobacz moją odpowiedź na odpowiednie rozwiązanie
Ryan Stewart,
Przykład czego? Moja odpowiedź powinna zawierać wszystkie potrzebne kroki.
Ryan Stewart,
3
Kod pokazany tutaj wydaje się pomocny, jeśli piszesz bardzo prosty test na serwerze deweloperskim, jednak nie polegałbym na nim w produkcji.
Jason D
12

Ilekroć próbujemy połączyć się z adresem URL,

jeśli serwer w innej witrynie działa na protokole https i nakazuje nam komunikację za pośrednictwem informacji podanych w certyfikacie, mamy następującą opcję:

1) poproś o certyfikat (pobierz certyfikat), zaimportuj ten certyfikat do trustore. Domyślne zastosowania trustore java można znaleźć w \ Java \ jdk1.6.0_29 \ jre \ lib \ security \ cacerts, a jeśli spróbujemy ponownie połączyć się z adresem URL, połączenie zostanie zaakceptowane.

2) W normalnych przypadkach biznesowych możemy łączyć się z wewnętrznymi adresami URL w organizacjach i wiemy, że są one poprawne. W takich przypadkach ufasz, że jest to poprawny adres URL. W takich przypadkach można użyć kodu, który nie upoważnia do przechowywania certyfikatu w celu połączenia się z określonym adresem URL.

dla punktu nr 2 musimy wykonać poniższe kroki:

1) napisz poniżej metodę, która ustawia HostnameVerifier dla HttpsURLConnection, który zwraca wartość true dla wszystkich przypadków, co oznacza, że ​​ufamy 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) napisz poniżej metodę, która wywołuje doTrustToCertificates przed próbą połączenia się z adresem 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());
   }

To wywołanie zwróci kod odpowiedzi = 200 oznacza, że ​​połączenie się powiodło.

Więcej szczegółów i przykładowy przykład można znaleźć pod adresem URL .

Shirishkumar Bari
źródło
1
Sklep Google Play odrzuci to. Zobaczą, że ignorujesz X509Certificate podczas skanowania APK.
Oliver Dixon
0

SSLHandshakeException można rozwiązać na dwa sposoby.

  1. Zawiera SSL

    • Uzyskaj SSL (pytając administratora systemu źródłowego, można go również pobrać za pomocą polecenia openssl lub dowolnej przeglądarki pobiera certyfikaty)

    • Dodaj certyfikat do zaufanego magazynu (cacerts) znajdującego się w JRE / lib / security

    • podaj lokalizację zaufanych certyfikatów w argumentach maszyny wirtualnej jako „-Djavax.net.ssl.trustStore =”

  2. Ignorowanie SSL

    W tym punkcie nr 2 odwiedź moją drugą odpowiedź na innej stronie internetowej stackoverflow: Jak przeprowadzić weryfikację SSL Ignoruj ​​błędy certyfikatu SSL w Javie

Amit Kaneria
źródło
-1

Uważam, że próbujesz połączyć się z czymś za pomocą SSL, ale coś dostarcza certyfikat, który nie jest weryfikowany przez główne urzędy certyfikacji, takie jak verisign .. W zasadzie domyślnie bezpieczne połączenia można nawiązać tylko wtedy, gdy osoba próbująca się połączyć wie klucze kontrahentów lub inny dostawca, taki jak verisign, może wkroczyć i powiedzieć, że dostarczony klucz publiczny jest rzeczywiście prawidłowy.

Zaufanie ALL OS garstka urzędów certyfikacji i mniejszych wystawców certyfikatów musi być certyfikowana przez jednego z dużych certyfikatorów tworzących łańcuch certyfikatów, jeśli rozumiesz, o co mi chodzi ...

W każdym razie wracając do rzeczy ... Miałem podobny problem podczas programowania apletu java i serwera java (mam nadzieję, że pewnego dnia napiszę cały wpis na blogu o tym, jak sprawiłem, że wszystkie zabezpieczenia działają :))

W istocie to, co musiałem zrobić, to wyodrębnić klucze publiczne z serwera i przechowywać je w magazynie kluczy wewnątrz mojego apletu, a kiedy połączyłem się z serwerem, użyłem tego magazynu kluczy do stworzenia fabryki zaufania i tej fabryki zaufania do utworzenia ssl połączenie. Istnieją alternatywne procedury, takie jak dodanie klucza do zaufanego hosta maszyny JVM i modyfikowanie domyślnego magazynu zaufanych certyfikatów podczas uruchamiania.

Zrobiłem to około dwa miesiące temu i nie mam teraz przy sobie kodu źródłowego ... użyj google i powinieneś być w stanie rozwiązać ten problem. Jeśli nie możesz wysłać do mnie wiadomości zwrotnej i mogę dostarczyć Ci odpowiedni kod źródłowy projektu .. Nie wiem, czy to rozwiąże problem, ponieważ nie dostarczyłeś kodu, który powoduje te wyjątki. Ponadto pracowałem z apletami i nie rozumiem, dlaczego nie działają na Serverletach ...

PS Nie mogę pobrać kodu źródłowego przed weekendem, ponieważ zewnętrzne SSH jest wyłączone w moim biurze :(

Osama Javed
źródło
1
PS Znalazłem ten kod java online, aby wyodrębnić i przechowywać klucze publiczne mojego serwera, więc googluj lub poczekaj do niedzieli
Osama Javed
-8

Teraz rozwiązałem ten problem w ten sposób,

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);
}
Evelyn Condes
źródło
4
„Nie ma potrzeby wdrażania” jest całkowicie i całkowicie błędne. Właśnie sprawiłeś, że połączenie SSL jest niezabezpieczone. Nie rób tego.
Markiz Lorne