Как решить javax.net.ssl.SSLHandshakeException Ошибка?
Я подключился к 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
7 ответов
Сначала вам нужно получить публичный сертификат с сервера, к которому вы пытаетесь подключиться. Это можно сделать различными способами, например, связавшись с администратором сервера и запросив его, используя openssl для его загрузки или, поскольку это HTTP-сервер, подключающийся к нему через любой браузер, просматривающий информацию о безопасности страницы. и сохранение копии сертификата. (Google должен быть в состоянии точно сказать вам, что делать для вашего конкретного браузера.)
Теперь, когда у вас есть сертификат, сохраненный в файле, вам нужно добавить его в хранилище доверенных сертификатов вашей JVM. В $JAVA_HOME/jre/lib/security/
для JRE или $JAVA_HOME/lib/security
для JDK есть файл с именем cacerts
, который поставляется с Java и содержит публичные сертификаты известных сертифицирующих органов. Чтобы импортировать новый сертификат, запустите keytool от имени пользователя, имеющего разрешение на запись в cacerts:
keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>
Скорее всего, он попросит вас ввести пароль. Пароль по умолчанию, поставляемый с Java, changeit
, Почти никто не меняет это. Выполнив эти относительно простые шаги, вы будете общаться безопасно и с уверенностью, что говорите с нужным сервером и только с нужным сервером (при условии, что они не потеряют свой закрытый ключ).
Теперь я решил эту проблему таким образом,
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
например, локальное тестирование с временными сертификатами.
Всякий раз, когда мы пытаемся подключиться к URL,
если сервер на другом сайте работает по протоколу https и требует, чтобы мы общались через информацию, указанную в сертификате, то у нас есть следующий вариант:
1) попросить сертификат (скачать сертификат), импортировать этот сертификат в trustore. Стандартное использование Java по умолчанию можно найти в \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.
Исключение SSLHandshakeException можно разрешить двумя способами.
Включение SSL
Получить SSL (по запросу исходного системного администратора, также может быть загружен командой openssl, или любые браузеры загружают сертификаты)
Добавьте сертификат в склад доверенных сертификатов (cacerts), расположенный в JRE/lib/security
укажите расположение хранилища доверенных сертификатов в аргументах vm как -Djavax.net.ssl.trustStore=
Игнорирование SSL
Для этого #2, пожалуйста, посетите мой другой ответ на другом веб-сайте stackru: Как получить подтверждение SSL Игнорировать ошибки сертификата SSL с Java
Это то, что я сделал, чтобы отправить JSON на URL-адрес с небезопасными/недействительными сертификатами SSL, используя последнюю версию JDK 11 HttpClient:
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
try {
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Creat HttpClient with new SSLContext.
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofMillis(3 * 1000))
.sslContext(sc) // SSL context 'sc' initialised as earlier
.build();
// Create request.
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(<URL>))
.timeout(Duration.ofMinutes(1))
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(<PAYLOAD JSON STRING>))
.build();
//Send Request
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
//Access response JSON
String responseJson = response.body();
} catch (Exception e) {
throw new Exception(e);
}
Я считаю, что вы пытаетесь подключиться к чему-либо с помощью SSL, но что-то предоставляет сертификат, который не проверен корневыми центрами сертификации, такими как verisign. По сути, по умолчанию безопасные соединения могут быть установлены только в том случае, если человек, пытающийся подключиться, знает ключи контрагентов или другие поставщики, такие как verisign, могут вмешаться и сказать, что предоставляемый открытый ключ действительно верен.
ВСЕ ОС доверяют горстке сертификационных органов и более мелких эмитентов сертификатов, которые должны быть сертифицированы одним из крупных сертификаторов, составляющих цепочку сертификаторов, если вы понимаете, о чем я...
В любом случае возвращаюсь к сути... У меня была похожая проблема при программировании Java-апплета и Java-сервера (Надеюсь, когда-нибудь я напишу полный пост в блоге о том, как я установил всю безопасность:))
По сути, мне нужно было извлечь открытые ключи с сервера и сохранить их в хранилище ключей внутри моего апплета, и когда я подключился к серверу, я использовал это хранилище ключей для создания фабрики доверия и эту фабрику доверия для создания ssl подключение. Существуют также альтернативные процедуры, такие как добавление ключа к доверенному хосту JVM и изменение хранилища доверия по умолчанию при запуске.
Я сделал это около двух месяцев назад, и сейчас у меня нет исходного кода. Используйте Google, и вы сможете решить эту проблему. Если вы не можете отправить мне сообщение, и я могу предоставить вам соответствующий исходный код для проекта. Не знаю, решит ли это вашу проблему, так как вы не предоставили код, который вызывает эти исключения. Кроме того, я работал с апплетами, думал, что не понимаю, почему он не работает на Serverlets...
PS Я не могу получить исходный код до выходных, поскольку внешний SSH отключен в моем офисе:(
Теперь я решил эту проблему таким образом,
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);
}