Не зная, что такое SNI, я попытался понять с помощью тестовой программы, показанной ниже.
Я не знаю вывод команды openssl s_client
, но тестовая программа может оказаться отправной точкой. Когда вывод javax.net.debug
включен, выводится много выходных данных, из которых релевантны только несколько строк (см. Также комментарии). Это немного раздражает, и у меня нет простого решения для этого. Класс TrustAllServers
может быть переработан для проверки сертификатов, которые вы ожидаете получить от сервера (a.ka. host) для определенного домена. Могут быть и другие варианты (например, методы рукопожатия сокета), но это насколько я понял.
<code>import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
// /8451691/java-ekvivalentnyi-komande-openssl-sclient
// Please use latest Java 8 version, bugs are around in earlier versions.
public class ServerNameTest {
public static void main(String[] args) {
// SSL debug options, see https://stackoverflow.com/q/23659564/3080094 and https://access.redhat.com/solutions/973783
// System.setProperty("javax.net.debug", "all");
// System.setProperty("javax.net.debug", "ssl:handshake");
// System.setProperty("jsse.enableSNIExtension", "true"); // "true" is the default
try {
ServerNameTest sn = new ServerNameTest();
// This will show 2 different server certificate chains.
// Note this is a random server - please pick your own one.
sn.test("major.io", "rackerhacker.com");
sn.test("major.io", "major.io");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Done");
}
/*
* With javax.net.debug output you should see something like:
* <pre>
* *** ClientHello
* ...
* Extension server_name, server_name: [type=host_name (0), value=DOMAIN;]
* ...
* *** ServerHello
* ...
* Extension server_name, server_name:
* ...
*
* Обратите внимание, что если сервер не предоставляет значение для имя_сервера,
* это на самом деле не означает, что сервер не поддерживает SNI / имя_сервера (см.
https://serverfault.com/a/506303)
* /
void test (String host, String domain) выдает исключение {
SSLParameters sslParams = новые SSLParameters ();
if (domain! = null &&! domain.isEmpty ()) {
sslParams.setServerNames (Arrays.asList (новое SNIHostName (домен)));
}
// Только для веб-серверов: установить алгоритм конечной точки на HTTPS
sslParams.setEndpointIdentificationAlgorithm ( "HTTPS");
SSLSocketFactory sslsf = serverTrustingSSLFactory ();
try (SSLSocket socket = (SSLSocket) sslsf.createSocket ()) {
socket.setSSLParameters (sslParams);
socket.setSoTimeout (3_000);
System.out.println («Подключение к« + хост + »для домена» + домен);
socket.connect (новый InetSocketAddress (хост, 443), 3_000);
// Запускаем фактическое соединение, получая сеанс.
socket.getSession ();
System.out.println («Подключен к удаленному» + socket.getRemoteSocketAddress ());
try (BufferedReader input = new BufferedReader (new InputStreamReader (socket.getInputStream (), StandardCharsets.UTF_8))) {
try (OutputStream out = socket.getOutputStream ()) {
System.out.println (">> ОПЦИИ");
out.write ("ОПЦИИ * HTTP / 1.1 \ r \ n \ r \ n" .getBytes (StandardCharsets.UTF_8));
System.out.println ("<<" + input.readLine ());
}
} catch (исключение e) {
System.err.println ("Нет прочитанной строки:" + e);
}
}
}
SSLSocketFactory serverTrustingSSLFactory () создает исключение {
SSLContext ctx = SSLContext.getInstance ("TLS");
ctx.init (null, trustManager (), null);
return ctx.getSocketFactory ();
}
TrustManager [] trustManager () создает исключение {
TrustManagerFactory tmf = TrustManagerFactory.getInstance (TrustManagerFactory.getDefaultAlgorithm ());
tmf.init ((KeyStore) null);
// Должен использовать расширенный тип по сравнению с javax.net.ssl.X509TrustManager по умолчанию,
// в противном случае ошибка «Нет сопоставления альтернативного имени субъекта DNS» продолжает появляться.
X509ExtendedTrustManager defaultManager = null;
для (TrustManager trustManager: tmf.getTrustManagers ()) {
if (trustManager instanceof X509ExtendedTrustManager) {
defaultManager = (X509ExtendedTrustManager) trustManager;
перерыв;
}
}
if (defaultManager == null) {
генерировать новое RuntimeException («Не удается найти X509ExtendedTrustManager по умолчанию»);
}
вернуть новый TrustManager [] {new TrustAllServers (defaultManager)};
}
static void printChain (X509Certificate [] цепочка) {
пытаться {
for (int i = 0; i <chain.length; i ++) {
X509Сертификат сертификата = цепь [i];
System.out.println ("Cert [" + i + "]" + cert.getSubjectX500Principal () + ": alt:" + cert.getSubjectAlternativeNames ());
}
} catch (исключение e) {
e.printStackTrace ();
}
}
статический класс TrustAllServers extends X509ExtendedTrustManager {
окончательный X509ExtendedTrustManager defaultManager;public TrustAllServers (X509ExtendedTrustManager defaultManager) {this.defaultManager = defaultManager;} public void checkServerTrusted (цепочка X509Certificate [], String authType) выбрасывает CertificateException {try {defaultManager.checkServerTrusted (chain, authType);} catch (Exception e) {System.err.println ("Ненадежный сервер:" + e);} printChain (цепочка);} public void checkServerTrusted (цепочка X509Certificate [], String authType, сокет Socket) выдает CertificateException {try {defaultManager.checkServerTrusted (chain, authType, socket);} catch (Exception e) {System.err.println ("Ненадежный сервер для сокета:" + e);} printChain (цепочка);} public void checkServerTrusted (цепочка X509Certificate [], String authType, движок SSLEngine) выдает CertificateException {try {defaultManager.checkServerTrusted (chain, authType, engine);} catch (Exception e) {System.err.println ("Ненадежный сервер для движка:" + e);} printChain (цепочка);} public void checkClientTrusted (цепочка X509Certificate [], String authType) выбрасывает CertificateException {defaultManager.checkClientTrusted (chain, authType);} public void checkClientTrusted (цепочка X509Certificate [], String authType, сокет Socket) выдает CertificateException {defaultManager.checkClientTrusted (chain, authType, socket);} public void checkClientTrusted (цепочка X509Certificate [], String authType, движок SSLEngine) выдает CertificateException {defaultManager.checkClientTrusted (chain, authType, engine);} public X509Certificate [] getAcceptedIssuers () {return defaultManager.getAcceptedIssuers ();}}} </code>