Решение javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Ошибка?

Редактировать: - Попытался отформатировать вопрос и принял ответ более презентабельным способом в моем блоге

Вот оригинальная проблема.

Я получаю эту ошибку:

подробное сообщение sun.security.validator.ValidatorException: не удалось построить путь PKIX:
sun.security.provider.certpath.SunCertPathBuilderException: невозможно найти действительный путь сертификации к запрошенной цели

причиной javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: сбой построения пути PKIX: sun.security.provider.certpath.SunCertPathBuilderException: невозможно найти действительный путь сертификации для запрошенной цели

Я использую Tomcat 6 в качестве веб-сервера. У меня есть два веб-приложения HTTPS, установленные на разных Tomcats на разных портах, но на одной машине. Скажите App1(port 8443) и App2(port 443). App1 подключается к App2. Когда App1 подключается к App2 я получаю вышеуказанную ошибку. Я знаю, что это очень распространенная ошибка, поэтому сталкивался со многими решениями на разных форумах и сайтах. У меня есть запись ниже в server.xml обоих Tomcats:

keystoreFile="c:/.keystore" 
keystorePass="changeit"

На каждом сайте указана одна и та же причина, по которой сертификат, предоставленный app2, отсутствует в доверенном хранилище app1 jvm. Кажется, это также верно, когда я пытался перейти по тому же URL-адресу в браузере IE, он работает (с потеплением. Существует проблема с этим сертификатом безопасности веб-сайта. Здесь я говорю, перейдите на этот веб-сайт). Но когда Java-клиент использует тот же URL-адрес (в моем случае), я получаю вышеуказанную ошибку. Поэтому, чтобы положить его в доверенное хранилище, я попробовал эти три варианта:

Опция 1

System.setProperty("javax.net.ssl.trustStore", "C:/.keystore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

Option2 Настройка ниже в переменной среды

CATALINA_OPTS -- param name
-Djavax.net.ssl.trustStore=C:\.keystore -Djavax.net.ssl.trustStorePassword=changeit ---param value

Option3 Настройка ниже в переменной среды

JAVA_OPTS -- param name
-Djavax.net.ssl.trustStore=C:\.keystore -Djavax.net.ssl.trustStorePassword=changeit ---param value

Но ничего не сработало.

Наконец, сработало выполнение подхода Java, предложенного в разделе Как обрабатывать недействительные сертификаты SSL с помощью Apache HttpClient? по Паскалю Thivent, т.е. выполнение программы InstallCert.

Но этот подход хорош для установки devbox, но я не могу использовать его в производственной среде.

Мне интересно, почему три упомянутых выше подхода не сработали, когда я упомянул одинаковые значения в server.xml сервера app2 и одинаковые значения в хранилище доверенных сертификатов, установив

System.setProperty("javax.net.ssl.trustStore", "C:/.keystore") and System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

в программе app1.

Для получения дополнительной информации вот как я делаю соединение:

URL url = new URL(urlStr);

URLConnection conn = url.openConnection();

if (conn instanceof HttpsURLConnection) {

  HttpsURLConnection conn1 = (HttpsURLConnection) url.openConnection();

  conn1.setHostnameVerifier(new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
      return true;
    }
  });

  reply.load(conn1.getInputStream());

Ответ 1

Вам необходимо добавить сертификат для App2 в файл хранилища доверенных сертификатов используемой JVM, расположенный по адресу %JAVA_HOME%\lib\security\cacerts.

Сначала вы можете проверить, находится ли ваш сертификат в хранилище доверенных сертификатов, выполнив следующую команду: keytool -list -keystore "%JAVA_HOME%/jre/lib/security/cacerts" (вам не нужно keytool -list -keystore "%JAVA_HOME%/jre/lib/security/cacerts" пароль)

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

keytool -import -noprompt -trustcacerts -alias <AliasName> -file <certificate> -keystore <KeystoreFile> -storepass <Password>

После импорта вы можете снова запустить первую команду, чтобы проверить, был ли добавлен ваш сертификат.

Информация Sun/Oracle может быть найдена здесь.

Ответ 2

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: Не удалось создать путь PKIX: sun.security.provider.certpath.SunCertPathBuilderException: не удалось найти допустимый путь сертификации для запрошенной цели

• Когда я получил сообщение об ошибке, я попытался выяснить значение выражения в Google, и я обнаружил, что эта проблема возникает, когда сервер изменяет свой SSL-сертификат HTTPS, а наша более ранняя версия java не распознает корневой центр сертификации (CA).

• Если вы можете получить доступ к URL-адресу HTTPS в своем браузере, тогда можно обновить Java для распознавания корневого ЦС.

• В своем браузере перейдите к URL-адресу HTTPS, к которому Java не может получить доступ. Нажмите на цепочку сертификатов HTTPS (в Internet Explorer есть значок блокировки), щелкните по блокировке, чтобы просмотреть сертификат.

