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

     

  • 相关阅读:
    cURL(wget)—— 测试 RESTful 接口及模拟 GET/POST/PUT/DELETE/OPTIONS 请求
    docker 第一课 —— 从容器到 docker
    docker 第一课 —— 从容器到 docker
    多类 SVM 的损失函数及其梯度计算
    软RAID 0的技术概要及实现
    用 Linux blkid 命令查找块设备详情
    常用命令
    Ubuntu---samba(安装、配置、使用)***
    系统常用命令
    在64位的UBUBTU 服务器 ***
  • 原文地址:https://www.cnblogs.com/yueli/p/7490453.html
Copyright © 2020-2023  润新知