六、Mosquitto 高級應用之SSL/TLS


  mosquitto提供SSL支持加密的網絡連接和身份驗證、本章節講述次功能的實現、 在此之前需要一些准備工作。

准本工作: 一台 Linux 服務器、 安裝好 openssl (不會明白怎么安裝 openssl 的可以在網上搜索下、 我就不在這講解了)

准本工作完成后我們開始來制作所需要的證書。

注:在生成證書過程 在需要輸入 【Common Name 】 參數的地方 輸入主機ip

一、Certificate Authority

Generate a certificate authority certificate and key.

  openssl req -newkey rsa:2045 -x509 -nodes -sha256 -days 36500 -extensions v3_ca -keyout ca.key -out ca.crt

二、Server

Generate a server key.

  openssl genrsa -des3 -out server.key 2048

Generate a server key without encryption.

  openssl genrsa -out server.key 2048

Generate a certificate signing request to send to the CA.

  openssl req -out server.csr -key server.key -new

Send the CSR to the CA, or sign it with your CA key:

  openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days <duration>

三、Client

Generate a client key.

  openssl genrsa -des3 -out client.key 2048

Generate a certificate signing request to send to the CA.

  openssl req -out client.csr -key client.key -new

Send the CSR to the CA, or sign it with your CA key:

  openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days <duration>

到此處證書基本生成完成、 此時會在對應的目錄下生成如下證書

ca.crt  ca.key  ca.srl  client.crt  client.csr  client.key  server.crt  server.csr  server.key

Server 端:ca.crt、 client.crt 、server.key

Client 端:ca.crt、 client.csr  client.key

其中 ca.crt 是同一個證書。

四、Mosquitto 服務進行配置證書

1> 打開配置文件 mosquitto.conf 修改如下配置  

  cafile /home/mosquitto-CA/ssl/ca.crt // 對應上述生成證書的絕對路勁
  certfile /home/mosquitto-CA/ssl/server.crt
  certfile /home/mosquitto-CA/ssl/server.key
  require_certificate true
  use_identity_as_username true

 

配置完成后 保存退出 到這 SSL/TLS  功能基本完成。

2> 啟動 Mosquitto 服務

  mosquitto -c mosquitto.conf –v

五、Java 端實現

Java 端的 SSL 客戶端的實現和<<Mosquitto Java 客戶端實現>> 講解的基本一致 在這就不多做解釋了、直接放代碼。

需要引入依賴

 

<dependency>
    <groupId>org.eclipse.paho</groupId>
    <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
    <version>1.0.2</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk16</artifactId>
    <version>1.45</version>
</dependency>

 

 

 

1> ClientMQTT

 

 1 import java.util.concurrent.ScheduledExecutorService;
 2 
 3 import javax.net.ssl.SSLSocketFactory;
 4 
 5 import org.eclipse.paho.client.mqttv3.MqttClient;
 6 import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
 7 import org.eclipse.paho.client.mqttv3.MqttException;
 8 import org.eclipse.paho.client.mqttv3.MqttTopic;
 9 import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
10 
11 public class ClientMQTT {
12 
13     public static final String HOST = "ssl://172.16.192.102:1883";
14     public static final String TOPIC = "root/topic/123";
15     private static final String clientid = "client11";
16     private MqttClient client;
17     private MqttConnectOptions options;
18     private String userName = "admin";
19     private String passWord = "admin";
20     public static String caFilePath = "D:/keystore/Mosquitto-ca/ssl/ca.crt";
21     public static String clientCrtFilePath = "D:/keystore/Mosquitto-ca/ssl/client.crt";
22     public static String clientKeyFilePath = "D:/keystore/Mosquitto-ca/ssl/client.key";
23 
24     private ScheduledExecutorService scheduler;
25 
26     private void start() {
27         try {
28             // host為主機名,clientid即連接MQTT的客戶端ID,一般以唯一標識符表示,MemoryPersistence設置clientid的保存形式,默認為以內存保存
29             client = new MqttClient(HOST, clientid, new MemoryPersistence());
30             // MQTT的連接設置
31             options = new MqttConnectOptions();
32             // 設置是否清空session,這里如果設置為false表示服務器會保留客戶端的連接記錄,這里設置為true表示每次連接到服務器都以新的身份連接
33             options.setCleanSession(true);
34             // 設置連接的用戶名
35             options.setUserName(userName);
36             // 設置連接的密碼
37             options.setPassword(passWord.toCharArray());
38             // 設置超時時間 單位為秒
39             options.setConnectionTimeout(10);
40             // 設置會話心跳時間 單位為秒 服務器會每隔1.5*20秒的時間向客戶端發送個消息判斷客戶端是否在線,但這個方法並沒有重連的機制
41             options.setKeepAliveInterval(20);
42             // 設置回調
43             client.setCallback(new PushCallback());
44             MqttTopic topic = client.getTopic(TOPIC);
45             // setWill方法,如果項目中需要知道客戶端是否掉線可以調用該方法。設置最終端口的通知消息
46             options.setWill(topic, "close".getBytes(), 2, true);
47             SSLSocketFactory factory = SslUtil.getSocketFactory(caFilePath, clientCrtFilePath, clientKeyFilePath,
48                     "111111");
49             options.setSocketFactory(factory);
50             client.connect(options);
51             // 訂閱消息
52             int[] Qos = { 1 };
53             String[] topic1 = { TOPIC };
54             client.subscribe(topic1, Qos);
55 
56         } catch (Exception e) {
57             e.printStackTrace();
58         }
59     }
60 
61     public static void main(String[] args) throws MqttException {
62         ClientMQTT client = new ClientMQTT();
63         client.start();
64     }
65 }
View Code

 