• Перейдите к "Подробности" сертификата и "Скопируйте в файл". Скопируйте его в формате Base64 (.cer). Он будет сохранен на вашем рабочем столе.

• Установите сертификат, игнорируя все предупреждения.

• Вот как я собрал информацию о сертификате URL-адреса, к которому я пытался получить доступ.

Теперь мне нужно было сделать мою версию Java, чтобы узнать о сертификате, чтобы в дальнейшем он не отказался распознавать URL. В этом отношении я должен упомянуть, что я искал информацию о корневом сертификате по умолчанию в JDKs \jre\lib\security, а пароль для доступа по умолчанию: changeit.

Чтобы просмотреть информацию о cacerts, выполните следующие действия:

• Нажмите кнопку "Пуск" → "Выполнить"

• Введите cmd. Откроется командная строка (возможно, вам нужно будет открыть ее как администратор).

• Перейдите в каталог Java/jreX/bin

• Введите следующий

keytool -list -keystore D:\Java\jdk1.5.0_12\jre\lib\security\cacerts

Он дает список текущих сертификатов, содержащихся в хранилище ключей. Это выглядит примерно так:

C:\Documents and Settings\NeelanjanaG > keytool -list -keystore D:\Java\jdk1.5.0_12\jre\lib\security\cacerts

Введите пароль хранилища ключей: changeit

Тип хранилища: jks

Поставщик ключей: SUN

Ваше хранилище ключей содержит 44 записи

verisignclass3g2ca, 26 марта 2004 г., trustedCertEntry,

Отпечаток сертификата (MD5): A2: 33: 9B: 4C: 74: 78: 73: D4: 6C: E7: C1: F3: 8D: CB: 5C: E9

entrustclientca, 9 января 2003 г., trustedCertEntry,

Отпечаток сертификата (MD5): 0C: 41: 2F: 13: 5B: A0: 54: F5: 96: 66: 2D: 7E: CD: 0E: 03: F4

thawtepersonalbasicca, 13 февраля 1999 г., trustedCertEntry,

Отпечаток сертификата (MD5): E6: 0B: D2: C9: CA: 2D: 88: DB: 1A: 71: 0E: 4B: 78: EB: 02: 41

addtrustclass1ca, 1 мая 2006 г., trustedCertEntry,

Отпечаток сертификата (MD5): 1E: 42: 95: 02: 33: 92: 6B: B9: 5F: C0: 7F: DA: D6: B2: 4B: FC

verisignclass2g3ca, 26 марта 2004 г., trustedCertEntry,

Отпечаток сертификата (MD5): F8: BE: C4: 63: 22: C9: A8: 46: 74: 8B: B8: 1D: 1E: 4A: 2B: F6

• Теперь мне пришлось включить ранее установленный сертификат в cacerts.

• Для этого выполняется процедура:

keytool -import -noprompt -trustcacerts -alias ALIASNAME файл FILENAME_OF_THE_INSTALLED_CERTIFICATE -keystore PATH_TO_CACERTS_FILE -storepass PASSWORD

Если вы используете Java 7:

keytool -importcert -trustcacerts -alias ALIASNAME файл PATH_TO_FILENAME_OF_THE_INSTALLED_CERTIFICATE -keystore PATH_TO_CACERTS_FILE -storepass changeit

• Затем он добавит информацию сертификата в файл cacert.

Это решение, которое я нашел для вышеупомянутого исключения!

Ответ 3

Как работать - в Tomcat 7

Я хотел поддержать самоподписанный сертификат в приложении Tomcat, но не удалось выполнить следующий сниппет

import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class HTTPSPlayground {
    public static void main(String[] args) throws Exception {

        URL url = new URL("https:// ... .com");
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();

        httpURLConnection.setRequestMethod("POST");
        httpURLConnection.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
        httpURLConnection.setDoOutput(true);
        DataOutputStream wr = new DataOutputStream(httpURLConnection.getOutputStream());

        String serializedMessage = "{}";
        wr.writeBytes(serializedMessage);
        wr.flush();
        wr.close();

        int responseCode = httpURLConnection.getResponseCode();
        System.out.println(responseCode);
    }
}

вот что решило мою проблему:

1) Загрузите .crt файл

echo -n | openssl s_client -connect <your domain>:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/<your domain>.crt
  • замените <your domain> на свой домен (например, jossef.com)

2) Примените файл .crt в хранилище сертификатов Java cacerts

keytool -import -v -trustcacerts -alias <your domain> -file ~/<your domain>.crt -keystore <JAVA HOME>/jre/lib/security/cacerts -keypass changeit -storepass changeit
  • замените <your domain> на свой домен (например, jossef.com)
  • замените <JAVA HOME> на ваш домашний каталог java

3) Взломайте его

