百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

mqtt实战-EMQX MQTT 服务器启用 SSL/TLS 安全连接

bigegpt 2024-08-01 11:54 4 浏览

大家好,我是yangyang.本计划不会最近不会更新mq相关文章,奈何今天使用tls连接生成证书的时候遇到了一个坑,于是,给大家分享一下.

SSL/TLS 证书准备

通常来说,我们会需要数字证书来保证 TLS 通讯的强认证。数字证书的使用本身是一个三方协议,除了通讯双方,还有一个颁发证书的受信第三方,有时候这个受信第三方就是一个 CA。和 CA 的通讯,一般是以预先发行证书的方式进行的。也就是在开始 TLS 通讯的时候,我们需要至少有 2 个证书,一个 CA 的,一个 EMQX 的,EMQX 的证书由 CA 颁发,并用 CA 的证书验证。

获得一个真正受外界信任的证书需要到证书服务提供商进行购买。在实验室环境,我们也可以用自己生成的证书来模拟这个过程。下面我们分别以这两种方式来说明 EMQX 服务器的 SSL/TLS 启用过程。

注意: 购买证书与自签名证书的配置,读者根据自身情况只需选择其中一种进行测试。


购买证书

如果有购买证书的话,就不需要自签名证书。

为方便 EMQX 配置,请将购买的证书文件重命名为 emqx.crt,证书密钥重命名为 emqx.key

自签名证书

在这里,我们假设您的系统已经安装了 OpenSSL。使用 OpenSSL 附带的工具集就可以生成我们需要的证书了。

首先,我们需要一个自签名的 CA 证书。生成这个证书需要有一个私钥为它签名,可以执行以下命令来生成私钥:

openssl genrsa -out ca.key 2048

这个命令将生成一个密钥长度为 2048 的密钥并保存在 ca.key 中。有了这个密钥,就可以用它来生成 EMQX 的根证书了:

openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.pem

查看 CA 证书信息(可选):

openssl x509 -in ca.pem -noout -text

根证书是整个信任链的起点,如果一个证书的每一级签发者向上一直到根证书都是可信的,那个我们就可以认为这个证书也是可信的。有了这个根证书,我们就可以用它来给其他实体签发实体证书了。

实体(在这里指的是 EMQX)也需要一个自己的私钥对来保证它对自己证书的控制权。生成这个密钥的过程和上面类似:

openssl genrsa -out emqx.key 2048

新建 openssl.cnf 文件,

  • req_distinguished_name :根据情况进行修改,
  • alt_names: BROKER_ADDRESS 修改为 EMQX 服务器实际的 IP 或 DNS 地址,例如:IP.1 = 127.0.0.1,或 DNS.1 = broker.xxx.com (如果在内网测试发现连不上,可以本地host `127.0.0.1 borker.test.com` 然后这里就写:IP.1=127.0.0.1 DNS.1=borker.test.com )
[req]
default_bits  = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
countryName = CN
stateOrProvinceName = Zhejiang
localityName = Hangzhou
organizationName = EMQX
commonName = Server certificate
[req_ext]
subjectAltName = @alt_names
[v3_req]
subjectAltName = @alt_names
[alt_names]
IP.1 = BROKER_ADDRESS
DNS.1 = BROKER_ADDRESS

然后以这个密钥和配置签发一个证书请求:

openssl req -new -key ./emqx.key -config openssl.cnf -out emqx.csr

然后以根证书来签发 EMQX 的实体证书:

openssl x509 -req -in ./emqx.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out emqx.pem -days 3650 -sha256 -extensions v3_req -extfile openssl.cnf

查看 EMQX 实体证书(可选):

openssl x509 -in emqx.pem -noout -text

验证 EMQX 实体证书,确定证书是否正确:

$ openssl verify -CAfile ca.pem emqx.pem
emqx.pem: OK

准备好证书后,我们就可以启用 EMQX 的 TLS/SSL 功能了。

SSL/TLS 启用及验证

在 EMQX 中 mqtt:ssl 的默认监听端口为 8883。




MQTT 连接测试


意:自签证书必须要传递ca根证书,同时如果emqx开启客户端证书验证时,如果上传了客户端证书和key就必须要传入对的或者不传.购买证书直接选第一个证书类型.

