首页 > 解决方案 > java客户端的MQTT SSL连接失败

问题描述

我有启用了 mqtt 的 rabbit mq 服务器。它启用了 ssl,我有客户端证书和密钥。它从 MQTTBox 和 python paho mqtt 客户端成功连接。但是 java paho mqtt 客户端给出了以下错误。

Exception in thread "main" MqttException (0) - javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca
    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:834)
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:313)
    at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293)
    at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:186)
    at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1144)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1055)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:395)
    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 more

maven依赖细节

<!-- mqtt client-->
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>

<!-- ssl support -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>

以下是我尝试使用的java代码

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PasswordFinder;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;

import java.io.*;
import java.security.cert.*;
import javax.net.ssl.*;

public class ScioMQTTClient {

  public static void main(String[] args) throws Exception {

    MqttClient mqttClient = new MqttClient("ssl://server:8883",
     "clientId1", new MemoryPersistence());

    MqttConnectOptions options = new MqttConnectOptions();
    options.setUserName("user");
    options.setPassword("password".toCharArray());

    SSLSocketFactory socketFactory = getSocketFactory("ca_certificate.pem", "client_certificate.pem",
      "client_key.pem", "");
    options.setSocketFactory(socketFactory);

    mqttClient.connect(options);

    System.out.println("Connected");

  }

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

    // load CA certificate
    PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
    X509Certificate caCert = (X509Certificate) reader.readObject();
    reader.close();

    // load client certificate
    reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
    X509Certificate cert = (X509Certificate) reader.readObject();
    reader.close();

    // load client private key
    reader = new PEMReader(
      new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),
    new PasswordFinder() {
      public char[] getPassword() {
        return password.toCharArray();
      }
    });
    KeyPair key = (KeyPair) reader.readObject();
    reader.close();

    // CA certificate is used to authenticate server
    KeyStore caKs = KeyStore.getInstance("JKS");
    caKs.load(null, null);
    caKs.setCertificateEntry("ca-certificate", caCert);
    TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
    tmf.init(caKs);

    // client key and certificates are sent to server so it can authenticate us
    KeyStore ks = KeyStore.getInstance("JKS");
    ks.load(null, null);
    ks.setCertificateEntry("certificate", cert);
    ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert});
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
    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();

  }

}

这是什么原因。

以下是我使用的 python 代码。它工作正常。

import paho.mqtt.client as mqtt

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
  print("Connected with result code "+str(rc))

  client.subscribe("$SYS/#")

def on_message(client, userdata, msg):
  print(msg.topic+" "+str(msg.payload))

client = mqtt.Client()
client.username_pw_set("user", password="password")
client.on_connect = on_connect
client.on_message = on_message

client.tls_set(ca_certs="ca_certificate.pem", certfile="client_certificate.pem", keyfile="client_key.pem")

client.connect("server", 8883, 60)

client.loop_forever()

这个错误的原因是什么。

标签: javamqttbouncycastlepaho

解决方案


推荐阅读