六、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 功能的实现和测试 。

 

posted @ 2017-09-07 16:29  阅历笔记  阅读(4496)  评论(0编辑  收藏  举报