我正在使用 Apache HttpClient在仅用于抓取公共(public)数据的网络爬虫中。
我希望它能够抓取证书无效的网站,无论证书多么无效。
我的爬虫不会传递任何用户名、密码等,也不会发送或接收任何敏感数据。
对于这个用例,我会抓取网站的 http
版本(如果它存在),但有时它当然不存在。
如何使用 Apache 的 HttpClient 完成此操作?
我尝试了一些建议,比如 this one , 但它们仍然因某些无效证书而失败,例如:
failed for url:https://dh480.badssl.com/, reason:java.lang.RuntimeException: Could not generate DH keypair
failed for url:https://null.badssl.com/, reason:Received fatal alert: handshake_failure
failed for url:https://rc4-md5.badssl.com/, reason:Received fatal alert: handshake_failure
failed for url:https://rc4.badssl.com/, reason:Received fatal alert: handshake_failure
failed for url:https://superfish.badssl.com/, reason:Connection reset
请注意,我已经尝试将我的 $JAVA_HOME/jre/lib/security/java.security
文件的 jdk.tls.disabledAlgorithms
设置为空,以确保这不是问题,我仍然会遇到类似上述的失败。
最佳答案
您的问题的简短答案是专门信任所有证书,将使用 TrustAllStrategy并做这样的事情:
SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
sslContextBuilder.loadTrustMaterial(null, new TrustAllStrategy());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
sslContextBuilder.build());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(
socketFactory).build();
但是...无效的证书可能不是您的主要问题。 handshake_failure 的发生可能有多种原因,但根据我的经验,这通常是由于 SSL/TLS 版本不匹配或密码套件协商失败。这并不意味着 ssl 证书是“坏的”,它只是服务器和客户端之间的不匹配。您可以使用 Wireshark (more on that) 等工具准确查看握手失败的位置
虽然 Wireshark 可以很好地发现它的失败之处,但它不会帮助您提出解决方案。每当我过去调试 handshake_failures 时,我发现这个工具特别有用:https://testssl.sh/
您可以将该脚本指向任何失败的网站,以详细了解该目标上可用的协议(protocol)以及您的客户端需要支持哪些协议(protocol)才能建立成功的握手。它还将打印有关证书的信息。
例如(仅显示 testssl.sh 输出的两部分):
./testssl.sh www.google.com
....
Testing protocols (via sockets except TLS 1.2, SPDY+HTTP2)
SSLv2 not offered (OK)
SSLv3 not offered (OK)
TLS 1 offered
TLS 1.1 offered
TLS 1.2 offered (OK)
....
Server Certificate #1
Signature Algorithm SHA256 with RSA
Server key size RSA 2048 bits
Common Name (CN) "www.google.com"
subjectAltName (SAN) "www.google.com"
Issuer "Google Internet Authority G3" ("Google Trust Services" from "US")
Trust (hostname) Ok via SAN and CN (works w/o SNI)
Chain of trust "/etc/*.pem" cannot be found / not readable
Certificate Expiration expires < 60 days (58) (2018-10-30 06:14 --> 2019-01-22 06:14 -0700)
....
Testing all 102 locally available ciphers against the server, ordered by encryption strength
(Your /usr/bin/openssl cannot show DH/ECDH bits)
Hexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits
------------------------------------------------------------------------
xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH AESGCM 256
xc02c ECDHE-ECDSA-AES256-GCM-SHA384 ECDH AESGCM 256
xc014 ECDHE-RSA-AES256-SHA ECDH AES 256
xc00a ECDHE-ECDSA-AES256-SHA ECDH AES 256
x9d AES256-GCM-SHA384 RSA AESGCM 256
x35 AES256-SHA RSA AES 256
xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH AESGCM 128
xc02b ECDHE-ECDSA-AES128-GCM-SHA256 ECDH AESGCM 128
xc013 ECDHE-RSA-AES128-SHA ECDH AES 128
xc009 ECDHE-ECDSA-AES128-SHA ECDH AES 128
x9c AES128-GCM-SHA256 RSA AESGCM 128
x2f AES128-SHA RSA AES 128
x0a DES-CBC3-SHA RSA 3DES 168
因此,使用此输出我们可以看到,如果您的客户端仅支持 SSLv3,则握手将失败,因为服务器不支持该协议(protocol)。提供的协议(protocol)不太可能是问题,但您可以通过获取已启用协议(protocol)的列表来仔细检查您的 Java 客户端支持的内容。您可以从上面的代码片段中提供 SSLConnectionSocketFactory 的覆盖实现,以获取启用/支持的协议(protocol)和密码套件的列表,如下所示 (SSLSocket):
class MySSLConnectionSocketFactory extends SSLConnectionSocketFactory {
@Override
protected void prepareSocket(SSLSocket socket) throws IOException {
System.out.println("Supported Ciphers" + Arrays.toString(socket.getSupportedCipherSuites()));
System.out.println("Supported Protocols" + Arrays.toString(socket.getSupportedProtocols()));
System.out.println("Enabled Ciphers" + Arrays.toString(socket.getEnabledCipherSuites()));
System.out.println("Enabled Protocols" + Arrays.toString(socket.getEnabledProtocols()));
}
}
我经常遇到密码套件协商失败时的handshake_failure。为避免此错误,您的客户端支持的密码套件列表必须至少包含一个与服务器支持的密码套件列表中的密码套件匹配的项。
如果服务器需要基于 AES256 的密码套件,您可能需要 Java 加密扩展 (JCE)。这些图书馆受国家限制,因此美国以外的人可能无法使用。
更多关于加密限制的信息,如果您有兴趣:https://crypto.stackexchange.com/questions/20524/why-there-are-limitations-on-using-encryption-with-keys-beyond-certain-length
关于java - 如何将 HttpClient 与任何 ssl 证书一起使用,无论 "bad"是多少,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53321255/