Как решить ошибку javax.net.ssl.SSLHandshakeException?

101

Я подключился к VPN, чтобы настроить API инвентаризации, чтобы получить список продуктов, и он отлично работает. Как только я получаю результат от веб-службы и привязываюсь к пользовательскому интерфейсу. А также я интегрировал PayPal со своим приложением для экспресс-оплаты, когда я звоню для оплаты, я сталкиваюсь с этой ошибкой. Я использую сервлет для внутреннего процесса. Кто-нибудь может сказать, как решить эту проблему?

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
что я хотел бы знать, какая именно цель была в этом случае ... Вы получаете исключение, но не получаете информации о цели, которая может отличаться от той, которую вы ожидаете .. У меня такой случай, я уверен, что моя ссылка есть сертификат, и я до сих пор получаю это исключение
ante.sabo

Ответы:

151

Во-первых, вам нужно получить общедоступный сертификат с сервера, к которому вы пытаетесь подключиться. Это можно сделать разными способами, например, связаться с администратором сервера и запросить его, использовать OpenSSL для его загрузки или, поскольку это выглядит как HTTP-сервер, подключиться к нему с помощью любого браузера, просмотреть информацию о безопасности страницы. и сохранение копии сертификата. (Google должен точно сказать вам, что делать для вашего конкретного браузера.)

Теперь, когда у вас есть сертификат, сохраненный в файле, вам нужно добавить его в хранилище доверенных сертификатов JVM. В $JAVA_HOME/jre/lib/security/течение JREs или $JAVA_HOME/lib/securityдля JDKs, есть файл с именем cacerts, который поставляется с Java и содержит открытые сертификаты известных органов по сертификации. Чтобы импортировать новый сертификат, запустите keytool от имени пользователя, у которого есть разрешение на запись в cacerts:

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

Скорее всего, он попросит вас ввести пароль. Пароль по умолчанию, поставляемый с Java, - changeit. Его почти никто не меняет. После того, как вы выполните эти относительно простые шаги, вы будете общаться безопасно и с уверенностью, что разговариваете с правильным сервером и только с правильным сервером (если они не потеряют свой закрытый ключ).

Райан Стюарт
источник
12
Мне нужно немного подробнее, чем «не работает». Попробуйте обновить свой вопрос, указав то, что вы пробовали, и сообщение об ошибке. К сожалению, я уже давно пора спать, так что, возможно, кто-то еще сможет ответить на ваши вопросы. Это тоже очень распространенная ситуация. Вы можете найти много информации об этом в Интернете, включая документацию по программному обеспечению .
Райан Стюарт
В основном мне нужно включить сертификат PayPal. Я выполнил ваши шаги, и сертификат также добавлен в соответствующем месте. то я запускаю приложение, оно говорит ту же ошибку?
selladurai
На самом деле он работает хорошо, без проблем с браузером, и я подключил VPN, чтобы получить список инвентаря, в то время я совершаю платеж через PayPal, он говорит об ошибке SSL, но я использую автономные данные или данные с горячим кодированием, что означает, что он работает.
selladurai
4
@selladurai: Вам не нужно импортировать сертификат для PayPal. Если ваш файл cacerts не был поврежден или изменен, он должен содержать все доверенные корневые сертификаты, и сертификат PayPal должен иметь возможность отследить один из них. Вы можете попробовать получить копию заведомо хороших cacerts и попробовать эту. Если проблема не исчезнет, ​​возможно, вы действительно не подключаетесь к PayPal.
Райан Стюарт
@RyanStewart, мне нужно как-то импортировать его в Glassfish? Поскольку я добавил файл .cer к моему cacert в моем домашнем каталоге java, но, похоже, Glassfish его не использует, поэтому я все еще получаю сообщение об ошибке.
Ced
15

Теперь я решил эту проблему таким образом,

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

Конечно, это решение следует использовать только в сценариях, где невозможно установить требуемые сертификаты, keytoolнапример, с помощью локального тестирования с временными сертификатами.

Селладурай
источник
53
Вау, я бы не стал называть это «исправлением». Вы просто отключили безопасность. Да, данные по-прежнему будут зашифрованы, но у вас нет гарантии, что вы разговариваете с тем, с кем хотите разговаривать. Правильное решение - получить открытый ключ целевого сервера и импортировать его в хранилище доверенных сертификатов JVM, устанавливающей соединение.
Райан Стюарт
1
см. мой ответ для соответствующего решения
Райан Стюарт
Пример чего? В моем ответе должны быть все необходимые шаги.
Райан Стюарт
3
Показанный здесь код выглядит полезным, если вы пишете очень простой тест на сервере разработки, хотя я бы не стал полагаться на него в производственной среде.
Jason D
12

