获取javax.net.ssl.SSLException:收到致命警报:使用Jsoup抓取数据时的protocol_version

我试图使用Jsoup从站点获取数据。 链接到该网站是点击这里 !

这是我获取数据的代码。 `

// WARNING: do it only if security isn't important, otherwise you have // to follow this advices: http://stackoverflow.com/a/7745706/1363265 // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager(){ public X509Certificate[] getAcceptedIssuers(){return null;} public void checkClientTrusted(X509Certificate[] certs, String authType){} public void checkServerTrusted(X509Certificate[] certs, String authType){} }}; // Install the all-trusting trust manager try { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } catch (Exception e) { ; }` 

String url = "https://www.sos.nh.gov/corporate/soskb/SearchResults.asp?FormName=CorpNameSearch&Words=Starting&SearchStr="+query+"&SearchType=Search"; Connection.Response response = Jsoup.connect(url).timeout(30000) .method(Connection.Method.GET) .userAgent("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0")
.execute(); Document document = response.parse();

请告诉我这里的错误是什么。

您希望在此处使用Java 8,因为它默认支持TLSv1.2以及其他必需的密码套件。

为什么不Java 7?

我在Java 7(1.7.0_45)的盒子上测试并得到了同样的错误。

我激活了调试消息并强制使用TLSv1.2。

 System.setProperty("javax.net.debug", "all"); System.setProperty("https.protocols", "TLSv1.2"); 

然后我点击了这个新错误:

 javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure 

最后,我去了Comodoca的SSL分析仪 ,看到了一些有趣的东西。 根据SSL分析器,您定位的网站仅启用了以下密码套件:

启用密码套件
名称(ID)密钥大小(以位为单位)
 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F)128 ECDH 256位(P-256) 
 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030)256 ECDH 256位(P-256) 
 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(0x9E)128 DH 2048位  
 TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(0x9F)256 DH 2048位

(见全部细节 )

就我而言,我没有上述套房。 检查你是否有它们:

 SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, null, new java.security.SecureRandom()); String[] scs = sc.getSocketFactory().getSupportedCipherSuites(); Arrays.sort(scs); for(String s : scs) { System.out.println(s); } 

请参阅SSLSocketFactoryEx以启用所需的密码套件。

为何选择Java 8?

另一方面,我通过从Java 7迁移到Java 8(1.8.0_20),默认支持TLS v1.2并提供所需的密码套件,成功运行代码。

以下是Windows 7上Java 8(1.8.0_20)支持的密码套件(总共71套件)的修整列表。

 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ... TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 

片段

 try { // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { } } }; // Install the all-trusting trust manager SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // Fetch url String url = "https://www.sos.nh.gov/corporate/soskb/SearchResults.asp?FormName=CorpNameSearch&Words=All&SearchStr=facebook&SearchType=Search"; Connection.Response response = Jsoup // .connect(url) // .timeout(60000) // .method(Connection.Method.GET) // .userAgent("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0") // .execute(); Document document = response.parse(); System.out.println(document); } catch (Exception e) { e.printStackTrace(); } 

最后的想法:

在安全方面, 始终使用最新的更新版本。

(来自关闭的评论,为未来的发现者扩大了一点)

通过实验,该站点需要协议版本TLSv1.2,尽管Java7 JSSE实现了这一点,但客户端默认禁用 1.2和1.1。 Java8默认启用它们; 或者在Java7中,因为Jsoup使用HttpsURLConnection您可以使用系统属性https.protocols来更改已启用的版本。 您需要至少包含TLSv1.2并且为了获得最大的灵活性,应使用所有当前可接受的协议https.protocols=TLSv1,TLSv1.1,TLSv1.2

此外,使用所有信任的TrustManager意味着几乎所有访问您网络的坏人都可以伪造此站点并公开您发送它的任何敏感数据。 最好设置本地信任库,使其接受您需要的证书和服务器,但不接受虚假的服务器。