如何处理最终明文连接的https url

时间:2016-04-27 14:02:21

标签: java https

我尝试获取https网址的页面内容,该网址内容会在获取输入流时抛出异常。

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)    

HttpURLConnectionHttpsURLConnection都失败了。我尝试了org.apache.http.impl.client.CloseableHttpClient,但得到了相同的异常。在浏览器中它工作正常。

7 个答案:

答案 0 :(得分:3)

我没有收到plaintext connection错误。我还验证了您尝试梳理的网站实际上已配置为HTTPS。这是我得到的例外:

  

java.security.cert.CertificateException:找不到匹配sub.website.com的主题备用DNS名称。

此问题的一个解决方案是安装一个信任所有主机(包括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可能已经改变了。

答案 1 :(得分:3)

您可以在此页面中找到有关您要下载的网页证书的所有信息:certificate of 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 ...

答案 2 :(得分:3)

指定careers.virtusa.com正在使用的SSL版本。即代码中的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;
}

答案 3 :(得分:3)

我认为问题是重定向(302)。最后一页是http而不是https。

设置

HttpsURLConnection.setFollowRedirects(false);

调用第一个网址,获取位置标题,再次拨打指定位置。在 Set-Cookie 标题的第二个调用中获取Cookie,再次在位置标头中获取Cookie。最后打开另一个URLConnection,第二个重定向的URL通知cookie( con.setRequestProperty(&#34; Cookie&#34;,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");
    }

答案 4 :(得分:3)

https://careers.virtusa.com的所有请求都会重定向到McAfee Web Gateway(代理)

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证书。

问题没有出现在Web浏览器中,因为我认为virtusa的前端Web服务器使用可信证书重定向到Web Gateway,因此最终会毫无问题地返回网页。

另一方面,大多数现代Web浏览器默认使用TLS 1.1TLS 1.2来执行安全请求,但Java不依赖于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 56 支持SSLv3TLSv1,因此如果您使用该版本,则会收到SSL异常。

JDK 7, 89 支持SSLv3, TLSv1, TLSv1.1TLSv1.2,但您需要明确指出支持的连接协议,在本例中

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

所以,你需要:

  • 公钥为careers.virtusa.com的证书(使用openssl s_client或直接在浏览器中显示)
  • 将证书导入密钥库,以将其用作HTTP客户端连接的信任库。
  • Java版本&gt; 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();
        }
    }
}

然后,您可以使用两个网址http://careers.virtusa.com/https://careers.virtusa.com/

获取网页内容

希望这有帮助。

答案 5 :(得分:2)

如果您信任该终点,则可以使用此处列出的选项2完全禁用证书验证:

telling java to accept self-signed ssl certificate

只需在代码前添加:

    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());

答案 6 :(得分:0)

根据安全堆栈交换中的“SSL握手异常:无法找到请求目标的有效证书路径”问题,您需要

  1. 将缺少的证书添加为受信任的Java
  2. 或将缺少的证书添加到Web服务器的链中。
  3. 我尝试了Mkyong's tutorial的第一个选项,但它确实有效。

    注意:InstallCert已移至github。

    您可能需要创建一个运行此java文件的临时项目,并将您的目标站点(在您的情况下,“careers.virtusa.com”)作为参数,请参阅screenshot。与控制台交互,它将生成一个名为“jssecacerts”的文件。只需将此文件复制并粘贴到“$ JAVA_HOME \ jre \ lib \ security”(在我的情况下,“C:\ Program Files \ Java \ jdk1.8.0_60 \ jre \ lib \ security”)文件夹中。

    最后运行你的应用,你会得到内容!