Каждый раз, когда мы пытаемся подключиться к URL,

Если сервер на другом сайте работает по протоколу https и требует, чтобы мы общались через информацию, указанную в сертификате, у нас есть следующий вариант:

1) запросить сертификат (скачать сертификат), импортировать этот сертификат в трусторе. Использование java trustore по умолчанию можно найти в \ Java \ jdk1.6.0_29 \ jre \ lib \ security \ cacerts, тогда, если мы попытаемся подключиться к URL-адресу, соединение будет принято.

2) В обычных бизнес-случаях мы можем подключаться к внутренним URL-адресам в организациях и знаем, что они верны. В таких случаях вы уверены, что это правильный URL-адрес. В таких случаях, описанных выше, может использоваться код, который не требует сохранения сертификата для подключения к определенному URL-адресу.

для пункта 2 мы должны выполнить следующие шаги:

1) напишите ниже метод, который устанавливает HostnameVerifier для HttpsURLConnection, который возвращает true для всех случаев, что означает, что мы доверяем 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) напишите ниже метод, который вызывает doTrustToCertificates перед попыткой подключения к 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());
   }

Этот вызов вернет код ответа = 200 означает, что соединение успешно.

Для получения более подробной информации и примера вы можете обратиться к URL .

Ширишкумар Бари
источник
1
Магазин Google Play отклонит это. Они увидят, что вы игнорируете X509Certificate во время сканирования APK.
Оливер Диксон
0

SSLHandshakeException можно разрешить двумя способами.

  1. Включение SSL

    • Получите SSL (запросив системного администратора источника, также можно загрузить с помощью команды openssl, или любой браузер загрузит сертификаты)

    • Добавьте сертификат в хранилище доверенных сертификатов (cacerts), которое находится в JRE / lib / security.

    • укажите расположение доверенного хранилища в аргументах виртуальной машины как "-Djavax.net.ssl.trustStore ="

  2. Игнорирование SSL

    Для этого # 2, пожалуйста, посетите мой другой ответ на другом веб-сайте stackoverflow: Как пройти проверку SSL Игнорировать ошибки сертификата SSL с помощью Java

Амит Канерия
источник
-1

Я считаю, что вы пытаетесь подключиться к чему-то с помощью SSL, но что-то предоставляет сертификат, который не проверяется корневыми центрами сертификации, такими как verisign .. По сути, по умолчанию безопасные соединения могут быть установлены только в том случае, если человек, пытающийся подключиться, знает ключи контрагентов или другой поставщик, такой как verisign, могут вмешаться и сказать, что предоставленный открытый ключ действительно правильный.

ВСЕ ОС доверяют горстке центров сертификации и более мелких издателей сертификатов, которые должны быть сертифицированы одним из крупных центров сертификации, составляющих цепочку сертификаторов, если вы понимаете, о чем я ...

В любом случае, возвращаясь к сути ... У меня была похожая проблема при программировании java-апплета и java-сервера (надеюсь, когда-нибудь я напишу полный пост в блоге о том, как я заставил всю безопасность работать :))

По сути, мне нужно было извлечь открытые ключи с сервера и сохранить их в хранилище ключей внутри моего апплета, и когда я подключился к серверу, я использовал это хранилище ключей для создания фабрики доверия и эту фабрику доверия для создания ssl подключение. Также существуют альтернативные процедуры, такие как добавление ключа к доверенному узлу JVM и изменение хранилища доверенных сертификатов по умолчанию при запуске.

Я сделал это около двух месяцев назад, и прямо сейчас у меня нет исходного кода ... используйте Google, и вы сможете решить эту проблему. Если вы не можете отправить мне ответ, и я могу предоставить вам соответствующий исходный код для проекта .. Не знаю, решит ли это вашу проблему, поскольку вы не предоставили код, вызывающий эти исключения. Кроме того, я работал с апплетами, и думал, что не понимаю, почему они не работают на серверлетах ...

PS Я не могу получить исходный код до выходных, так как у меня в офисе отключен внешний SSH :(

Усама Джавед
источник
1
PS Я нашел этот java-код в Интернете для извлечения и хранения открытых ключей моего сервера, так что продолжайте поискать в Google или подождите до воскресенья
Усама Джавед
-8

Теперь я решил эту проблему таким образом,

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
«Нет необходимости внедрять» - это совершенно неверно. Вы только что сделали ваше SSL-соединение небезопасным. Не делай этого.
Маркиз Лорн,