Даже если я установил свой сертификат в хранилища сертификатов Java по умолчанию, Tomcat игнорирует этот (похоже, он не настроен на использование хранилищ сертификатов по умолчанию Java).

Чтобы взломать это, добавьте следующее в код:

String certificatesTrustStorePath = "<JAVA HOME>/jre/lib/security/cacerts";
System.setProperty("javax.net.ssl.trustStore", certificatesTrustStorePath);

// ...

Ответ 4

В моем случае проблема заключалась в том, что веб-сервер отправлял только сертификат и промежуточный ЦС, а не корневой ЦС. Добавление этой опции JVM решило проблему: -Dcom.sun.security.enableAIAcaIssuers=true

Доступна поддержка метода доступа caIssuers расширения Authority Information Access. По умолчанию он отключен для совместимости и может быть включен путем установки системному свойству com.sun.security.enableAIAcaIssuers значения true.

Если задано значение true, реализация CertPathBuilder в Sun PKIX использует информацию в расширении AIA сертификата (в дополнение к указанному CertStores) для поиска сертификата выдающего ЦС при условии, что это URI типа ldap, http или ftp.

Источник

Ответ 5

Другой причиной может быть устаревшая версия JDK. Я использовал jdk версии 1.8.0_60, просто обновление до последней версии решило проблему с сертификатом.

Ответ 6

Мой файл cacerts был полностью пуст. Я решил это, скопировав файл cacerts с моей машины Windows (используя Oracle Java 7) и scp'd в мой Linux-блок (OpenJDK).

cd %JAVA_HOME%/jre/lib/security/
scp cacerts mylinuxmachin:/tmp

а затем на машине linux

cp /tmp/cacerts /etc/ssl/certs/java/cacerts

До сих пор он отлично работал.

Ответ 7

Используя Tomcat 7 под Linux, это сделало трюк.

String certificatesTrustStorePath = "/etc/alternatives/jre/lib/security/cacerts";
System.setProperty("javax.net.ssl.trustStore", certificatesTrustStorePath);
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

В Linux $JAVA_HOME не всегда настроен, но обычно /etc/alternatives/jre указывает на $JAVA_HOME/jre

Ответ 8

Ниже код работает для меня:

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

public class TrustAnyTrustManager implements X509TrustManager {

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}

public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[] {};
}
}

HttpsURLConnection conn = null;
            URL url = new URL(serviceUrl);
            conn = (HttpsURLConnection) url.openConnection();
             SSLContext sc = SSLContext.getInstance("SSL");  
             sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());  

             conn.setSSLSocketFactory(sc.getSocketFactory());

Ответ 9

Для меня эта ошибка также возникла при попытке подключиться к процессу с обратным прокси-сервером NGINX, который обрабатывал SSL.

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

Надеюсь, что это поможет.

Ответ 10

Я использовал jdk1.8.0_171 когда столкнулся с той же проблемой. Я попробовал два лучших решения здесь (добавление сертификата с помощью keytool и другого решения, в котором есть хак), но они не спомогли мне.

Я обновил свой JDK до 1.8.0_181 и он работал, как шарм.

Ответ 11

Я написал небольшой win32 (WinXP 32bit testet) stupid cmd (commandline) script, который ищет все версии java в файлах программ и добавляет к ним сертификат. Пароль должен быть по умолчанию "changeit" или изменить его самостоятельно в script: -)

@echo off

for /F  %%d in ('dir /B %ProgramFiles%\java') do (
    %ProgramFiles%\Java\%%d\bin\keytool.exe -import -noprompt -trustcacerts -file some-exported-cert-saved-as.crt -keystore %ProgramFiles%\Java\%%d\lib\security\cacerts -storepass changeit
)

pause

Ответ 12

Для Tomcat, работающего на сервере Ubuntu, чтобы узнать, какая Java используется, используйте команду "ps -ef | grep tomcat":

Пример:

/home/mcp01$ **ps -ef |grep tomcat**
tomcat7  28477     1  0 10:59 ?        00:00:18 **/usr/local/java/jdk1.7.0_15/bin/java** -Djava.util.logging.config.file=/var/lib/tomcat7/conf/logging.properties -Djava.awt.headless=true -Xmx512m -XX:+UseConcMarkSweepGC -Djava.net.preferIPv4Stack=true -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/share/tomcat7/endorsed -classpath /usr/share/tomcat7/bin/bootstrap.jar:/usr/share/tomcat7/bin/tomcat-juli.jar -Dcatalina.base=/var/lib/tomcat7 -Dcatalina.home=/usr/share/tomcat7 -Djava.io.tmpdir=/tmp/tomcat7-tomcat7-tmp org.apache.catalina.startup.Bootstrap start
1005     28567 28131  0 11:34 pts/1    00:00:00 grep --color=auto tomcat

