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

Как обрабатывать https-url, который заканчивает соединение с открытым текстом

Я пытаюсь получить содержимое страницы https-url, который генерирует исключение при получении входного потока.

String httpsURL = "https://careers.virtusa.com/";
URL myurl = new URL(httpsURL);
HttpsURLConnection con = (HttpsURLConnection)myurl.openConnection();
InputStream ins = con.getInputStream();

Исключение составляет, как показано ниже,

Exception in thread "main" javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
    at com.sun.net.ssl.internal.ssl.InputRecord.handleUnknownRecord(InputRecord.java:523)
    at com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:355)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:798)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)    
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)    
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)    
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)    
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1172)    
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)      
    at url.JavaHttpsExample.main(JavaHttpsExample.java:18)    

Оба HttpURLConnection и HttpsURLConnection терпят неудачу. Я пробовал org.apache.http.impl.client.CloseableHttpClient, но получал то же исключение. В браузере он отлично работает.

4b9b3361

Ответ 1

Я не получаю ошибку plaintext connection, которую вы получаете. Я также подтвердил, что сайт, который вы пытаетесь расчесывать, фактически настроен для HTTPS. Это исключение, которое я получаю:

java.security.cert.CertificateException: не найдено альтернативного имени DNS-имени, найденного sub.website.com.

Одним из решений этой проблемы является установка верификатора хоста, который доверяет всем хостам (включая sun.website.com). Попробуйте использовать следующий фрагмент кода, который выводит вашу целевую страницу на консоль Java:

HostnameVerifier allHostsValid = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};

HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

String httpsURL = "https://sub.website.com/";
URL myurl = new URL(httpsURL);
HttpsURLConnection con = (HttpsURLConnection)myurl.openConnection();

BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
String input;
while ((input = br.readLine()) != null) {
    System.out.println(input);
}
br.close();

Примечание:

Я дал этот ответ, когда OP использовал другой сайт. Ответ действителен для условий, указанных, хотя OP может измениться с тех пор.

Ответ 2

Вы можете найти всю информацию о сертификате страницы, которую хотите загрузить на этой странице: сертификат careers.virtusa.com.

В нем говорится, что проблема заключается в следующем:

  • Java 6u45: несоответствие протокола или шифрования
  • Java 7u25: несоответствие набора протоколов или шифров

Чтобы решить проблему, вы можете ослабить сертификацию или настроить клиента на наличие протокола и шифрования, соответствующих сертификату. Например, они могут быть:

Протокол: TLS 1.2
Шифр: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA

Как решить и получить контент

Я пишу код с помощью apache httpcomponents, он в основном доверяет любому источнику и не заботится о сертификации.

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.2</version>
</dependency>

Он возвращает содержимое страницы.

import java.io.*;
import org.apache.http.*;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.*;
import org.apache.http.impl.client.*;
import org.apache.http.ssl.SSLContextBuilder;

public class Main {
  public static void main(String[] args) throws Exception {
    String httpsURL = "https://careers.virtusa.com";

    SSLContextBuilder builder = new SSLContextBuilder();
    builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
            builder.build());
    CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(
            sslsf).build();

    HttpGet httpget = new HttpGet(httpsURL);
    HttpResponse response = httpclient.execute(httpget);

    HttpEntity httpEntity = response.getEntity();
    InputStream inputStream = httpEntity.getContent();
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

    StringBuffer result = new StringBuffer();
    String line = "";
    while ((line = bufferedReader.readLine()) != null) {
      result.append(line);
    }
    System.out.println(result.toString());
  }
}

Он печатает:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transiti ...

Ответ 3

Укажите версию SSL, которую использует careers.virtusa.com. т.е. TLSv1.2 в коде.

SSLContext sc = SSLContext.getInstance( "TLSv1.2" );

public String getData(String URL)
{
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkClientTrusted(X509Certificate[] certs,
                String authType) {
        }

        public void checkServerTrusted(X509Certificate[] certs,
                String authType) {
        }

    } };

    String output = "";
    try{

        //System.setProperty("https.proxyHost", "<PROXY HOST IP>");   // Uncomment if using proxy
        //System.setProperty("https.proxyPort", "<PROXY HOST PORT>");        // Uncomment if using proxy
        SSLContext sc = SSLContext.getInstance("TLSv1.2");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        /*
         * end of the fix
         */

        URL url = new URL(URL);
        URLConnection con = url.openConnection();

        InputStream ins = con.getInputStream();
        InputStreamReader isr = new InputStreamReader(ins);
        BufferedReader in = new BufferedReader(isr);

        String inputLine;


        while ((inputLine = in.readLine()) != null) {
            output = output + inputLine;
        }

        System.out.println(output);
        in.close();

    }   
    catch(Exception e){
        e.printStackTrace();            
    }


    return output;
}

Ответ 4

Я думаю, что проблема заключается в перенаправлении (302). Последняя страница - http не https.

Установите

HttpsURLConnection.setFollowRedirects(false);

