java - 具有通配符或 SAN 证书的站点不调用 HostNameVerifier

标签 java security ssl-certificate

在我们的应用程序中,我们通过 HTTPS 对 auth 服务进行 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 将始终被调用,我可以在那里进行某些检查。

但是,我观察到 HostNameVerifier#verify 方法仅在我使用站点的 IP 地址时调用,或者仅在主机名与证书主题或任何主题替代不匹配时调用名字。

HostNameVerifier#verify 在以下情况下不会被调用:

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

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

如果我改为使用“example.com”的 IP 地址,则会调用我的主机名 validator 。

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

谁能向我确认 Java 在内部处理通配符证书和 SAN 证书检查?如果是,那么我可能不需要做任何额外的代码更改。

最佳答案

我引用 Java documentation 可能会有点越界但它是我们最纯净的来源。

During handshaking, if the URL's hostname and the server's identification hostname mismatch, the verification mechanism can call back to implementers of this interface to determine if this connection should be allowed.

These callbacks are used when the default rules for URL hostname verification fail.

这基本上证实了您的观察,即一旦您输入无效证书,它就会调用该函数以查看您是否仍要允许它。通过您自己的测试,正确的通配符证书没有调用该函数,我们可以说他明确确认它有效并且不需要您的帮助。 SAN证书同理。

关于java - 具有通配符或 SAN 证书的站点不调用 HostNameVerifier,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52665673/

相关文章:

java - 如何查找文件中的子字符串是否已存在于 hashmap 中?

java - "Variable might not have been initialized"错误

未提供 Azure 网站中间证书?

json - 了解 Web 身份验证上下文中的 JSON Web token (JWT)

java - servlet 中的 System.exit

ssl - 配置导入的自签名 SSL 证书到 SQL Server Express

https 流量似乎流向根域而不是子域

java - perl Inline::Java 与 JAXB

java - 在安全上下文中仅在重定向时访问页面

java - 管理/创建动态安全策略规则的最佳 Java 框架?