You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java SSL无SAN证书的IP主机验证问题及合规方案咨询

解决方案:自定义HostnameVerifier实现IP与证书CN的校验

核心思路

Java默认的主机名校验逻辑对IP地址强制要求证书包含SAN字段,无法通过CN匹配。我们可以通过自定义HostnameVerifier,针对IP地址场景手动校验证书CN与目标IP是否匹配,同时保留非IP地址的默认校验逻辑,既满足合规要求,又解决证书无SAN的问题。

实现步骤

1. 编写自定义HostnameVerifier

实现javax.net.ssl.HostnameVerifier接口,区分IP与非IP地址的校验逻辑:

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.regex.Pattern;

public class IpCnHostnameVerifier implements HostnameVerifier {
    // IP地址匹配正则
    private static final Pattern IP_PATTERN = Pattern.compile(
        "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$");
    // 保留默认校验器用于非IP场景
    private final HostnameVerifier defaultVerifier;

    public IpCnHostnameVerifier(HostnameVerifier defaultVerifier) {
        this.defaultVerifier = defaultVerifier;
    }

    @Override
    public boolean verify(String hostname, SSLSession session) {
        // 处理IP地址场景:校验证书CN与IP是否一致
        if (IP_PATTERN.matcher(hostname).matches()) {
            try {
                Certificate[] peerCerts = session.getPeerCertificates();
                if (peerCerts.length > 0 && peerCerts[0] instanceof X509Certificate) {
                    X509Certificate x509Cert = (X509Certificate) peerCerts[0];
                    String certCn = extractCommonName(x509Cert.getSubjectDN().getName());
                    return hostname.equals(certCn);
                }
            } catch (Exception e) {
                // 证书解析失败或无有效CN,返回校验失败
                return false;
            }
            return false;
        }
        // 非IP地址用默认校验逻辑
        return defaultVerifier.verify(hostname, session);
    }

    // 从证书DN中提取CN字段
    private String extractCommonName(String distinguishedName) {
        String[] dnParts = distinguishedName.split(",");
        for (String part : dnParts) {
            String trimmedPart = part.trim();
            if (trimmedPart.startsWith("CN=")) {
                return trimmedPart.substring(3);
            }
        }
        return null;
    }
}

2. 在HTTPS请求中启用自定义校验器

根据你使用的HTTP客户端,设置自定义的HostnameVerifier:

示例1:HttpsURLConnection

import javax.net.ssl.HttpsURLConnection;
import java.net.URL;

public class HttpsRequestExample {
    public static void main(String[] args) throws Exception {
        URL url = new URL("https://193.321.1.321/customers");
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        
        // 设置自定义校验器,保留默认逻辑处理非IP地址
        connection.setHostnameVerifier(new IpCnHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier()));
        
        // 执行POST请求
        connection.setRequestMethod("POST");
        connection.setDoOutput(true);
        // 后续请求体写入、响应处理逻辑...
    }
}

示例2:Apache HttpClient

import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;

import javax.net.ssl.SSLContext;

public class ApacheHttpClientExample {
    public static void main(String[] args) throws Exception {
        SSLContext sslContext = SSLContexts.createDefault();
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
                sslContext,
                new IpCnHostnameVerifier(SSLConnectionSocketFactory.getDefaultHostnameVerifier())
        );
        
        try (CloseableHttpClient client = HttpClients.custom()
                .setSSLSocketFactory(sslSocketFactory)
                .build()) {
            HttpPost post = new HttpPost("https://193.321.1.321/customers");
            // 执行请求并处理响应...
        }
    }
}

关于修改默认校验逻辑的说明

直接修改sun.security.util.HostnameChecker的isIpAddress方法属于操作JVM私有API,存在以下问题:

  • 依赖JVM实现细节,不同版本的OpenJDK/Oracle JDK可能有差异,升级JVM后代码可能失效
  • 违反Java规范,私有API随时可能被移除或修改
  • 全局修改会影响所有HTTPS请求,风险不可控

因此不推荐这种方式,自定义HostnameVerifier是标准、可控的合规解决方案。

合规性说明

该方案没有完全跳过主机名校验,仅针对IP地址场景调整了匹配规则(用CN代替SAN),仍在验证证书与目标地址的关联性,符合"不跳过主机校验"的要求。但需确保证书的CN字段确实与目标IP一致,避免引入安全风险。

内容的提问来源于stack exchange,提问作者ROBLCSnail

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 09:09:21