首页 > 解决方案 > HttpsURLConnections 默认主机名验证程序

问题描述

我正在使用一个HttpURLConnection来创建一个 POST 请求(用于在某个 OAuth2 令牌端点获取令牌)。令牌端点使用 HTTPS。我想知道关于 HTTPS 的主机名验证是如何工作的。的默认主机名验证器HttpsURLConnection似乎如下 [1]:

     /**
     * HostnameVerifier provides a callback mechanism so that
     * implementers of this interface can supply a policy for
     * handling the case where the host to connect to and
     * the server name from the certificate mismatch.
     *
     * The default implementation will deny such connections.
     */
    private static HostnameVerifier defaultHostnameVerifier =
        new HostnameVerifier() {
            public boolean verify(String urlHostname, String certHostname) {
                return false;
            }
        };

我预计我的 POST 请求会失败,因为此验证程序总是返回false。不是这种情况。该评论已经指出存在某种回调机制。我不知道的是:defaultHostnameVerifier验证连接的主机名和证书还是虚拟实现?

我当前的编码如下所示:

private HttpURLConnection openConnection(String url) throws IOException {
    URL urly = new URL(url);
    final HttpURLConnection con;
    Proxy proxy = getProxy();
    if (proxy == null) {
        con = (HttpURLConnection) urly.openConnection();
    } else {
        con = (HttpURLConnection) urly.openConnection(proxy);
    }

    if (con instanceof HttpsURLConnection) {
        HostnameVerifier verifier = ((HttpsURLConnection) con).getHostnameVerifier(); // there is a default set
        System.out.println(verifier.getClass().getName());
        
    }
    return con;
}

AsyncHttpClient关于[2] ,我找到了一些解释。由于我此时不使用它,因此我可以安全地使用默认实现吗?

[1] https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/com/sun/net/ssl/HttpsURLConnection.java#L76

[2] https://kevinlocke.name/bits/2012/10/03/ssl-certificate-verification-in-dispatch-and-asynchttpclient/

标签: javahttps

解决方案


正如顶部的评论所说,该类不再使用;它是抽象的用户可见类,现在被javax.net.HttpsURLConnection取代,您将观察到它具有相同的代码。但是 https URL 的实现类是sun.net.www.protocol.https.HttpsURLConnectionImpl,它只是包装(委托给)sun.net.www.protocol.https.DelegateHttpsURLConnection ,它是sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection的子类建立实际连接使用sun.net.www.protocol.HttpsClient ,特别是 .afterConnect()。作为背景,在 Java 的早期版本中SSLSocket没有做hostname校验,所以HttpsURLConnection的实现只好加了。在 Java 7 中,SSLSocket确实支持“端点识别”,因此当 afterConnect() 识别到此 URL 连接上的 hostnameVerifier 是默认值时,它会关闭needToCheckSpoofing并设置SSLSocketto do 端点识别。

javax.net.ssl.SSLSocket是一个抽象类,实际上是由sun.security.ssl.SSLSocketImpl和几十个相关类实现的,包括sun.security.ssl.ClientHandshaker.serverCertificate()调用可配置的信任管理器,默认情况下是sun.security.ssl.X509TrustManagerImplcheckTrusted()中调用了checkIdentity(),它调用了带有 TYPE_TLS 的sun.security.util.HostnameChecker (实际上是 HTTPS RFC 2818),并进行了实际检查。

很高兴你问?

PS:对该网页的分析,即 HttpsURLConnection.DefaultHostnameVerifier 仅因不匹配而被调用,是完全错误的。如上所述,它被绕过并且从未被实际实现调用。

此外,我假设您意识到 java 7 多年来一直不受支持,除非您付费。虽然我知道在最近的版本中这个区域没有改变。Java 11 确实添加了一个新的 java.net.http.HttpClient,它在功能上取代了 [Http,Https]URLConnection。


推荐阅读