У меня есть сервер с самозаверяющим сертификатом. Я импортировал его с помощью keytool на своем компьютере и использовал
-Djavax.net.ssl.trustStore=blabla
компилировать аргумент. Когда я пытаюсь запустить следующий код:
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket("MY_URL_DIGITS", 443);
OutputStream os = socket.getOutputStream();
os.write("Test request \n".getBytes());
os.flush();
os.close();
Все идет хорошо, и я вижу "тестовый запрос" на сервере. Однако, когда я запускаю:
URL url = new URL("https://MY_URL_DIGITS");
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
OutputStream os = con.getOutputStream();
os.write("Test request \n".getBytes());
os.flush();
os.close();
У меня есть
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present
Итак, какова принципиальная разница между этими двумя фрагментами?
SSLSocket по умолчанию проверяет, доверяете ли вы сертификату. HttpsURLConnection проверяет, доверяете ли вы сертификату, а также проверяет, говорит ли он, что он поступает из того же места, на котором вы фактически перешли. Для успешного выполнения HttpsURLConnection сертификат должен указать альтернативное имя объекта (SAN), которое было таким же, как сервер, к которому вы подключаетесь. В этом случае SAN должен быть "dns: MY_URL_DIGITS", где часть "dns" говорит, что вы указываете имя хоста, а не IP-адрес.
Если вам нужна дополнительная информация о том, как создать сертификат с альтернативным именем объекта, см.
Разница в том, что HTTPS добавляет шаг, который можно увидеть в интерфейсе HostnameVerifier
. Он пытается сопоставить имя хоста, связанное с именем хоста в SubjectDN
или альтернативных именах.