httpClient,Certificate for ip/域名 doesn't match any of the subject altinative names: []问题处理

本文深入探讨了在HTTPS双向认证中遇到的SSLPeerUnverifiedException异常,详细分析了错误原因,涉及证书的subjectalternativename(SAN)设置问题,并提供了三种解决方案,包括修改请求域名/IP、设置证书SAN和关闭主机认证。

在做https双向认证通信类开发的时候,遇到一个问题,错误信息是:javax.net.ssl.SSLPeerUnverifiedException: Certificate for <我请求的IP地址> doesn't match any of the subject alternative names: []

遇到这种情况,先查看你安装的Certificate证书的subject alternative name(SAN)是否有设置,设置的记录是否包含你请求的IP/域名,如果没有设置即报此错误。(关于SAN的科普建议百度)

百度的证书举例:

一个证书保护N多个域名,而我所请求的服务器的证书没有设置SAN,所以在做https请求时,走到SSLSocketConnectionFactory的connectSocketFactory时,verifyHostname抛异常,见代码。

SSLSocketConnectionFactory:

    @Override
    public Socket connectSocket(
            final int connectTimeout,
            final Socket socket,
            final HttpHost host,
            final InetSocketAddress remoteAddress,
            final InetSocketAddress localAddress,
            final HttpContext context) throws IOException {
        Args.notNull(host, "HTTP host");
        Args.notNull(remoteAddress, "Remote address");
        final Socket sock = socket != null ? socket : createSocket(context);
        if (localAddress != null) {
            sock.bind(localAddress);
        }
        try {
            if (connectTimeout > 0 && sock.getSoTimeout() == 0) {
                sock.setSoTimeout(connectTimeout);
            }
            if (this.log.isDebugEnabled()) {
                this.log.debug("Connecting socket to " + remoteAddress + " with timeout " + connectTimeout);
            }
            sock.connect(remoteAddress, connectTimeout);
        } catch (final IOException ex) {
            try {
                sock.close();
            } catch (final IOException ignore) {
            }
            throw ex;
        }
        // Setup SSL layering if necessary
        if (sock instanceof SSLSocket) {
            final SSLSocket sslsock = (SSLSocket) sock;
            this.log.debug("Starting handshake");
            sslsock.startHandshake();
            verifyHostname(sslsock, host.getHostName());
            return sock;
        } else {
            return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), context);
        }
    }

verifyHostname实现细节截取:

  final Certificate[] certs = session.getPeerCertificates();
                    final X509Certificate x509 = (X509Certificate) certs[0];
                    final X500Principal peer = x509.getSubjectX500Principal();

                    this.log.debug(" peer principal: " + peer.toString());
                    final Collection<List<?>> altNames1 = x509.getSubjectAlternativeNames();
                    if (altNames1 != null) {
                        final List<String> altNames = new ArrayList<String>();
                        for (final List<?> aC : altNames1) {
                            if (!aC.isEmpty()) {
                                altNames.add((String) aC.get(1));
                            }
                        }
                        this.log.debug(" peer alternative names: " + altNames);
                    }

后面会将从证书里获取到的altNames和所请求的host做比对,如果没有和host所匹配的,则抛如题的异常了。

            if (!this.hostnameVerifier.verify(hostname, session)) {
                final Certificate[] certs = session.getPeerCertificates();
                final X509Certificate x509 = (X509Certificate) certs[0];
                final List<SubjectName> subjectAlts = DefaultHostnameVerifier.getSubjectAltNames(x509);
                throw new SSLPeerUnverifiedException("Certificate for <" + hostname + "> doesn't match any " +
                        "of the subject alternative names: " + subjectAlts);
            }

解决办法

1. 修改请求的域名/ip,使证书可以覆盖到。

2. 设置证书的SAN,覆盖到请求的域名/ip。

3. 从代码上关闭https请求的主机认证,代码:

        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[] { "TLSv1" },
                null,
                NoopHostnameVerifier.INSTANCE);//暂时关闭hostnameVerify
//                SSLConnectionSocketFactory.getDefaultHostnameVerifier());

 

问题解决。

