如何将HttpClient与任何ssl证书一起使用,无论它有多"糟糕"

Bra*_*rks 5 java apache-httpclient-4.x

我在一个只用于抓取公共数据的网络爬虫中使用Apache HttpClient.

我希望能够使用无效证书抓取网站,无论多么无效.

我的抓取工具不会传入任何用户名,密码等,也不会发送或接收敏感数据.

对于这个用例,我会抓取http一个站点的版本,如果它存在,但有时它当然不会.

如何使用Apache的HttpClient完成这项工作?

我尝试了一些像这样的建议,但是对于一些无效的证书,它们仍然失败,例如:

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
Run Code Online (Sandbox Code Playgroud)

请注意,我已经尝试了这个,我的$JAVA_HOME/jre/lib/security/java.security文件jdk.tls.disabledAlgorithms设置为空,以确保这不是一个问题,我仍然得到如上所述的失败.

use*_*985 6

对您的问题的简短回答,即特别信任所有证书,将使用 TrustAllStrategy并执行以下操作:

SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
sslContextBuilder.loadTrustMaterial(null, new TrustAllStrategy());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
        sslContextBuilder.build());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(
        socketFactory).build();
Run Code Online (Sandbox Code Playgroud)

但是……无效的证书可能不是您的主要问题。发生握手失败的原因有很多,但根据我的经验,这通常是由于 SSL/TLS 版本不匹配或密码套件协商失败造成的。这并不意味着 ssl 证书是“坏的”,它只是服务器和客户端之间的不匹配。您可以使用 Wireshark 之类的工具准确查看握手失败的位置(更多内容

虽然 Wireshark 可以很好地了解它在哪里失败,但它不会帮助您提出解决方案。每当我过去调试 handshake_failures 时,我发现这个工具特别有用:https : //testssl.sh/

您可以将该脚本指向任何失败的网站,以详细了解该目标上可用的协议以及您的客户端需要支持哪些协议才能建立成功的握手。它还将打印有关证书的信息。

例如(仅显示 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 
Run Code Online (Sandbox Code Playgroud)

因此,使用此输出我们可以看到,如果您的客户端仅支持 SSLv3,握手将失败,因为服务器不支持该协议。协议提供不太可能是问题,但您可以通过获取已启用协议的列表来仔细检查您的 java 客户端支持什么。您可以从上面的代码片段中提供 SSLConnectionSocketFactory 的覆盖实现,以获取启用/支持的协议和密码套件的列表,如下所示 ( 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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

当密码套件协商失败时,我经常遇到handshake_failure。为避免此错误,您的客户端支持的密码套件列表必须至少包含一个与服务器支持的密码套件列表中的密码套件的匹配项。

如果服务器需要基于 AES256 的密码套件,您可能需要 Java 加密扩展 (JCE)。这些图书馆受国家限制,因此美国以外的人可能无法使用它们。

更多关于加密限制的信息,如果你有兴趣:https : //crypto.stackexchange.com/questions/20524/why-there-are-limitations-on-using-encryption-with-keys-beyond-certain-length