Принять самоподписанный ssl-сертификат сервера в клиенте Java
Это похоже на стандартный вопрос, но я нигде не смог найти четких указаний.
У меня есть код Java, пытающийся подключиться к серверу с, вероятно, самозаверяющим (или просроченным) сертификатом. Код сообщает о следующей ошибке:
[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught
when processing request: sun.security.validator.ValidatorException: PKIX path
building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
Насколько я понимаю, я должен использовать keytool и сказать Java, что все в порядке, чтобы разрешить это соединение.
Все инструкции по устранению этой проблемы предполагают, что я полностью владею keytool, например:
создать приватный ключ для сервера и импортировать его в хранилище ключей
Есть ли кто-нибудь, кто мог бы опубликовать подробные инструкции?
Я использую Unix, поэтому лучше использовать bash-скрипт.
Не уверен, что это важно, но код выполняется в jboss.
18 ответов
У вас есть два основных варианта: добавить самозаверяющий сертификат в хранилище доверенных сертификатов JVM или настроить свой клиент на
Опция 1
Экспортируйте сертификат из вашего браузера и импортируйте его в доверенное хранилище JVM (для создания цепочки доверия):
<JAVA_HOME>\bin\keytool -import -v -trustcacerts
-alias server-alias -file server.cer
-keystore cacerts.jks -keypass changeit
-storepass changeit
Вариант 2
Отключить проверку сертификата:
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
// 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 (GeneralSecurityException e) {
}
// Now you can access an https URL without having the certificate in the truststore
try {
URL url = new URL("https://hostname/index.html");
} catch (MalformedURLException e) {
}
Обратите внимание, что я не рекомендую вариант № 2 вообще. Отключение диспетчера доверия разрушает некоторые части SSL и делает вас уязвимым для атак посредников. Предпочитайте вариант № 1 или, что еще лучше, используйте на сервере "настоящий" сертификат, подписанный хорошо известным центром сертификации.
Есть лучшая альтернатива доверять всем сертификатам: создать TrustStore
который специально доверяет данному сертификату и использует его для создания SSLContext
из которого получить SSLSocketFactory
установить на HttpsURLConnection
. Вот полный код:
File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
Вы также можете загрузить KeyStore
непосредственно из файла или получить сертификат X.509 из любого надежного источника.
Обратите внимание, что с этим кодом сертификаты в cacerts
не будет использоваться. Этот конкретныйHttpsURLConnection
будет доверять только этому конкретному сертификату.
Apache HttpClient 4.5 поддерживает прием самоподписанных сертификатов:
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(new TrustSelfSignedStrategy())
.build();
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> reg =
RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", socketFactory)
.build();
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse sslResponse = httpClient.execute(httpGet);
Это создает фабрику сокетов SSL, которая будет использовать TrustSelfSignedStrategy
регистрирует его с помощью настраиваемого диспетчера подключений, затем выполняет HTTP GET с использованием этого диспетчера подключений.
Я согласен с теми, кто поет "не делай этого на производстве", однако есть варианты использования для принятия самозаверяющих сертификатов вне производства; мы используем их в автоматизированных интеграционных тестах, поэтому мы используем SSL (как в рабочей среде), даже если он не работает на производственном оборудовании.
Я преследовал эту проблему до поставщика сертификатов, который не является частью доверенных хостов JVM по умолчанию с JDK 8u74
, Поставщик https://www.identrust.com/, но это не тот домен, к которому я пытался подключиться. Этот домен получил свой сертификат от этого провайдера. См. Будет ли покрытие через корневой каталог доверять списку по умолчанию в JDK / JRE? - прочитайте пару записей. Также см., Какие браузеры и операционные системы поддерживают Let's Encrypt.
Итак, для подключения к интересующему меня домену, у которого был выдан сертификат от identrust.com
Я сделал следующие шаги. В основном мне нужно было получить identrust.com (DST Root CA X3
) сертификат, которому доверяет JVM. Я смог сделать это с помощью Apache HttpComponents 4.5 следующим образом:
1: Получите сертификат от недоверенных в инструкциях по загрузке цепочки сертификатов. Нажмите на ссылку DST Root CA X3.
2. Сохраните строку в файл с именем "DST Root CA X3.pem". Не забудьте добавить в файл строки "-----BEGIN CERTIFICATE-----" и "-----END CERTIFICATE-----" в начале и конце файла.
3. Создайте файл хранилища ключей Java, cacerts.jks, с помощью следующей команды:
keytool -import -v -trustcacerts -alias IdenTrust -keypass yourpassword -file dst_root_ca_x3.pem -keystore cacerts.jks -storepass yourpassword
4: Скопируйте полученное хранилище ключей cacerts.jks в каталог ресурсов вашего приложения java/(maven).
5: используйте следующий код, чтобы загрузить этот файл и прикрепить его к Apache 4.5 HttpClient. Это решит проблему для всех доменов, у которых есть сертификаты indetrust.com
Утилита Oracle включает сертификат в хранилище ключей JRE по умолчанию.
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(CalRestClient.class.getResource("/cacerts.jks").getFile()), "yourpasword".toCharArray(),
new TrustSelfSignedStrategy())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
Когда проект будет собран, cacerts.jks будет скопирован в путь к классам и загружен оттуда. В данный момент я не тестировал другие ssl-сайты, но если приведенный выше код "цепочек" в этом сертификате, они тоже будут работать, но опять же, я не знаю.
Ссылка: пользовательский контекст SSL и как принять самозаверяющий сертификат с Java HttpsURLConnection?
Вместо того, чтобы устанавливать фабрику сокетов по умолчанию (что является IMO - плохая вещь) - это повлияет на текущее соединение, а не на каждое соединение SSL, которое вы пытаетесь открыть:
URLConnection connection = url.openConnection();
// JMD - this is a better way to do it that doesn't override the default SSL factory.
if (connection instanceof HttpsURLConnection)
{
HttpsURLConnection conHttps = (HttpsURLConnection) connection;
// Set up a Trust all manager
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)
{
}
} };
// Get a new SSL context
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// Set our connection to use this SSL context, with the "Trust all" manager in place.
conHttps.setSSLSocketFactory(sc.getSocketFactory());
// Also force it to trust all hosts
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// and set the hostname verifier.
conHttps.setHostnameVerifier(allHostsValid);
}
InputStream stream = connection.getInputStream();
Загрузите самозаверяющий сертификат с помощью браузера с целевой страницы и добавьте его в хранилище по умолчанию с паролем по умолчанию :
keytool -import -v -trustcacerts -file selfsigned.crt -alias myserver -keystore /etc/alternatives/jre/lib/security/cacerts -storepass changeit
Использовать файл
$JAVA_HOME/jre/lib/security/cacerts
, мой пример здесь из Oracle linux 7.7.
Это первый древний вопрос со слишком большим количеством ответов, на который я думаю, что могу предложить более полезную идею: этот вариант я бы использовал, если владелец сервера отказывается предоставить мне свой сертификат в автономном режиме заслуживающим доверия способом:
- Получить сертификат с самого сервера (используя инструменты командной строки вместо браузера)
- Добавьте этот сертификат в хранилище ключей Java, чтобы доверять ему. Вам будут показаны сведения о сертификате для его проверки.
# HOSTNAME_PORT is the host that you want to connect to - example: HOSTNAME_PORT=stackoverflow.com:443
HOSTNAME_PORT=hostname_part_of_url_without_https:port
# whatever you want to call the key within the Java key store
MY_KEY_ALIAS=the_key_I_added_with_help_from_stackoverflow
openssl s_client -showcerts -connect $HOSTNAME_PORT </dev/null 2>/dev/null|openssl x509 -outform PEM >mycertfile.pem
sudo keytool -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts/pki/java/cacerts -storepass changeit -importcert -alias $MY_KEY_ALIAS -file mycertfile.pem
Введите yes при появлении запроса, но только в том случае, если вы действительно доверяете показанному вам сертификату и хотите добавить его в глобальное хранилище ключей Java на вашем компьютере.
К вашему сведению:/etc/pki/java/cacerts
Доверяйте всем SSL-сертификатам:- Вы можете обойти SSL, если хотите провести тестирование на тестовом сервере. Но не используйте этот код для производства.
public static class NukeSSLCerts {
protected static final String TAG = "NukeSSLCerts";
public static void nuke() {
try {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] myTrustedAnchors = new X509Certificate[0];
return myTrustedAnchors;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
} catch (Exception e) {
}
}
}
Пожалуйста, вызовите эту функцию в функции onCreate() в Activity или в вашем классе приложения.
NukeSSLCerts.nuke();
Это может быть использовано для залпа в Android.
Если "они" используют самозаверяющий сертификат, они сами должны предпринять шаги, необходимые для того, чтобы сделать их сервер работоспособным. В частности, это означает предоставление вам сертификата в автономном режиме надежным способом. Так что заставь их сделать это. Затем вы импортируете это в ваше хранилище доверенных сертификатов, используя keytool, как описано в Справочном руководстве JSSE. Даже не думайте о небезопасном TrustManager, размещенном здесь.
РЕДАКТИРОВАТЬ В интересах семнадцати (!) Downvoters и многочисленных нижеприведенных комментариев, которые явно не читали то, что я здесь написал, это не является иеремией против самозаверяющих сертификатов. Нет ничего плохого в самозаверяющих сертификатах, если они реализованы правильно. Но правильным способом их реализации является безопасная доставка сертификата через автономный процесс, а не через канал без аутентификации, который они будут использовать для аутентификации. Конечно, это очевидно? Это, безусловно, очевидно для каждой организации, в которой я работаю в сфере безопасности, от банков с тысячами отделений до моих собственных компаний. Клиентское "кодовое" решение "доверять всем сертификатам, включая самозаверяющие сертификаты, подписанные абсолютно кем-либо, или любым арбитражным органом, устанавливающим себя в качестве ЦС, ipso facto небезопасно. Это просто игра в безопасности. Это бессмысленно. У вас частная беседа, защищенная от взлома, ответа и инъекции, с... кем-то. Кто-нибудь. Человек посередине. Имитатор. Кто-нибудь. Вы также можете просто использовать открытый текст.
У меня была проблема, когда я передавал URL-адрес в библиотеку, которая вызывала url.openConnection();
Я адаптировал ответ Джон-Дэниела,
public class TrustHostUrlStreamHandler extends URLStreamHandler {
private static final Logger LOG = LoggerFactory.getLogger(TrustHostUrlStreamHandler.class);
@Override
protected URLConnection openConnection(final URL url) throws IOException {
final URLConnection urlConnection = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile()).openConnection();
// adapated from
// https://stackru.com/questions/2893819/accept-servers-self-signed-ssl-certificate-in-java-client
if (urlConnection instanceof HttpsURLConnection) {
final HttpsURLConnection conHttps = (HttpsURLConnection) urlConnection;
try {
// Set up a Trust all manager
final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(final java.security.cert.X509Certificate[] certs, final String authType) {
}
@Override
public void checkServerTrusted(final java.security.cert.X509Certificate[] certs, final String authType) {
}
} };
// Get a new SSL context
final SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// Set our connection to use this SSL context, with the "Trust all" manager in place.
conHttps.setSSLSocketFactory(sc.getSocketFactory());
// Also force it to trust all hosts
final HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(final String hostname, final SSLSession session) {
return true;
}
};
// and set the hostname verifier.
conHttps.setHostnameVerifier(allHostsValid);
} catch (final NoSuchAlgorithmException e) {
LOG.warn("Failed to override URLConnection.", e);
} catch (final KeyManagementException e) {
LOG.warn("Failed to override URLConnection.", e);
}
} else {
LOG.warn("Failed to override URLConnection. Incorrect type: {}", urlConnection.getClass().getName());
}
return urlConnection;
}
}
Используя этот класс, можно создать новый URL с помощью:
trustedUrl = new URL(new URL(originalUrl), "", new TrustHostUrlStreamHandler());
trustedUrl.openConnection();
Это имеет то преимущество, что он локализован и не заменяет стандартный URL.openConnection
.
Принятый ответ в порядке, но я хотел бы кое-что добавить к нему, поскольку я использовал IntelliJ на Mac и не мог заставить его работать с помощью JAVA_HOME
переменная пути.
Оказывается, Java Home отличался при запуске приложения из IntelliJ.
Чтобы точно определить, где это, вы можете просто сделать System.getProperty("java.home")
поскольку именно отсюда считываются доверенные сертификаты.
Для принятого ответа нужен вариант 3
ТАКЖЕ Вариант 2 УЖАСНЫЙ. Его НИКОГДА не следует использовать (особенно в производстве), поскольку он обеспечивает ЛОЖНОЕ чувство безопасности. Просто используйте HTTP вместо варианта 2.
ВАРИАНТ 3
Используйте самозаверяющий сертификат для подключения Https.
Вот пример:
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.KeyStore;
/*
* Use a SSLSocket to send a HTTP GET request and read the response from an HTTPS server.
* It assumes that the client is not behind a proxy/firewall
*/
public class SSLSocketClientCert
{
private static final String[] useProtocols = new String[] {"TLSv1.2"};
public static void main(String[] args) throws Exception
{
URL inputUrl = null;
String certFile = null;
if(args.length < 1)
{
System.out.println("Usage: " + SSLSocketClient.class.getName() + " <url>");
System.exit(1);
}
if(args.length == 1)
{
inputUrl = new URL(args[0]);
}
else
{
inputUrl = new URL(args[0]);
certFile = args[1];
}
SSLSocket sslSocket = null;
PrintWriter outWriter = null;
BufferedReader inReader = null;
try
{
SSLSocketFactory sslSocketFactory = getSSLSocketFactory(certFile);
sslSocket = (SSLSocket) sslSocketFactory.createSocket(inputUrl.getHost(), inputUrl.getPort() == -1 ? inputUrl.getDefaultPort() : inputUrl.getPort());
String[] enabledProtocols = sslSocket.getEnabledProtocols();
System.out.println("Enabled Protocols: ");
for(String enabledProtocol : enabledProtocols) System.out.println("\t" + enabledProtocol);
String[] supportedProtocols = sslSocket.getSupportedProtocols();
System.out.println("Supported Protocols: ");
for(String supportedProtocol : supportedProtocols) System.out.println("\t" + supportedProtocol + ", ");
sslSocket.setEnabledProtocols(useProtocols);
/*
* Before any data transmission, the SSL socket needs to do an SSL handshake.
* We manually initiate the handshake so that we can see/catch any SSLExceptions.
* The handshake would automatically be initiated by writing & flushing data but
* then the PrintWriter would catch all IOExceptions (including SSLExceptions),
* set an internal error flag, and then return without rethrowing the exception.
*
* This means any error messages are lost, which causes problems here because
* the only way to tell there was an error is to call PrintWriter.checkError().
*/
sslSocket.startHandshake();
outWriter = sendRequest(sslSocket, inputUrl);
readResponse(sslSocket);
closeAll(sslSocket, outWriter, inReader);
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
closeAll(sslSocket, outWriter, inReader);
}
}
private static PrintWriter sendRequest(SSLSocket sslSocket, URL inputUrl) throws IOException
{
PrintWriter outWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(sslSocket.getOutputStream())));
outWriter.println("GET " + inputUrl.getPath() + " HTTP/1.1");
outWriter.println("Host: " + inputUrl.getHost());
outWriter.println("Connection: Close");
outWriter.println();
outWriter.flush();
if(outWriter.checkError()) // Check for any PrintWriter errors
System.out.println("SSLSocketClient: PrintWriter error");
return outWriter;
}
private static void readResponse(SSLSocket sslSocket) throws IOException
{
BufferedReader inReader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
String inputLine;
while((inputLine = inReader.readLine()) != null)
System.out.println(inputLine);
}
// Terminate all streams
private static void closeAll(SSLSocket sslSocket, PrintWriter outWriter, BufferedReader inReader) throws IOException
{
if(sslSocket != null) sslSocket.close();
if(outWriter != null) outWriter.close();
if(inReader != null) inReader.close();
}
// Create an SSLSocketFactory based on the certificate if it is available, otherwise use the JVM default certs
public static SSLSocketFactory getSSLSocketFactory(String certFile)
throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException
{
if (certFile == null) return (SSLSocketFactory) SSLSocketFactory.getDefault();
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(new File(certFile)));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
return sslContext.getSocketFactory();
}
}
Это не полное решение проблемы, но у Oracle есть хорошая подробная документация по использованию этого инструментария. Это объясняет, как
- используйте keytool.
- генерировать сертификаты / самоподписанные сертификаты с помощью keytool.
- импортировать сгенерированные сертификаты для клиентов Java.
https://docs.oracle.com/cd/E54932_01/doc.705/e54936/cssg_create_ssl_cert.htm
Вариант в Котлине
@SuppressLint("CustomX509TrustManager", "TrustAllX509TrustManager")
fun ignoreSsl() {
val trustAllCerts: Array<TrustManager> = arrayOf(
object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate>? = null
override fun checkClientTrusted(certs: Array<X509Certificate?>?, authType: String?) {}
override fun checkServerTrusted(certs: Array<X509Certificate?>?, authType: String?) {}
})
val sc = SSLContext.getInstance("SSL")
sc.init(null, trustAllCerts, SecureRandom())
HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory)
}
Вдохновленный приведенным ниже сообщением, я нахожу способ доверять самозаверяющим и сохранять доверие ЦС по умолчанию.
File file = new File(System.getProperty("java.home"), "lib/security/cacerts");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(file), "changeit".toCharArray());
InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("testCer.cer");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(resourceAsStream);
keyStore.setCertificateEntry("my-server-alias", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContexts.createDefault();
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
// check domain
// SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
// not check domain
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext,
new String[]{"TLSv1","TLSv1.1","TLSv1.2","SSLv3"},null, NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
factory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
Я столкнулся с этой проблемой, потому что репозиторий maven, откуда исходят зависимости, — это мой локальный сервер с самозаверяющим сертификатом и самозаверяющим сертификатом CA. Чтобы устранить ошибку, мне пришлось запустить эти две команды:
<my_java_install_dir>\bin\keytool.exe -importcert -file <my-self-signed-CA-cert>.crt -keystore <my_java_install_dir>\lib\security\cacerts -alias my-CA-cert
а потом
<my_java_install_dir>\jdk11.0.14_10\bin\keytool.exe -importcert -file <my-self-signed-maven-repo-cert>.crt -keystore <my_java_install_dir>\lib\security\cacerts -alias my-maven-repo-cert
Опаздывая на вечеринку, вы можете сделать это, отключив его на уровне RestTemplate. Обратите внимание, что эта TrustStrategy будет доверять всем сертификатам, и вы отключите проверку имени хоста с помощью NoopHostnameVerifier().
public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return new RestTemplate(requestFactory);
}
Вместо использования keytool, как предложено в верхнем комментарии, в RHEL вы можете использовать update-ca-trust, начиная с более новых версий RHEL 6. Вам потребуется сертификат в формате pem. потом
trust anchor <cert.pem>
Отредактируйте /etc/pki/ca-trust/source/cert.p11-kit и замените "категория сертификата: другая-запись" на "категория сертификата: орган". (Или используйте sed, чтобы сделать это в скрипте.) Затем выполните
update-ca-trust
Пара предостережений:
- Я не мог найти "доверие" на моем сервере RHEL 6, и yum не предлагал его установить. В итоге я использовал его на сервере RHEL 7 и скопировал файл.p11-kit.
- Чтобы это работало для вас, вам может потребоваться
update-ca-trust enable
. Это заменит /etc/pki/java/cacerts символической ссылкой, указывающей на / etc / pki / ca-trust / извлеченный / java / cacerts. (Так что вы можете сначала сделать резервную копию первого.) - Если ваш java-клиент использует cacerts, хранящиеся в каком-то другом месте, вы захотите вручную заменить его символической ссылкой на / etc / pki / ca-trust / extract / java / cacerts или заменить его этим файлом.