java - 允许 Java JDK 11 HttpClient 的不安全 HTTPS 连接

标签 java java-11 java-http-client

有时需要允许不安全的 HTTPS 连接,例如在一些应该适用于任何站点的网络爬虫应用程序中。我用了one such solution使用最近被 new HttpClient API 取代的旧 HttpsURLConnection API在 JDK 11 中。使用这个新 API 允许不安全的 HTTPS 连接(自签名或过期证书)的方法是什么?

UPD:我试过的代码(在 Kotlin 中但直接映射到 Java):

    val trustAllCerts = arrayOf<TrustManager>(object: X509TrustManager {
        override fun getAcceptedIssuers(): Array<X509Certificate>? = null
        override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {}
        override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {}
    })

    val sslContext = SSLContext.getInstance("SSL")
    sslContext.init(null, trustAllCerts, SecureRandom())

    val sslParams = SSLParameters()
    // This should prevent host validation
    sslParams.endpointIdentificationAlgorithm = ""

    httpClient = HttpClient.newBuilder()
        .sslContext(sslContext)
        .sslParameters(sslParams)
        .build()

但是在发送时我有异常(尝试使用自签名证书的本地主机):

java.io.IOException: No name matching localhost found

使用 IP 地址而不是 localhost 会产生“不存在主题替代名称”异常。

在对 JDK 进行一些调试之后,我发现在抛出异常的地方实际上忽略了 sslParams 并且使用了一些本地创建的实例。进一步调试显示,影响主机名验证算法的唯一方法是将 jdk.internal.httpclient.disableHostnameVerification 系统属性设置为 true。这似乎是一个解决方案。上面代码中的 SSLParameters 没有任何作用,所以这部分可以舍弃。使其仅在全局范围内可配置看起来像是新 HttpClient API 中的严重设计缺陷。

最佳答案

正如已经建议的那样,您需要一个忽略错误证书的 SSLContext。在问题的一个链接中获取 SSLContext 的确切代码应该通过基本上创建一个不查看证书的空 TrustManager 来工作:

private static 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) {
        }
    }
};

public static  void main (String[] args) throws Exception {
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustAllCerts, new SecureRandom());

    HttpClient client = HttpClient.newBuilder()
        .sslContext(sslContext)
        .build();

上面的问题显然是所有站点都完全禁用了服务器身份验证。如果只有一个坏证书,那么您可以将其导入 keystore :

keytool -importcert -keystore keystorename -storepass pass -alias cert -file certfile

然后使用读取 keystore 的 InputStream 初始化 SSLContext,如下所示:

char[] passphrase = ..
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(i, passphrase); // i is an InputStream reading the keystore

KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ks, passphrase);

TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(ks);

sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

上述任一解决方案都适用于自签名证书。第三种选择是在服务器提供有效的非自签名证书但主机不匹配它提供的证书中的任何名称的情况下,然后系统属性“jdk.internal.httpclient.disableHostnameVerification”可以设置为“true”,这将强制以与之前使用 HostnameVerifier API 相同的方式接受证书。请注意,在正常部署中,预计不会使用任何这些机制,因为应该可以自动验证任何正确配置的 HTTPS 服务器提供的证书。

关于java - 允许 Java JDK 11 HttpClient 的不安全 HTTPS 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52988677/

相关文章:

JavaFX - 获取非 ASCII 字符的 KeyEvent

java - 当只有类型很重要时,在 JPA 中保留成员变量

java - JDK-11 SSLHandshakeException : No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

java - 如何使用 Java 11 运行 Wildfly 14?

java - 使用 Java 11 编译时,Intellij 找不到 java.net.http

java - 登录 JDK11 HttpClient

java - org.apache.maven.plugins.enforcer.BanDuplicateClasses 失败,消息 : Duplicate classes found

java - 有没有更好的方法来为整个布局设置 onClickListener?

java - 如何响应式下载和处理大数据?

Java 9 HttpClient java.lang.NoClassDefFoundError : jdk/incubator/http/HttpClient