<think>好的,用户现在遇到了两个问题:SSL证书验证失败和连接被拒绝的错误。我需要结合之前的对话和提供的引用资料来给出解决方案。 首先,用户之前的问题是关于连接到127.0.0.1:7897被拒绝,现在又遇到了SSL证书的问题。可能用户是在尝试连接华为的服务器时遇到了这些错误,比如在集成华为的AGConnect插件或访问其Maven仓库时。 先看看SSL证书错误。根据引用[1]和[3],SSLPeerUnverifiedException通常是因为证书的主机名不匹配。可能用户访问的URL实际解析的IP域名与证书中的Subject Alternative Names(SAN)不匹配。比如,用户可能配置了代理,导致请求被重定向到错误的地址,或者DNS解析有问题。 另外,引用[3]和[4]提到两种解决方法:一种是将正确的域名添加到信任列表,另一种是忽略证书验证。不过忽略证书验证不安全,只适合测试环境。用户可能需要检查他们的代理设置是否正确,或者是否直接访问了正确的URL。 接下来是连接被拒绝的问题,可能和之前的代理配置有关。用户可能在之前的解决方案中关闭了代理,但现在又出现了新的问题,或者SSL错误导致连接未能建立。需要确认端口监听情况,比如华为的仓库是否使用正确的端口,或者是否有防火墙阻挡了连接。 需要综合这两个问题。可能用户在尝试连接时,由于代理配置错误,导致请求被发送到错误的地址,从而触发了SSL证书错误,或者因为连接被拒绝而无法建立SSL连接。需要分步骤排查:先解决连接问题,再处理SSL证书问题。 可能的解决方案步骤包括检查代理配置、验证网络连通性、检查证书是否正确、以及必要时调整SSL验证设置。需要确保用户使用的URL是正确的,并且证书中的SAN包含该域名。如果用户使用IP地址访问,而证书只绑定了域名,就会引发此错误。建议用户使用正确的域名访问,并检查证书信息。 此外,用户可能需要更新证书或配置信任库,特别是在使用自签名证书时。如果问题依然存在,可能需要联系华为开发者支持,确认其证书配置是否正确。 最后,需要提醒用户忽略证书验证的风险,仅作为临时解决方案,生产环境中应使用有效证书。</think>### SSL证书验证失败与连接被拒绝的综合解决方案 #### 一、问题关联性分析 根据引用[^1][^3][^4],`SSLPeerUnverifiedException`和连接拒绝错误通常形成因果关系: 1. **证书验证失败**导致连接无法建立,最终触发`Connection refused` 2. **代理配置残留**使请求错误路由到`127.0.0.1:7897`,而该端口无有效SSL证书 #### 二、分阶段排查流程 **阶段1:基础环境验证** ```bash # 验证DNS解析(必须匹配证书SAN) nslookup developer.huawei.com # 测试原始连接(跳过代理) curl -vk https://developer.huawei.com/repo/ ``` - **关键指标**:检查`CN`和`Subject Alternative Name`是否包含`developer.huawei.com` - **典型异常**:若返回IP地址与证书域名不匹配,需进入阶段2 **阶段2:证书深度检查** ```java // 通过Java代码提取证书信息 SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) {} public void checkServerTrusted(X509Certificate[] chain, String authType) { System.out.println("Subject: " + chain[0].getSubjectX500Principal()); System.out.println("SAN: " + chain[0].getSubjectAlternativeNames()); } public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}, null); ``` - **预期输出**:证书SAN列表应包含请求的域名[^3] **阶段3:代理配置清除** ```gradle // 检查gradle.properties中的残留配置 grep -E "http.proxy|https.proxy" gradle.properties // 清除Maven代理配置 sed -i '' '/<proxies>/,/<\/proxies>/d' ~/.m2/settings.xml ``` #### 三、安全解决方案(生产环境推荐) **方案1:证书强制验证模式** ```java // 创建白名单验证器 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( SSLContexts.custom().loadTrustMaterial(trustStore).build(), new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return hostname.equals("developer.huawei.com") || hostname.endsWith(".huawei.com"); } } ); ``` **优势**:精确控制可信域名,避免完全禁用验证[^4] **方案2:证书链修复(针对华为特定案例)** ```bash # 下载华为最新根证书 keytool -importcert -alias huawei-root -file HuaweiRootCA.crt \ -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit ``` #### 四、临时解决方案(仅限开发环境) ```java // 使用Apache HttpClient的安全绕过配置 CloseableHttpClient client = HttpClients.custom() .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) .setSSLContext(SSLContexts.custom().loadTrustMaterial(null, (chain, authType) -> true).build()) .build(); ``` **风险提示**:此配置将禁用所有SSL验证,仅用于紧急调试[^3][^4] #### 五、网络层联合排查 1. **抓包分析TCP握手** ```bash tcpdump -i any -vvv 'host developer.huawei.com and (tcp port 443 or tcp port 7897)' ``` - 检查`SYN`包是否发往正确IP - 验证是否出现`RST`包(表明防火墙拦截) 2. **证书链完整性验证** ```bash openssl s_client -connect developer.huawei.com:443 -showcerts ``` - 确认中间证书完整 - 检查证书过期时间 --- ### 相关问题 1. 如何在不同构建工具(Gradle/Maven)中安全配置SSL证书? 2. 企业内网环境下如何正确部署自签名证书? 3. HTTP客户端如何实现动态域名白名单验证? 4. 如何通过Wireshark诊断SSL/TLS握手失败问题
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值