2> ServerMQTT

 1 /**
 2  * 
 3  * Title:Server Description: 服務器向多個客戶端推送主題,即不同客戶端可向服務器訂閱相同主題
 4  * 
 5  * @author yueli 2017年9月1日下午17:41:10
 6  */
 7 public class ServerMQTT {
 8 
 9     // tcp://MQTT安裝的服務器地址:MQTT定義的端口號
10     public static final String HOST = "ssl://172.16.192.102:1883";
11     // 定義一個主題
12     public static final String TOPIC = "root/topic/123";
13     // 定義MQTT的ID,可以在MQTT服務配置中指定
14     private static final String clientid = "server11";
15 
16     private MqttClient client;
17     private MqttTopic topic11;
18     private String userName = "mosquitto";
19     private String passWord = "mosquitto";
20     public static String caFilePath = "D:/keystore/Mosquitto-ca/ssl/ca.crt";
21     public static String clientCrtFilePath = "D:/keystore/Mosquitto-ca/ssl/client.crt";
22     public static String clientKeyFilePath = "D:/keystore/Mosquitto-ca/ssl/client.key";
23 
24     private MqttMessage message;
25 
26     /**
27      * 構造函數
28      * 
29      * @throws Exception
30      */
31     public ServerMQTT() throws Exception {
32         // MemoryPersistence設置clientid的保存形式,默認為以內存保存
33         client = new MqttClient(HOST, clientid, new MemoryPersistence());
34         connect();
35     }
36 
37     /**
38      * 用來連接服務器
39      * 
40      * @throws Exception
41      */
42     private void connect() throws Exception {
43         MqttConnectOptions options = new MqttConnectOptions();
44         options.setCleanSession(false);
45         options.setUserName(userName);
46         options.setPassword(passWord.toCharArray());
47         // 設置超時時間
48         options.setConnectionTimeout(10);
49         // 設置會話心跳時間
50         options.setKeepAliveInterval(20);
51         SSLSocketFactory factory = SslUtil.getSocketFactory(caFilePath, clientCrtFilePath, clientKeyFilePath, "111111");
52         options.setSocketFactory(factory);
53         try {
54             client.setCallback(new PushCallback());
55             client.connect(options);
56 
57             topic11 = client.getTopic(TOPIC);
58         } catch (Exception e) {
59             e.printStackTrace();
60         }
61     }
62 
63     /**
64      * 
65      * @param topic
66      * @param message
67      * @throws MqttPersistenceException
68      * @throws MqttException
69      */
70     public void publish(MqttTopic topic, MqttMessage message) throws MqttPersistenceException, MqttException {
71         MqttDeliveryToken token = topic.publish(message);
72         token.waitForCompletion();
73         System.out.println("message is published completely! " + token.isComplete());
74     }
75 
76     /**
77      * 啟動入口
78      * 
79      * @param args
80      * @throws Exception
81      */
82     public static void main(String[] args) throws Exception {
83         ServerMQTT server = new ServerMQTT();
84 
85         server.message = new MqttMessage();
86         server.message.setQos(1);
87         server.message.setRetained(true);
88         server.message.setPayload("hello,topic14".getBytes());
89         server.publish(server.topic11, server.message);
90         System.out.println(server.message.isRetained() + "------ratained狀態");
91     }
92 }
View Code