php workerman/mqtt 接入代码

   public function onWorkerStart(Worker $worker)
    {
        $mqttConfig = config('mqtt.fbox');
        if (empty($mqttConfig['listen'])) {
            return;
        }

        $options = [];
        !empty($mqttConfig['username']) && $options['username'] = $mqttConfig['username'];
        !empty($mqttConfig['password']) && $options['password'] = $mqttConfig['password'];
        if (0 === strpos($mqttConfig['listen'], 'mqtts') || 0 === strpos($mqttConfig['listen'], 'wss') ) {
            if (empty($mqttConfig['ssl_ca'])) {
                echo '[FBOX]', '请申请并配置证书', PHP_EOL;
                return;
            }

            $verifyPeer = false;
            $allowSelfSigned = false;
            if ($mqttConfig['ssl_ca_type'] === 'selfSign') {
                $verifyPeer = true;
                $allowSelfSigned = true;
            }


            $options['ssl'] = [
                'verify_peer' => $verifyPeer, // 如果是自签名证书需要设置为true,因为需要验证根证书,自行购买不用
                'allow_self_signed' => $allowSelfSigned,
                'cafile' => $mqttConfig['ssl_ca']
            ];
            if (!empty($mqttConfig['ssl_key'])) {
                $options['ssl']['local_pk'] = $mqttConfig['ssl_key'];
            }

            if (!empty($mqttConfig['ssl_cert'])) {
                $options['ssl']['local_cert'] = $mqttConfig['ssl_cert'];
            }
        }

        $this->connectMQTT($mqttConfig['listen'], $options);
    }
    
    protected function connectMQTT(string $addr, array $options = [])
    {
        // TODO: Implement connectMQTT() method.
        $this->client = new Client($addr, $options);
        $this->client->onConnect = [$this, 'onMqttConnect'];
//        $this->client->onReconnect =  [$this, 'onMqttReConnect'];
        $this->client->onMessage = [$this, 'onMqttMessage'];
        $this->client->onError = [$this, 'onMqttError'];
        $this->client->connect();
    }

nodejs mqtt 接入代码

const mqtt = require('mqtt')
/* 
  choose which protocol to use for connection here 
*/
// const { connectOptions } = require('./use_mqtt.js')
const { connectOptions } = require('./use_mqtts.js')
// const { connectOptions } = require('./use_ws.js')
// const { connectOptions } = require('./use_wss.js')

/**
 * this demo uses EMQX Public MQTT Broker (https://www.emqx.com/en/mqtt/public-mqtt5-broker), here are the details:
 *
 * Broker host: broker.emqx.io
 * TCP Port: 1883
 * SSL/TLS Port: 8883
 * WebSocket port: 8083
 * WebSocket over TLS/SSL port: 8084
 */

const clientId = 'emqx_nodejs_' + Math.random().toString(16).substring(2, 8)
const options = {
  clientId,
  clean: true,
  connectTimeout: 4000,
  /**
   * By default, EMQX allows clients to connect without authentication.
   * https://docs.emqx.com/en/enterprise/v4.4/advanced/auth.html#anonymous-login
   */
  username: 'emqx_test',
  password: 'emqx_test',
  reconnectPeriod: 1000,
  // Enable the SSL/TLS, whether a client verifies the server's certificate chain and host name
  rejectUnauthorized: true,
  // for more options and details, please refer to https://github.com/mqttjs/MQTT.js#mqttclientstreambuilder-options
}

const { protocol, host, port } = connectOptions
/**
 * if protocol is "mqtt", connectUrl = "mqtt://broker.emqx.io:1883"
 * if protocol is "mqtts", connectUrl = "mqtts://broker.emqx.io:8883"
 * if protocol is "ws", connectUrl = "ws://broker.emqx.io:8083/mqtt"
 * if protocol is "wss", connectUrl = "wss://broker.emqx.io:8084/mqtt"
 *
 * for more details about "mqtt.connect" method & options,
 * please refer to https://github.com/mqttjs/MQTT.js#mqttconnecturl-options
 */
let connectUrl = `${protocol}://${host}:${port}`
if (['ws', 'wss'].includes(protocol)) {
  // mqtt: MQTT-WebSocket uniformly uses /path as the connection path,
  // which should be specified when connecting, and the path used on EMQX is /mqtt.
  connectUrl += '/mqtt'
}

/**
 * If you are using mutual (two-way) TLS authentication, you need to pass the CA, client certificate, and client private key.
 */
if (['mqtts', 'wss'].includes(protocol)) {
  options['ca'] = fs.readFileSync('./path/to/your/ca.crt')
  options['key'] = fs.readFileSync('./path/to/your/client.key')
  options['cert'] = fs.readFileSync('./path/to/your/client.crt')
}

const client = mqtt.connect(connectUrl, options)

const topic = '/nodejs/mqtt'
const payload = 'nodejs mqtt test'
// https://github.com/mqttjs/MQTT.js#qos
const qos = 0

// https://github.com/mqttjs/MQTT.js#event-connect
client.on('connect', () => {
  console.log(`${protocol}: Connected`)

  // subscribe topic
  // https://github.com/mqttjs/MQTT.js#mqttclientsubscribetopictopic-arraytopic-object-options-callback
  client.subscribe(topic, { qos }, (error) => {
    if (error) {
      console.log('subscribe error:', error)
      return
    }
    console.log(`${protocol}: Subscribe to topic '${topic}'`)
    // publish message
    // https://github.com/mqttjs/MQTT.js#mqttclientpublishtopic-message-options-callback
    client.publish(topic, payload, { qos }, (error) => {
      if (error) {
        console.error(error)
      }
    })
  })
})

// https://github.com/mqttjs/MQTT.js#event-reconnect
client.on('reconnect', (error) => {
  console.log(`Reconnecting(${protocol}):`, error)
})

// https://github.com/mqttjs/MQTT.js#event-error
client.on('error', (error) => {
  console.log(`Cannot connect(${protocol}):`, error)
})

// https://github.com/mqttjs/MQTT.js#event-message
client.on('message', (topic, payload) => {
  console.log('Received Message:', topic, payload.toString())
})

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...