Подтвердить что ты не робот

Java SSLException: имя хоста в сертификате не совпало

Я использовал следующий код для подключения к одной из сервисов google. Этот код отлично работал на моей локальной машине:

HttpClient client=new DefaultHttpClient();
HttpPost post = new HttpPost("https://www.google.com/accounts/ClientLogin");
post.setEntity(new UrlEncodedFormEntity(myData));
HttpResponse response = client.execute(post);

Я помещаю этот код в производственную среду, которая заблокировала Google.com. По запросу они разрешили общение с сервером Google, предоставив мне доступ к IP: 74.125.236.52 - который является одним из IP-адресов Google. Я также отредактировал файл моих хостов, чтобы добавить эту запись.

Тем не менее, я не смог получить доступ к URL-адресу, и мне интересно, почему. Поэтому я заменил приведенный выше код:

HttpPost post = new HttpPost("https://74.125.236.52/accounts/ClientLogin");

Теперь я получаю сообщение об ошибке:

javax.net.ssl.SSLException: имя хоста в сертификате не соответствует: < 74.125.236.52 > != < www.google.com >

Я предполагаю, что это связано с тем, что Google имеет несколько IP-адресов. Я не могу попросить администратора сети разрешить мне доступ ко всем этим IP-адресам - я даже не могу получить весь этот список.

Что мне теперь делать? Есть ли обходной путь на уровне Java? Или это полностью в руках сетевого парня?

4b9b3361

Ответ 1

Спасибо Vineet Reynolds. Ссылка, которую вы предоставили, содержала множество комментариев пользователей - одна из которых я старался в отчаянии, и это помогло. Я добавил этот метод:

// Do not do this in production!!!
HttpsURLConnection.setDefaultHostnameVerifier( new HostnameVerifier(){
    public boolean verify(String string,SSLSession ssls) {
        return true;
    }
});

Теперь это кажется мне прекрасным, хотя я знаю, что это решение является временным. Я работаю с сетевыми пользователями, чтобы определить, почему мой файл hosts игнорируется.

Ответ 2

Вы также можете попробовать установить HostnameVerifier, как описано здесь. Это помогло мне избежать этой ошибки.

// Do not do this in production!!!
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;

DefaultHttpClient client = new DefaultHttpClient();

SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());

// Set verifier     
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

// Example send http request
final String url = "https://encrypted.google.com/";  
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost);

Ответ 3

Процесс проверки сертификата всегда будет проверять DNS-имя сертификата, представленного сервером, с именем хоста сервера в URL-адресе, используемом клиентом.

Следующий код

HttpPost post = new HttpPost("https://74.125.236.52/accounts/ClientLogin");

