Emqx SSL 证书连接问题

开启EMQX SSL证书连接后. MQTTX 可以直连 但是Java 使用SpringBoot 却无法直连.
首先需求是 不能通过 Nginx 并且证书是以ssl的方式进行连接. 我使用MQTTX是可以连接的 以下是代码:

MqttConf:

@Bean
public MqttPahoClientFactory mqttClientFactory() {
    DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
    MqttConnectOptions options = new MqttConnectOptions();

    // 设置连接的用户名
    if (StringUtils.hasText(username)) {
        options.setUserName(username);
    }

    // 设置连接的密码
    if (StringUtils.hasText(productKey)) {
        options.setPassword(productKey.toCharArray());
    }

    // 设置服务器地址
    if (StringUtils.hasText(hostUrl)) {
        if (hostUrl.contains(",")) {
            options.setServerURIs(hostUrl.split(","));
        } else {
            options.setServerURIs(new String[]{hostUrl});
        }
    }

    log.info("当前请求EMQX连接:{}", hostUrl);

    // 设置超时时间 单位为秒
    options.setConnectionTimeout(30);
    options.setMaxInflight(100);
    options.setKeepAliveInterval(20);

    // 设置遗嘱消息
    options.setWill("willTopic", WILL_DATA, qos, false);

    // 自动重连和清理会话
    options.setAutomaticReconnect(true);
    options.setCleanSession(true);

    // 初始化 CA 证书路径
    initCaPath(options);
    factory.setConnectionOptions(options);
    return factory;
}

/***
 * @Description 初始化 CA 证书路径
 * @param options MqttConnectOptions
 */
private void initCaPath(MqttConnectOptions options) {
    // 获取类加载器
    ClassLoader classLoader = MqttConfig.class.getClassLoader();
    String caCrtPath = classLoader.getResource("ca.crt").getPath();
    String clientPemPath = classLoader.getResource("client.crt").getPath();
    String clientKeyPath = classLoader.getResource("client.key").getPath();

    try {
        //options.setSocketFactory(SSLUtils.getSocketFactory("D:\\ca\\ca.pem", "D:\\CA\\client.pem", "D:\\CA\\client-key.pem", ""));
        options.setSocketFactory(SSLUtils.getSocketFactory(caCrtPath, clientPemPath, clientKeyPath, ""));
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

以下是SSLUtils:
public class SSLUtils {

public static SSLSocketFactory getSingleSocketFactory(final String caCrtFile) throws Exception {
    Security.addProvider(new BouncyCastleProvider());

    // Load CA certificates
    KeyStore caKs = loadCAKeyStore(caCrtFile);
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(caKs);
    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, tmf.getTrustManagers(), null);
    return sslContext.getSocketFactory();
}


public static SSLSocketFactory getSocketFactory(final String caCrtFile, final String crtFile, final String keyFile, final String password) throws Exception {
    Security.addProvider(new BouncyCastleProvider());

    // Load CA certificates
    KeyStore caKs = loadCAKeyStore(caCrtFile);

    // Load client certificate chain and key
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(null, null);

    // Load the entire client certificate chain
    Certificate[] chain;
    try (FileInputStream fis = new FileInputStream(crtFile)) {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        Collection<? extends Certificate> certs = cf.generateCertificates(fis);
        chain = certs.toArray(new Certificate[0]);
    }

    // Load client private key
    try (PEMParser pemParser = new PEMParser(new FileReader(keyFile))) {
        Object object = pemParser.readObject();
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        KeyPair key = converter.getKeyPair((PEMKeyPair) object);
        ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), chain);
    }

    // Set up key managers and trust managers
    TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
    tmf.init(caKs);
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(ks, password.toCharArray());

    // finally, create SSL socket factory
    SSLContext context = SSLContext.getInstance("TLSv1.2");
    context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    return context.getSocketFactory();
}