3> PushCallback

 1 import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
 2 import org.eclipse.paho.client.mqttv3.MqttCallback;
 3 import org.eclipse.paho.client.mqttv3.MqttMessage;
 4 
 5 /**
 6  * 發布消息的回調類
 7  * 
 8  * 必須實現MqttCallback的接口並實現對應的相關接口方法CallBack 類將實現 MqttCallBack。
 9  * 每個客戶機標識都需要一個回調實例。在此示例中,構造函數傳遞客戶機標識以另存為實例數據。 在回調中,將它用來標識已經啟動了該回調的哪個實例。
10  * 必須在回調類中實現三個方法:
11  * 
12  * public void messageArrived(MqttTopic topic, MqttMessage message)接收已經預訂的發布。
13  * 
14  * public void connectionLost(Throwable cause)在斷開連接時調用。
15  * 
16  * public void deliveryComplete(MqttDeliveryToken token)) 接收到已經發布的 QoS 1 或 QoS 2
17  * 消息的傳遞令牌時調用。 由 MqttClient.connect 激活此回調。
18  * 
19  */
20 public class PushCallback implements MqttCallback {
21 
22     public void connectionLost(Throwable cause) {
23         // 連接丟失后,一般在這里面進行重連
24         System.out.println("連接斷開,可以做重連");
25     }
26 
27     public void deliveryComplete(IMqttDeliveryToken token) {
28         System.out.println("deliveryComplete---------" + token.isComplete());
29     }
30 
31     public void messageArrived(String topic, MqttMessage message) throws Exception {
32         // subscribe后得到的消息會執行到這里面
33         System.out.println("接收消息主題 : " + topic);
34         System.out.println("接收消息Qos : " + message.getQos());
35         System.out.println("接收消息內容 : " + new String(message.getPayload()));
36     }
37 }
View Code

4> SslUtil

 1 import java.io.ByteArrayInputStream;
 2 import java.io.InputStreamReader;
 3 import java.nio.file.Files;
 4 import java.nio.file.Paths;
 5 import java.security.KeyPair;
 6 import java.security.KeyStore;
 7 import java.security.Security;
 8 import java.security.cert.X509Certificate;
 9 
10 import javax.net.ssl.KeyManagerFactory;
11 import javax.net.ssl.SSLContext;
12 import javax.net.ssl.SSLSocketFactory;
13 import javax.net.ssl.TrustManagerFactory;
14 
15 import org.bouncycastle.jce.provider.BouncyCastleProvider;
16 import org.bouncycastle.openssl.*;
17 
18 public class SslUtil {
19     public static SSLSocketFactory getSocketFactory(final String caCrtFile, final String crtFile, final String keyFile,
20             final String password) throws Exception {
21         Security.addProvider(new BouncyCastleProvider());
22 
23         // load CA certificate
24         PEMReader reader = new PEMReader(
25                 new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile)))));
26         X509Certificate caCert = (X509Certificate) reader.readObject();
27         reader.close();
28 
29         // load client certificate
30         reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile)))));
31         X509Certificate cert = (X509Certificate) reader.readObject();
32         reader.close();
33 
34         // load client private key
35         reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))),
36                 new PasswordFinder() {
37                     @Override
38                     public char[] getPassword() {
39                         return password.toCharArray();
40                     }
41                 });
42         KeyPair key = (KeyPair) reader.readObject();
43         reader.close();
44 
45         // CA certificate is used to authenticate server
46         KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
47         caKs.load(null, null);
48         caKs.setCertificateEntry("ca-certificate", caCert);
49         TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
50         tmf.init(caKs);
51 
52         // client key and certificates are sent to server so it can authenticate
53         // us
54         KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
55         ks.load(null, null);
56         ks.setCertificateEntry("certificate", cert);
57         ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(),
58                 new java.security.cert.Certificate[] { cert });
59         KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
60         kmf.init(ks, password.toCharArray());
61 
62         // finally, create SSL socket factory
63         SSLContext context = SSLContext.getInstance("TLSv1");
64         context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
65 
66         return context.getSocketFactory();
67     }
68 }
View Code

到這基本完成 Mosquitto 配置SSL/TLS 功能的實現和測試 。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM