繼上個星期以後,這個星期有花了兩三天的時間來搞emqtt的ssl/tls加密。html
主流程仍是參考了:https://blog.csdn.net/a704397849/article/details/88885198#commentsedit, 到最後一步用mosquitto_sub訂閱消息的時候始終有問題:java
聯繫了參考文章的做者,而且加了QQ,想不到是個很熱情的人。他從新走了一下流程而且肯定是沒有問題的,給了我一份客戶端的代碼:mysql
package com.zkong.mqttssl; import org.eclipse.paho.client.mqttv3.*; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; public class MqttTLSTest { static MqttClientCallback mqttClientCallback = new MqttClientCallback(); static MqttAsyncClient mqttClient = null; static String username = "stan"; //注意這裏 填你本身的mqtt帳號密碼 static String password = "123456"; //注意這裏 填你本身的mqtt帳號密碼 //String broker = "tcp://xxx.xx.xxx.xxx:1883"; //注意這裏要填本身mqtt服務器所在地址 static String broker = "ssl://192.168.100.117:8883"; //注意這裏要填本身mqtt服務器所在地址 public static void main(String[] args) throws InterruptedException { start(); while (true){ Thread.sleep(10000); } } public static void start() { String clientId = "mqttserver" + String.valueOf(System.currentTimeMillis()); try { mqttClient = new MqttAsyncClient(broker, clientId, new MemoryPersistence()); mqttClient.setCallback(mqttClientCallback); //訂閱 鏈接mqtt服務器 subscribeConnect(); //發佈 鏈接mqtt服務器 //... 略 } catch (MqttException me) { System.out.println("reason " + me.getReasonCode()); System.out.println("msg " + me.getMessage()); System.out.println("loc " + me.getLocalizedMessage()); System.out.println("cause " + me.getCause()); System.out.println("excep " + me); me.printStackTrace(); } } public static void subscribeConnect() { System.out.println("訂閱鏈接"); if (mqttClient != null) { try { MqttConnectOptions connOpts = new MqttConnectOptions(); connOpts.setCleanSession(true); connOpts.setMaxInflight(100000); connOpts.setUserName(username); connOpts.setPassword(password.toCharArray()); //ssl 鏈接 , 這裏的 TrustManager 是本身實現的,沒有去校驗服務端的證書 TrustManager[] trustAllCerts = new TrustManager[1]; TrustManager tm = new MyTM(); trustAllCerts[0] = tm; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, null); SocketFactory factory = sc.getSocketFactory(); connOpts.setSocketFactory(factory); // mqttClient.connect(connOpts, null, new IMqttActionListener() { @Override public void onSuccess(IMqttToken asyncActionToken) { try { //訂閱 topic 爲test 的消息,消息質量1 mqttClient.subscribe("test", 1); } catch (MqttException me) { System.out.println("reason " + me.getReasonCode()); System.out.println("msg " + me.getMessage()); System.out.println("loc " + me.getLocalizedMessage()); System.out.println("cause " + me.getCause()); System.out.println("excep " + me); me.printStackTrace(); } } @Override public void onFailure(IMqttToken asyncActionToken, Throwable exception) { System.out.println("mqtt 沒有鏈接上:" + exception.getMessage()); exception.printStackTrace(); } }); } catch (MqttException me) { System.out.println("reason " + me.getReasonCode()); System.out.println("msg " + me.getMessage()); System.out.println("loc " + me.getLocalizedMessage()); System.out.println("cause " + me.getCause()); System.out.println("excep " + me); me.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } } } //MyTM 是本身實現的認證管理類,裏面並有校驗服務端的證書就返回true,永久成功! static class MyTM implements TrustManager, X509TrustManager { @Override public X509Certificate[] getAcceptedIssuers() { return null; } public boolean isServerTrusted(X509Certificate[] certs) { return true; } public boolean isClientTrusted(X509Certificate[] certs) { return true; } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } } public static class MqttClientCallback implements MqttCallback{ @Override public void connectionLost(Throwable arg0) { System.out.println("mqtt 失去了鏈接"); } @Override public void deliveryComplete(IMqttDeliveryToken arg0) { System.out.println("mqtt 發送完成!"); } @Override public void messageArrived(String topic, MqttMessage message) throws Exception { String content = new String(message.getPayload(), "utf-8"); System.out.println("收到mqtt消息,topic: "+topic+" ,content: "+content); } } }
用這份客戶端代碼訂閱消息,是沒有問題的,由於個人服務器開啓了基於mysql的認證,因此必須提供username和password。sql
要完全走通的,估計還得找一臺乾淨的機器從新安裝emqtt。windows
有些問題在此記錄一下:服務器
關於mosquittoeclipse
它跟emqtt同樣,自己也是一個mqtt的broker,在windows上安裝完以後做爲一個服務存在,當它處於啓動狀態時,會佔掉1883端口致使emqtt沒法徹底啓動,能夠在Windows的服務管理中將其關閉:async
Centos7上安裝openssltcp
http://www.javashuo.com/article/p-muqouusg-eb.htmlide
Centos7上安裝mosquitto
http://www.javashuo.com/article/p-mppgavlk-mw.html
未走通流程:
雙向認證:https://blog.csdn.net/zljintan/article/details/83619309
自帶證書驗證(使用MQTTBox做爲客戶端):http://www.javashuo.com/article/p-nbzatlbi-gq.html