https.protocols在Java中的使用
目录
异常现象:Caused by: java.io.EOFException
在服务器部署的微博爬虫系统中,偶尔会抛出以下异常:
Caused by: java.io.EOFException: SSL peer shut down incorrectly通过搜索引擎查询,在 Stackoverflow 上找到了相关的讨论。该回答指出,明确指定 HTTPS 的协议版本即可解决此问题。例如:
System.setProperty("https.protocols", "TLSv1.1");为了彻底理解其原理,以下将对 HTTPS 协议及其在 Java 中的实现进行详细梳理。
HTTPS 协议概述
根据维基百科关于 HTTPS 的介绍:
严格地讲,HTTPS 并不是一个单独的协议,而是对工作在一加密连接(TLS 或 SSL)上的常规 HTTP 协议的称呼。
TLS 与 SSL
- SSL (Secure Sockets Layer) 是 TLS (Transport Layer Security) 的前身。
- SSL 最初由 Netscape 公司推出,用于为 HTTPS 协议提供加密支持。
- 随后,IETF (Internet Engineering Task Force) 将 SSL 进行标准化,并公布了第一版 TLS 标准文件。
协议发展历史
SSL
- SSL 1.0:从未公开过。
- SSL 2.0:1995 年 2 月发布。
- SSL 3.0:1996 年发布。2014 年 10 月,Google 发现 SSL 3.0 存在设计缺陷(POODLE 攻击),建议禁用此一协议。
TLS
IETF 将 SSL 标准化后称为 TLS。TLS 1.0 与 SSL 3.0 的差异非常小。
- TLS 1.0
- TLS 1.1:2006 年发布
- TLS 1.2:2008 年发布
- TLS 1.3:2016 年发布
JDK 对 HTTPS 协议版本的支持
不同版本的 JDK 默认支持及启用的 HTTPS 协议版本有所不同,具体如下:
JDK 6
- SSL v3
- TLS v1 (默认)
- TLS v1.1 (JDK 6 Update 111 及以上版本支持)
JDK 7
- SSLv3
- TLS v1 (默认)
- TLS v1.1
- TLS v1.2
JDK 8
- SSL v3
- TLS v1
- TLS v1.1
- TLS v1.2 (默认)
JSSE (Java Secure Socket Extension)
JSSE 实现了 SSL 和 TLS 协议,为 Java 应用提供安全通信能力。
JSSE 参数调节
可以通过系统属性(System Properties)对 JSSE 行为进行调节:
javax.net.debug:打印连接的详细信息。- 例如:
-Djavax.net.debug=all或者-Djavax.net.debug=ssl:verbose
- 例如:
https.protocols:控制 Java 客户端通过HttpsURLConnection或URL.openStream()操作时使用的协议版本。- 例如:
-Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2 - 注意:对于非 HTTP 协议,可以通过
SocketFactory的SSLContext来控制。
- 例如:
jdk.tls.client.protocols:控制底层平台的 TLS 实现。- 例如:
-Djdk.tls.client.protocols=TLSv1.1,TLSv1.2
- 例如:
http.agent:当初始化连接时,Java 会使用这个字符串作为User-Agent。- 例如:
-Dhttp.agent="known agent"
- 例如:
java.net.useSystemProxies:使用系统本身的代理设置。- 例如:
-Djava.net.useSystemProxies=true
- 例如:
http.proxyHost和http.proxyPort:使用 HTTP 协议时的代理设置。- 例如:
-Dhttp.proxyHost=proxy.example.com -Dhttp.proxyPort=8080
- 例如:
https.proxyHost和https.proxyPort:与上述类似,区别在于针对 HTTPS 协议。http.proxyUser,http.proxyPassword,https.proxyUser,https.proxyPassword:代理认证的用户名和密码。
查看服务器支持的协议版本
可以使用 nmap 工具扫描服务器支持的 HTTPS 协议版本及加密套件。
nmap --script ssl-enum-ciphers -p 443 api.weibo.com返回的结果示例如下:
Starting Nmap 7.40 ( https://nmap.org ) at 2017-03-02 14:18 CST
Nmap scan report for api.weibo.com (180.149.135.176)
Host is up (0.039s latency).
Other addresses for api.weibo.com (not scanned): 180.149.135.230
PORT STATE SERVICE
443/tcp open https
| ssl-enum-ciphers:
| SSLv3:
| ciphers:
| TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_SEED_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_IDEA_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_RC4_128_SHA (rsa 2048) - C
| TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
| TLS_RSA_WITH_DES_CBC_SHA (rsa 2048) - C
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 1024) - A
| TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA (dh 1024) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 1024) - A
| TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA (dh 1024) - A
| TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (secp256r1) - C
| TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA (dh 1024) - D
| compressors:
| NULL
| cipher preference: server
| warnings:
| 64-bit block cipher 3DES vulnerable to SWEET32 attack
| 64-bit block cipher DES vulnerable to SWEET32 attack
| 64-bit block cipher IDEA vulnerable to SWEET32 attack
| Broken cipher RC4 is deprecated by RFC 7465
| CBC-mode cipher in SSLv3 (CVE-2014-3566)
| Key exchange (dh 1024) of lower strength than certificate key
| TLSv1.0:
| ciphers:
| TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_SEED_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_IDEA_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_RC4_128_SHA (rsa 2048) - C
| TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
| TLS_RSA_WITH_DES_CBC_SHA (rsa 2048) - C
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 1024) - A
| TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA (dh 1024) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 1024) - A
| TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA (dh 1024) - A
| TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (secp256r1) - C
| TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA (dh 1024) - D
| compressors:
| NULL
| cipher preference: server
| warnings:
| 64-bit block cipher 3DES vulnerable to SWEET32 attack
| 64-bit block cipher DES vulnerable to SWEET32 attack
| 64-bit block cipher IDEA vulnerable to SWEET32 attack
| Broken cipher RC4 is deprecated by RFC 7465
| Key exchange (dh 1024) of lower strength than certificate key
| TLSv1.1:
| ciphers:
| TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_SEED_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_IDEA_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_RC4_128_SHA (rsa 2048) - C
| TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
| TLS_RSA_WITH_DES_CBC_SHA (rsa 2048) - C
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 1024) - A
| TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA (dh 1024) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 1024) - A
| TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA (dh 1024) - A
| TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (secp256r1) - C
| TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA (dh 1024) - D
| compressors:
| NULL
| cipher preference: server
| warnings:
| 64-bit block cipher 3DES vulnerable to SWEET32 attack
| 64-bit block cipher DES vulnerable to SWEET32 attack
| 64-bit block cipher IDEA vulnerable to SWEET32 attack
| Broken cipher RC4 is deprecated by RFC 7465
| Key exchange (dh 1024) of lower strength than certificate key
| TLSv1.2:
| ciphers:
| TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
| TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A
| TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
| TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
| TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_SEED_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_IDEA_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_RC4_128_SHA (rsa 2048) - C
| TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
| TLS_RSA_WITH_DES_CBC_SHA (rsa 2048) - C
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
| TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (dh 1024) - A
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (dh 1024) - A
| TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 1024) - A
| TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA (dh 1024) - A
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
| TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (dh 1024) - A
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (dh 1024) - A
| TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 1024) - A
| TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA (dh 1024) - A
| TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (secp256r1) - C
| TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA (dh 1024) - D
| compressors:
| NULL
| cipher preference: server
| warnings:
| 64-bit block cipher 3DES vulnerable to SWEET32 attack
| 64-bit block cipher DES vulnerable to SWEET32 attack
| 64-bit block cipher IDEA vulnerable to SWEET32 attack
| Broken cipher RC4 is deprecated by RFC 7465
| Key exchange (dh 1024) of lower strength than certificate key
|_ least strength: D
Nmap done: 1 IP address (1 host up) scanned in 10.99 seconds根据扫描结果可知,该服务器支持的协议包括:
- SSLv3
- TLSv1.0
- TLSv1.1
- TLSv1.2
解决方案
针对上述异常,在服务器环境(Linux + JDK7 + 64 位)中,通过在代码初始化阶段添加以下设置:
System.setProperty("https.protocols", "TLSv1.2");强制指定使用 TLSv1.2 协议后,爬虫系统在爬取数据时不再抛出此类异常。
参考资料
说明:本文部分内容基于较早的 JDK 版本(如 JDK 6/7/8 早期版本)整理。随着安全标准的提升,现代 JDK 版本(如 JDK 8u291+、JDK 11+ 等)已默认禁用 TLS 1.0 和 TLS 1.1 协议。在实际生产环境中,建议优先使用 TLS 1.2 或 TLS 1.3,并及时更新 JDK 补丁以确保安全性。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/httpsprotocols-zai-java-zhong-de-shi-yong.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。