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

Android HiveMQ客户端连接test.mosquitto.org 8885端口SSL握手失败

解决Android端HiveMQ连接test.mosquitto.org 8885端口的SSL握手错误(证书无匹配subjectAltNames)

问题原因

test.mosquitto.org的8885端口使用的是无Subject Alternative Name(SAN)字段的自签名证书。从Android 7.0开始,系统默认的Conscrypt安全提供者会强制要求SSL证书包含与服务器域名匹配的SAN字段——即使证书已被信任,缺少SAN也会触发CertificateException: 证书上无匹配的subjectAltNames错误。而8883/8886端口使用的是正规CA签发、带合法SAN的证书,因此能正常连接。

解决方案

方案1:测试环境临时绕过SAN校验(仅用于测试,禁止生产环境使用)

通过自定义X509TrustManager跳过SAN字段的校验逻辑,具体实现如下:

  1. 实现自定义TrustManager:
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class NoSanCheckTrustManager implements X509TrustManager {
    private final X509TrustManager originalTrustManager;

    public NoSanCheckTrustManager(X509TrustManager original) {
        this.originalTrustManager = original;
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        originalTrustManager.checkClientTrusted(chain, authType);
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        try {
            // 先尝试默认校验,失败则针对性跳过SAN检查
            originalTrustManager.checkServerTrusted(chain, authType);
        } catch (CertificateException e) {
            // 仅捕获SAN不匹配的异常,其他异常仍正常抛出
            if (e.getMessage() != null && e.getMessage().contains("subjectAltNames")) {
                return;
            }
            throw e;
        }
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return originalTrustManager.getAcceptedIssuers();
    }
}
  1. 配置HiveMQ客户端使用自定义TrustManager:
import javax.net.ssl.TrustManagerFactory;
import java.security.KeyStore;
import java.util.UUID;
import com.hivemq.client.mqtt.MqttClient;
import com.hivemq.client.mqtt.MqttClientSslConfig;
import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient;

// 获取系统默认TrustManagerFactory
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);

// 替换为自定义TrustManager
X509TrustManager originalTm = (X509TrustManager) tmf.getTrustManagers()[0];
X509TrustManager customTm = new NoSanCheckTrustManager(originalTm);

// 构建自定义SSL配置
MqttClientSslConfig sslConfig = MqttClientSslConfig.builder()
        .trustManagerFactory(new TrustManagerFactory() {
            @Override
            protected void engineInit(KeyStore keyStore) throws Exception {}
            @Override
            protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws Exception {}
            @Override
            protected javax.net.ssl.TrustManager[] engineGetTrustManagers() {
                return new javax.net.ssl.TrustManager[]{customTm};
            }
        })
        .build();

// 初始化客户端并添加用户名密码
MqttClientBuilder mqttClientBuilder = MqttClient.builder()
        .identifier(UUID.randomUUID().toString())
        .serverHost("test.mosquitto.org")
        .serverPort(8885)
        .sslConfig(sslConfig);

Mqtt3AsyncClient mqtt3Client = mqttClientBuilder
        .useMqttVersion3()
        .buildAsync();

// 发起连接
mqtt3Client.connectWith()
        .simpleAuth()
        .username("rw")
        .password("readwrite".getBytes())
        .applySimpleAuth()
        .send()
        .subscribe(
                connectAck -> {
                    // 连接成功处理逻辑
                },
                throwable -> {
                    // 连接失败处理逻辑
                }
        );

方案2:生产环境安全解决方案

如果你计划将代码用于生产应用,绝对不能使用方案1的绕过逻辑,建议:

  • 改用带有合法CA签发证书的MQTT服务器(类似test.mosquitto.org的8883/8886端口)。
  • 如果必须使用自签名证书的服务器,务必为证书添加正确的SAN字段(将服务器域名作为DNS条目加入证书),然后将该证书导入到Android应用的信任存储中,确保SSL校验完全合规。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 01:40:01