private static KeyStore loadCAKeyStore(String caCrtFile) throws Exception {
    KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
    caKs.load(null, null);
    try (FileInputStream fis = new FileInputStream(caCrtFile)) {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        Collection<? extends Certificate> caCerts = cf.generateCertificates(fis);
        int certIndex = 1;
        for (Certificate caCert : caCerts) {
            String alias = "ca-certificate-" + certIndex++;
            caKs.setCertificateEntry(alias, caCert);
        }
    }
    return caKs;
}

}

以下是EMQX 配置 图片

跪请求各路大神帮忙 谢谢~

最终会出现报错如下:

15:00:07.549 [main] WARN o.a.c.l.WebappClassLoaderBase - [log,173] - The web application [ROOT] appears to have started a thread named [Thread-12] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.base@17.0.9/sun.net.dns.ResolverConfigurationImpl.notifyAddrChange0(Native Method)
java.base@17.0.9/sun.net.dns.ResolverConfigurationImpl$AddressChangeListener.run(ResolverConfigurationImpl.java:176)
15:00:07.600 [main] ERROR o.s.b.SpringApplication - [reportFailure,824] - Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘mqttClientFactory’ defined in class path resource [com/hthium/gather/consumer/DeviceGateWayMqttListener.class]: Failed to instantiate [org.springframework.integration.mqtt.core.MqttPahoClientFactory]: Factory method ‘mqttClientFactory’ threw exception with message: MqttException (0) - javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:659)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:493)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1332)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1162)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:942)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:436)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
at com.hthium.gather.GatherApplication.main(GatherApplication.java:26)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.integration.mqtt.core.MqttPahoClientFactory]: Factory method ‘mqttClientFactory’ threw exception with message: MqttException (0) - javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:171)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655)
… 17 common frames omitted
Caused by: java.lang.RuntimeException: MqttException (0) - javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.hthium.mqtt.config.MqttConfig.mqttClientFactory(MqttConfig.java:140)
at com.hthium.gather.consumer.DeviceGateWayMqttListener$$SpringCGLIB$$0.CGLIB$mqttClientFactory$4()
at com.hthium.gather.consumer.DeviceGateWayMqttListener$$SpringCGLIB$$1.invoke()
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
at com.hthium.gather.consumer.DeviceGateWayMqttListener$$SpringCGLIB$$0.mqttClientFactory()
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:139)
… 18 common frames omitted
Caused by: org.eclipse.paho.client.mqttv3.MqttException: MqttException
at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:38)
at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:738)
at java.base/java.lang.Thread.run(Thread.java:842)
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:378)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:316)
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1357)
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(CertificateMessage.java:1232)
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(CertificateMessage.java:1175)
at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:396)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:480)
at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:458)
at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:201)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1506)
at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1421)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426)
at org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule.start(SSLNetworkModule.java:159)
at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:724)
… 1 common frames omitted
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:439)
at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:306)
at java.base/sun.security.validator.Validator.validate(Validator.java:264)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231)
at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1341)
… 14 common frames omitted
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:148)
at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:129)
at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:434)
… 19 common frames omitted
15:00:10.552 [Thread-1] WARN c.a.n.c.h.HttpClientBeanHolder - [shutdown,102] - [HttpClientBeanHolder] Start destroying common HttpClient
15:00:10.552 [Thread-7] WARN c.a.n.c.n.NotifyCenter - [shutdown,136] - [NotifyCenter] Start destroying Publisher
15:00:10.553 [Thread-7] WARN c.a.n.c.n.NotifyCenter - [shutdown,153] - [NotifyCenter] Destruction of the end
15:00:10.554 [Thread-1] WARN c.a.n.c.h.HttpClientBeanHolder - [shutdown,111] - [HttpClientBeanHolder] Destruction of the end
Disconnected from the target VM, address: ‘127.0.0.1:62767’, transport: ‘socket’

Process finished with exit code 1

爱莫能助,看不懂 java :sweat_smile:

调皮~ 这里报错就是说要信任证书. 但是我无法绕过信任证书这一步. 我有证书 但是却要绕过secure 这个步骤.