Вызвать первый URL-адрес, получить заголовок Местоположение, выполнить другой вызов в определенном месте. Получите cookie во втором вызове в заголовке Set-Cookie и снова в заголовке Местоположение. Наконец, откройте еще один URLConnection со вторым перенаправленным URL, сообщающим cookie (con.setRequestProperty( "Cookie", COOKIE)).
Это должно работать.

    HttpsURLConnection.setFollowRedirects(false);        
    String url = "https://<host>:443/OA_HTML/IrcVisitor";
    String cookie = "";
    HttpURLConnection con = (HttpURLConnection)new URL(url).openConnection();
    con.setRequestMethod("GET");
    con.connect();
    // aasuming that is always a redirect // if(con.getResponseCode() == 302) {
    url = con.getHeaderField("Location");
    con.disconnect();

    con = (HttpsURLConnection)new URL(url).openConnection();
    con.setRequestMethod("GET");
    con.connect();
    // aasuming that is always a redirect // if(con.getResponseCode() == 302) {
    url = con.getHeaderField("Location");
    cookie = con.getHeaderField("Set-Cookie");
    cookie = cookie.substring(0, cookie.indexOf(';'));

    con = (HttpsURLConnection)new URL(url).openConnection();
    con.setRequestMethod("GET");
    con.setRequestProperty("Cookie", cookie);
    con.setDoInput(true);
    con.connect();
    if(con.getResponseCode() == 200) {
        //readStream(con.getInputStream());
        //if you need to download something with a relative path from this page use "Content-Location" as base path
        String basePath = con.getHeaderField("Content-Location");
    }

Ответ 5

Все запросы https://careers.virtusa.com перенаправляются на веб-шлюз McAfee (прокси)

Request URL:https://careers.virtusa.com/
Request Method:GET
Status Code:302 Found
Remote Address:203.62.173.60:443

Если вы просто запросите этот адрес http://203.62.173.60:443, вы получите сообщение об ошибке рукопожатия

Handshake failed
The SSL handshake could not be performed.
Host: 10.4.190.60
Reason: :state 21:Application response 500 handshakefailed

потому что шлюз ожидает безопасного HTTP-запроса от доверенного клиента с сертификатом careers.virtusa.com.

Проблема не появляется в веб-браузере, потому что я полагаю, что передний веб-сервер virtusa internaly перенаправляется на веб-шлюз с использованием доверенного сертификата, поэтому он, наконец, возвращает веб-страницу без проблем.

С другой стороны, большинство современных веб-браузеров по умолчанию используют TLS 1.1 или TLS 1.2 для выполнения защищенных запросов, но Java dont, зависит от версии Java.

Если вы проанализируете careers.virtusa.com, вы увидите, что поддерживает только 1.1 и 1.2

TLS 1.2 Yes
TLS 1.1 Yes
TLS 1.0 No
SSL 3   No
SSL 2   No

JDK 5 и 6 поддерживает SSLv3 и TLSv1, поэтому, если вы используете эту версию, вы получите исключение SSL.

JDK 7, 8 и 9 поддерживает SSLv3, TLSv1, TLSv1.1 и TLSv1.2, но вам нужно явно указать поддерживаемые протоколы для вашего соединения, в этом случае

new String[] { "TLSv1.1", "TLSv1.2" }

Итак, вам нужно:

  • Сертификат с открытым ключом careers.virtusa.com (используя openssl s_client или непосредственно из браузера)
  • Импортировать сертификат в хранилище ключей, чтобы использовать его как доверенное хранилище с вашим HTTP-клиентом.
  • версия Java > 6.
  • Установите поддерживаемые протоколы для подключения.

Пример (с Apache HttpComponents 4.4.1)

import java.io.File;

import javax.net.ssl.SSLContext;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

public class SSLTest {

    public final static void main(String[] args) throws Exception {

        SSLContext sslcontext = SSLContexts.custom()
                .loadTrustMaterial(new File("/tmp/careers.virtusa.com.jks"), "changeit".toCharArray(), new TrustSelfSignedStrategy()).build();

        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1.1", "TLSv1.2" }, null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());

        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {

            HttpGet httpget = new HttpGet("https://careers.virtusa.com/");

            CloseableHttpResponse response = httpClient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    System.out.println("Response content length: " + entity.getContentLength());
                    System.out.println(IOUtils.toString(entity.getContent()));
                }

                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpClient.close();
        }
    }
}

Затем вы можете получить содержимое веб-страницы, используя оба URL-адреса, http://careers.virtusa.com/ или https://careers.virtusa.com/

Надеюсь, что это поможет.

Ответ 6

Если вы доверяете конечной точке, вы можете полностью отключить проверку сертификата, используя опцию 2, перечисленную здесь:

сообщить java, чтобы принять самоподписанный сертификат ssl

Просто добавьте это перед кодом:

    TrustManager[] trustAllCerts = new TrustManager[] {
        new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[0];
            }
            public void checkClientTrusted(
                java.security.cert.X509Certificate[] certs, String authType) {
                }
            public void checkServerTrusted(
                java.security.cert.X509Certificate[] certs, String authType) {
            }
        }
    };

    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

Ответ 7

В соответствии с вопросом "Исключение SSL-квитирования: невозможно найти правильный путь сертификации к запрашиваемому целевому объекту" в security packexchange, вам нужно либо

  • добавить отсутствующий сертификат как доверенный Java
  • или добавить отсутствующий сертификат в цепочку веб-сервера.

Я попробовал первый вариант из учебник Mkyong, и он сработал.

Примечание. InstallCert переместился в github.

Возможно, вам понадобится создать временный проект, запускающий этот java файл с вашим целевым сайтом (в вашем случае "careers.virtusa.com" ) в качестве аргументов, см. скриншот. Взаимодействуйте с консолью, он создаст файл с именем "jssecacerts". Просто скопируйте и вставьте этот файл в свою "$ JAVA_HOME\jre\lib\security" (в моем случае, папку "C:\Program Files\Java\jdk1.8.0_60\jre\lib\security" ).

Наконец, запустите приложение, вы получите контент!