【问题标题】:JDK 11 HttpClient throws "No subject alternative DNS name" errorJDK 11 HttpClient 抛出“无主题替代 DNS 名称”错误
【发布时间】:2019-03-22 04:49:06
【问题描述】:

我正在写一个基于JDK11 HttpClient的简单REST api客户端,简单代码如下:

public class MyClass {

    private static final X509TrustManager TRUST_MANAGER = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] xcs, String string) {
        }

        public void checkServerTrusted(X509Certificate[] xcs, String string) {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

    private static HttpClient getNewHttpClient() {
        int timeout = 600;
        try {

            HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);

            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(null, new TrustManager[]{TRUST_MANAGER}, new SecureRandom());

            // Install the all-trusting host verifier
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

            //Set SSL parameters
            SSLParameters parameters = new SSLParameters();
            parameters.setEndpointIdentificationAlgorithm("HTTPS");
                HttpClient httpClient = HttpClient.newBuilder()
                    .connectTimeout(Duration.ofMillis(timeout * 1000))
                    .sslContext(sslContext)
                    .sslParameters(parameters)
                    .build();
            return httpClient;
        } catch (Exception e) {
            logger.warn("Unable to create HttpClient with disabled SSL Certificate verifying, default client will be used", e);
            return HttpClient.newHttpClient();
        }
    }

    public static void main(String[] args) {
        HttpRequest requestBuilder = HttpRequest.newBuilder()
                .uri(URI.create("https://somehostname.xx.xxx.net"))
                .GET()
                .build();

        getNewHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
    }
}

问题是当我尝试打开某个 SSL 域时出现错误:

原因:java.security.cert.CertificateException:找不到与 somehostname.xx.xxx.net 匹配的主题备用 DNS 名称。

我该如何解决这个问题?

【问题讨论】:

  • 是的。但这对我没有帮助。如您所见,我尝试通过添加 HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); 来禁用验证但它对JDK11的HttpClient没有影响。
  • @Andrey 您在使用 JDK
  • @Andrey,具体的用例是什么?为什么您要连接的服务器的主机名与其​​证书中的 CN 或 SAN 不匹配?如果这只是一个测试环境,则可以使用 -Djdk.internal.httpclient.disableHostnameVerification 禁用主机名验证,风险自负。
  • 在实例化httpclient之前以编程方式禁用主机名验证// PREVENTS HOST VALIDATIONfinal Properties props = System.getProperties();props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());

标签: java java-11 java-http-client


【解决方案1】:

虽然完全关闭主机名验证(即针对整个 VM)可能是一种选择,但它可能很危险。 如果您从 javax.net.ssl.X509ExtendedTrustManager 派生 TRUST_MANAGER,您也可以禁用主机名验证。

    private static final TrustManager DUMMY_TRUST_MANAGER = new X509ExtendedTrustManager() {        
    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return new java.security.cert.X509Certificate[0];
    }
    @Override
    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }
    @Override
    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { }
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { }
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { }
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { }
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { }
};

当然,这也会完全禁用对提供给您的客户的证书的任何检查。

https://bugs.openjdk.java.net/browse/JDK-8213309 上还有一个未解决的问题,用于向 JDK 添加 API 以专门关闭主机名验证。如果您也可以访问 openjdk 错误数据库,您可以为这个问题投票。

【讨论】:

  • 这对我有用。你能解释一下这个功能吗?顺便说一句,感谢您提供此解决方案!
  • OP 已经发布了他通过使用自定义 X509TrustManager 解决问题的尝试。但是,这不适用于 TLS 连接检查的“主机名验证”部分。但是,如果您从 X509ExtendedTrustManager(它是 X509TrustManager 的子类 ...)派生您的信任管理器,那么主机名验证也可以从“您的”代码完成,而不是 JDK 中的内置检查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-16
  • 2014-11-28
  • 1970-01-01
  • 2014-04-25
  • 2014-04-02
  • 2023-04-11
  • 1970-01-01
相关资源
最近更新 更多