我尝试获取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)
HttpURLConnection
和HttpsURLConnection
都失败了。我尝试了org.apache.http.impl.client.CloseableHttpClient
,但得到了相同的异常。在浏览器中它工作正常。
答案 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。
它说问题是:
要解决此问题,您可以放宽认证或将客户端配置为具有与证书匹配的协议和密码。例如,他们可以是:
协议: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.1
或TLS 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 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
或直接在浏览器中显示)一个例子(使用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握手异常:无法找到请求目标的有效证书路径”问题,您需要
我尝试了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”)文件夹中。
最后运行你的应用,你会得到内容!