具有通配符或SAN证书的网站不会调用HostNameVerifier

时间:2018-10-05 12:29:37

标签: java security ssl-certificate

在我们的应用程序中,我们正在通过HTTPS对身份验证服务进行HTTP调用。 我需要确保我们进行了所有必要的检查,以确保连接安全。作为它的一部分,我必须实现以下内容:

  1. 验证授权服务器的证书是由著名的CA颁发的。
  2. 验证存在于身份验证服务器证书中的主机名是否与我们用于连接到身份验证服务器的主机名匹配。

我对项目1没有任何问题。

对于第2项,我尝试使用HostNameVerifier的实现。

到目前为止,我的代码:

package com.company;

import sun.security.x509.X500Name;

import javax.net.ssl.*;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class Main {

    public static void main(String[] args) throws Exception {

        URL endpoint = new URL("https://example.com");
        HttpsURLConnection conn = (HttpsURLConnection) endpoint.openConnection();
        conn.setHostnameVerifier(new MyHostNameVerifier());

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[] {trustAll}, null);
        conn.setSSLSocketFactory(sslContext.getSocketFactory());

        System.out.println("HTTP Response: " + conn.getResponseCode());
    }

    public static final X509TrustManager trustAll = new X509TrustManager() {

        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates,
                                       String s) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates,
                                       String s) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    };

    public static class MyHostNameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostName, SSLSession sslSession) {
            System.out.println("*** Validating host name for " + hostName);
            return false;
        }
    }
}

我期望MyHostNameVerifier#verify将始终被调用,我可以在那里进行某些检查。

但是,我发现仅当我使用站点的IP地址或仅当主机名与证书主题或任何主题备用名称不匹配时,才会调用HostNameVerifier#verify方法。

在以下情况下不会调用

HostNameVerifier#verify

  1. 如果站点的证书是通配符证书,并且与使用的主机名匹配。
  2. 如果站点的证书是SAN证书,并且我使用的主机名是主题的CN或被列为主题备用名称值之一。

例如: 在上面的代码中,我试图连接到“ example.com”。
SSL证书“ example.com”已颁发给CN“ www.example.org”。
但是,证书的“主题备用名称”(SAN)中列出了“ example.com”。

如果我改用IP地址“ example.com”,则将调用我的主机名验证程序。

URL endpoint = new URL("https://93.184.216.34/");

有人可以向我确认Java在内部处理通配符证书和SAN证书检查吗?如果是,那么,我可能不必进行任何其他代码更改。

1 个答案:

答案 0 :(得分:0)

在引用Java documentation的情况下,我可能会走出一些界限,但这是我们最纯正的消息来源。

  

在握手过程中,如果URL的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口的实现者,以确定是否应允许此连接。

     

当URL主机名验证的默认规则失败时,将使用这些回调。

从根本上确认您的观察,即一旦输入无效证书,它将随后(仅然后)调用该函数以查看是否仍要允许它。通过您自己的测试(正确的通配符证书未调用该函数),我们可以说他已明确确认该函数有效,并且不需要您的帮助。 SAN证书也是如此。