приведет к процессу проверки сертификата, проверяя, совпадает ли общее имя сертификата, выданного сервером, т.е. www.google.com, имени хоста i.e. 74.125.236.52. Очевидно, что это обязательно приведет к сбою (вы могли бы проверить это, просмотрев URL-адрес https://74.125.236.52/accounts/ClientLogin с помощью браузера и сами увидев результирующую ошибку).

Предположительно, ради безопасности вы не решаетесь написать свой собственный TrustManager (и, если вы не понимаете, как писать безопасный), вы должны посмотреть на создание записей DNS в своем центре данных убедитесь, что все поисковые запросы www.google.com будут разрешены на 74.125.236.52; это должно быть сделано либо на локальных DNS-серверах, либо в файле hosts вашей ОС; вам может потребоваться добавить записи и в другие домены. Излишне говорить, что вам нужно будет убедиться, что это согласуется с записями, возвращаемыми вашим интернет-провайдером.

Ответ 4

У меня была аналогичная проблема. Я использовал Android DefaultHttpClient. Я прочитал, что HttpsURLConnection может обрабатывать такое исключение. Поэтому я создал собственный HostnameVerifier, который использует верификатор из HttpsURLConnection. Я также завернул реализацию в пользовательский HttpClient.

public class CustomHttpClient extends DefaultHttpClient {

public CustomHttpClient() {
    super();
    SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
    socketFactory.setHostnameVerifier(new CustomHostnameVerifier());
    Scheme scheme = (new Scheme("https", socketFactory, 443));
    getConnectionManager().getSchemeRegistry().register(scheme);
}

Вот класс CustomHostnameVerifier:

public class CustomHostnameVerifier implements org.apache.http.conn.ssl.X509HostnameVerifier {

@Override
public boolean verify(String host, SSLSession session) {
    HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
    return hv.verify(host, session);
}

@Override
public void verify(String host, SSLSocket ssl) throws IOException {
}

@Override
public void verify(String host, X509Certificate cert) throws SSLException {

}

@Override
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {

}

}

Ответ 5

Более чистый подход (только для тестовой среды) в httpcliet4.3.3 выглядит следующим образом.

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();

Ответ 6

В httpclient-4.3.3.jar используется другой HttpClient:

public static void main (String[] args) throws Exception {
    // org.apache.http.client.HttpClient client = new DefaultHttpClient();
    org.apache.http.client.HttpClient client = HttpClientBuilder.create().build();
    System.out.println("HttpClient = " + client.getClass().toString());
    org.apache.http.client.methods.HttpPost post = new HttpPost("https://www.rideforrainbows.org/");
    org.apache.http.HttpResponse response = client.execute(post);
    java.io.InputStream is = response.getEntity().getContent();
    java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(is));
    String line;
    while ((line = rd.readLine()) != null) { 
        System.out.println(line);
    }
}

Этот HttpClientBuilder.create(). build() вернет org.apache.http.impl.client.InternalHttpClient. Он может обрабатывать этот имя хоста в сертификате не соответствует.

Ответ 7

Мы не должны использовать ALLOW_ALL_HOSTNAME_VERIFIER.

Как я могу реализовать собственный верификатор имени хоста?

class MyHostnameVerifier implements org.apache.http.conn.ssl.X509HostnameVerifier
{
    @Override
    public boolean verify(String host, SSLSession session) {
        String sslHost = session.getPeerHost();
        System.out.println("Host=" + host);
        System.out.println("SSL Host=" + sslHost);    
        if (host.equals(sslHost)) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void verify(String host, SSLSocket ssl) throws IOException {
        String sslHost = ssl.getInetAddress().getHostName();
        System.out.println("Host=" + host);
        System.out.println("SSL Host=" + sslHost);    
        if (host.equals(sslHost)) {
            return;
        } else {
            throw new IOException("hostname in certificate didn't match: " + host + " != " + sslHost);
        }
    }

    @Override
    public void verify(String host, X509Certificate cert) throws SSLException {
        throw new SSLException("Hostname verification 1 not implemented");
    }

    @Override
    public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
        throw new SSLException("Hostname verification 2 not implemented");
    }
}

Проведите тест против https://www.rideforrainbows.org/, который размещен на общем сервере.

public static void main (String[] args) throws Exception {
    //org.apache.http.conn.ssl.SSLSocketFactory sf = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
    //sf.setHostnameVerifier(new MyHostnameVerifier());
    //org.apache.http.conn.scheme.Scheme sch = new Scheme("https", 443, sf);

    org.apache.http.client.HttpClient client = new DefaultHttpClient();
    //client.getConnectionManager().getSchemeRegistry().register(sch);
    org.apache.http.client.methods.HttpPost post = new HttpPost("https://www.rideforrainbows.org/");
    org.apache.http.HttpResponse response = client.execute(post);
    java.io.InputStream is = response.getEntity().getContent();
    java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(is));
    String line;
    while ((line = rd.readLine()) != null) { 
        System.out.println(line);
    }
}

SSLException:

Исключение в потоке "main" javax.net.ssl.SSLException: имя хоста в сертификате не соответствует: www.rideforrainbows.org!= stac.rt.sg ИЛИ stac.rt.sg ИЛИ www.stac.rt. SG
  at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:231)
...

Сделайте с MyHostnameVerifier:

public static void main (String[] args) throws Exception {
    org.apache.http.conn.ssl.SSLSocketFactory sf = org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory();
    sf.setHostnameVerifier(new MyHostnameVerifier());
    org.apache.http.conn.scheme.Scheme sch = new Scheme("https", 443, sf);

    org.apache.http.client.HttpClient client = new DefaultHttpClient();
    client.getConnectionManager().getSchemeRegistry().register(sch);
    org.apache.http.client.methods.HttpPost post = new HttpPost("https://www.rideforrainbows.org/");
    org.apache.http.HttpResponse response = client.execute(post);
    java.io.InputStream is = response.getEntity().getContent();
    java.io.BufferedReader rd = new java.io.BufferedReader(new java.io.InputStreamReader(is));
    String line;
    while ((line = rd.readLine()) != null) { 
        System.out.println(line);
    }
}

Показывает:

Host = www.rideforrainbows.org
SSL Host = www.rideforrainbows.org

По крайней мере, у меня есть логика для сравнения (Host == SSL Host) и возвращает true.

Этот исходный код работает для httpclient-4.2.3.jar и httpclient-4.3.3.jar.