Затем мы можем перейти к: cd/usr/local/java/jdk1.7.0_15/jre/lib/security

Файл cacerts находится здесь. Вставьте в него ненадежный сертификат.

Ответ 13

У меня тоже есть эта проблема.

Я попробовал почти все, добавив SSL-сертификат в .keystore, но он не работал с Java1_6_x. Для меня это помогло, если мы начнем использовать более новую версию Java, Java1_8_x как JVM.

Ответ 14

для безопасности мы не должны использовать самозаверяющие сертификаты в нашей реализации. Тем не менее, когда речь заходит о разработке, нам часто приходится использовать пробные среды с самоподписанными сертификатами. Я попытался исправить эту проблему программно в моем коде, и мне это не удалось. Однако добавление сертификата в jre trust-store решило мою проблему. Пожалуйста, найдите ниже шаги,

  1. Скачать сайт сертификата,

    • Использование Chrome
    • Использование Firefox
  2. Скопируйте сертификат (например, cert_file.cer) в каталог $ JAVA_HOME\Jre\Lib\Security

  3. Откройте CMD в Администраторе и измените каталог на $ JAVA_HOME\Jre\Lib\Security

  4. Импортируйте сертификат в доверенное хранилище, используя следующую команду:

keytool -import -alias ca -file cert_file.cer -keystore cacerts -storepass changeit

Если вы получили сообщение о том, что keytool не распознается, обратитесь к этому.

Тип да как ниже

Доверяйте этому сертификату: [Да]

  1. Теперь попробуйте запустить свой код или получить доступ к URL программно с помощью Java.

Обновить

Если ваш сервер приложений jboss, попробуйте добавить системное свойство ниже

System.setProperty("org.jboss.security.ignoreHttpsHost","true");

Надеюсь это поможет!

Ответ 15

Я столкнулся с этой проблемой, используя AndroidStudio, Charles Proxy, JUnit и Robolectric. Спасибо @SimonSez, я смог решить проблему.

Обратите внимание: Поскольку ключевым моментом является изменение cacerts, которое может вызвать некоторые проблемы (например, обновление IDE), я бы порекомендовал вам скопировать исходный файл cacerts в отдельный файл. каталог и манипулировать ими при необходимости.

  1. %JAVA_HOME% - Android использует другой JavaHome. Вы можете найти полный путь на вкладке UnitTests resulr. В моем случае это /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home

    enter image description here

  2. Загрузить сертификат Чарльза (например, папка Downloads)

  3. Запустите команду

    sudo keytool -import -noprompt -trustcacerts -alias charles -file <path to Charles certificate.pem> -keystore "JAVA_HOME/jre/lib/security/cacerts" -storepass changeit

    Например,

    sudo keytool -import -noprompt -trustcacerts -alias charles -file ~/Downloads/charles-ssl-proxying-certificate.pem -keystore "/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/jre/lib/security/cacerts" -storepass changeit

Официальный Чарльз Док

Ответ 16

Для MacOS X ниже точная команда работала для меня, где я должен был попробовать с двойным hypen в опции importcert, которая работала:

sudo keytool -–importcert -file /PathTo/YourCertFileDownloadedFromBrowserLockIcon.crt -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/security/cacerts -alias "Cert" -storepass changeit

Ответ 17

У меня была эта проблема с Android Studio, когда я за прокси. Я использовал Crashlytics, который пытается загрузить файл сопоставления во время сборки.

Я добавил недостающий сертификат прокси в склад доверенных сертификатов, расположенный по адресу /Users/[username]/Documents/Android Studio.app/Contents/jre/jdk/Contents/Home/jre/lib/security/cacerts

с помощью следующей команды: keytool -import -trustcacerts -keystore cacerts -storepass [password] -noprompt -alias [alias] -file [my_certificate_location]

например, с паролем хранилища доверенных сертификатов по умолчанию keytool -import -trustcacerts -keystore cacerts -storepass changeit -noprompt -alias myproxycert -file /Users/myname/Downloads/MyProxy.crt

Ответ 18

добавьте это в свой код:

TrustManager[] trustAllCerts = new TrustManager[]{
           new X509TrustManager() {
               @Override
               public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                   return new X509Certificate[0];
               }

               @Override
               public void checkClientTrusted(
                       java.security.cert.X509Certificate[] certs, String authType) {
               }

               @Override
               public void checkServerTrusted(
                       java.security.cert.X509Certificate[] certs, String authType) {
               }
           }
       };

       try {
           SSLContext sc = SSLContext.getInstance("SSL");
           sc.init(null, trustAllCerts, new java.security.SecureRandom());
           HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
       } catch (GeneralSecurityException e) {
       }

Ответ 19

Его недостаток Java не использует стандартное хранилище ключей операционной системы, как в MacOS X. Я подал заявку на изменение сегодня, см. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8185892