Индикация имени сервера (SNI) на Java

Может ли кто-нибудь помочь мне приступить к выполнению HTTP-соединений с указанием имени сервера в Java?

Я пытаюсь запросить контент с сайта, который я администрирую. Я использую библиотеку Apache HttpClient, но мой запрос на безопасный контент терпит неудачу, потому что веб-сайт использует только SNI для HTTPS, а SNI не включен в DefaultHttpClient. Я искал инструкции о том, как подходить к этому в библиотеке Apache HttpClient, но я вижу, что в конечном итоге с этим документом: http://hc.apache.org/httpclient-3.x/sslguide.html, который устарел (ссылаясь на код обратно, когда HttpClient и HttpCore были частью пакета Apocal commons).

Итак... любая помощь?

Ответ 1

вы можете отслеживать https://issues.apache.org/jira/browse/HTTPCLIENT-1119

базовая клиентская реализация Java 7 способна поддерживать ее и предоставляет эту функцию через SSLSocketImpl # setHost (вызывается sun.net.www.protocol.https.HttpsClient

на Java 7 использовать

    new URL("https://cmbntr.sni.velox.ch/").openStream()

пока не будет зафиксирован HTTPCLIENT-1119

Ответ 2

Вот как я это сделал в org.apache.httpcomponents httpclient v4. 3+

private HttpClientConnectionManager createConnectionManager(final SSLContext ctx) {
    LOG.info("Creating sslConnectionSocketFactory");
    final SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(ctx) {

        @Override
        protected void prepareSocket(SSLSocket socket) throws IOException {
            try {
                System.out.println("************ setting socket HOST property *************");
                PropertyUtils.setProperty(socket, HOST, Constants.SNI_HOST);
            } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
                LOG.error(ex.getMessage());
            }
            super.prepareSocket(socket); 
        }

    };

    LOG.info("Creating connectionRegistry");
    final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("https", sslSF)
            .build();

    LOG.info("Creating poolingConnectionManager");
    final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
    connectionManager.setDefaultMaxPerRoute(MAX_CONNECTIONS_PER_ROUTE);
    connectionManager.setMaxTotal(MAX_CONNECTIONS);

    return connectionManager;
}

И вот как я создал HttpClient

final KeyManager[] keyManagers = createKeyManagers();
final TrustManager[] trustManagers = createTrustManagers();
final SSLContext ctx = createSslContext(keyManagers, trustManagers);

final HttpClientConnectionManager connectionManager = createConnectionManager(ctx);

LOG.info("Creating httpClient");
HttpClient httpClient = HttpClients
        .custom()
        .setConnectionManager(connectionManager)
        .build();

Ответ 3

с небольшим исправлением, как описано в разделе: TLS с SNI в клиентах Java. Возможно добавить поддержку SNI-сервера в JDK 7 и использовать его вместе с X509ExtendedKeyManager.

Ответ 4

Для меня работала правильная настройка ServerName в конфигурации Apache:

/etc/apache2/sites-avaible/default

<VirtualHost *:443>
  ServerName foo.domain.com
  ...
</VirtualHost>

Как сказано в fooobar.com/questions/31434/....

Ответ 5

Похоже, эта проблема исправлена в Java 7.