EMQX启用双向SSL/TLS安全连接以及java连接
作为基于现代密码学公钥算法的安全协议,TLS/SSL 能在计算机通讯网络上保证传输安全,EMQX 内置对 TLS/SSL 的支持,包括支持单/双向认证、X.509 证书、负载均衡 SSL 等多种安全认证。你可以为 EMQX 支持的所有协议启用 SSL/TLS,也可以将 EMQX 提供的 HTTP API 配置为使用 TLS。
SSL/TLS 带来的安全优势
-
强认证。 用 TLS 建立连接的时候,通讯双方可以互相检查对方的身份。在实践中,很常见的一种身份检查方式是检查对方持有的 X.509 数字证书。这样的数字证书通常是由一个受信机构颁发的,不可伪造。
-
保证机密性。TLS 通讯的每次会话都会由会话密钥加密,会话密钥由通讯双方协商产生。任何第三方都无法知晓通讯内容。即使一次会话的密钥泄露,并不影响其他会话的安全性。
-
完整性。 加密通讯中的数据很难被篡改而不被发现。
SSL/TLS 协议
TLS/SSL 协议下的通讯过程分为两部分,第一部分是握手协议。握手协议的目的是鉴别对方身份并建立一个安全的通讯通道。握手完成之后双方会协商出接下来使用的密码套件和会话密钥;第二部分是 record 协议,record 和其他数据传输协议非常类似,会携带内容类型,版本,长度和荷载等信息,不同的是它所携带的信息是加密了的。
下面的图片描述了 TLS/SSL 握手协议的过程,从客户端的 "hello" 一直到服务器的 "finished" 完成握手。有兴趣的同学可以找更详细的资料看。对这个过程不了解也并不影响我们在 EMQX 中启用这个功能。

为什么需要 SSL/TLS 双向认证
双向认证是指,在进行通信认证时要求服务端和客户端都需要证书,双方都要进行身份认证,以确保通信中涉及的双方都是受信任的。 双方彼此共享其公共证书,然后基于该证书执行验证、确认。一些对安全性要求较高的应用场景,就需要开启双向 SSL/TLS 认证。
SSL/TLS 证书准备
在双向认证中,一般都使用自签名证书的方式来生成服务端和客户端证书,因此本文就以自签名证书为例。
通常来说,我们需要数字证书来保证 TLS 通讯的强认证。数字证书的使用本身是一个三方协议,除了通讯双方,还有一个颁发证书的受信第三方,有时候这个受信第三方就是一个 CA。和 CA 的通讯,一般是以预先发行证书的方式进行的。也就是在开始 TLS 通讯的时候,我们需要至少有 2 个证书,一个 CA 的,一个 EMQX 的,EMQX 的证书由 CA 颁发,并用 CA 的证书验证。
在这里,我们假设您的系统已经安装了 OpenSSL。使用 OpenSSL 附带的工具集就可以生成我们需要的证书了。
生成自签名 CA 证书
首先,我们需要一个自签名的 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
根证书是整个信任链的起点,如果一个证书的每一级签发者向上一直到根证书都是可信的,那个我们就可以认为这个证书也是可信的。有了这个根证书,我们就可以用它来给其他实体签发实体证书了。
生成服务端证书
实体(在这里指的是 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
注意:IP 和 DNS 二者保留其一即可,如果已购买域名,只需保留 DNS 并修改为你所使用的域名地址
[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 = CA
[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
生成客户端证书
双向连接认证还需要创建客户端证书,首先需要创建客户端密钥:
openssl genrsa -out client.key 2048
使用生成的客户端密钥来创建一个客户端请求文件:
openssl req -new -key client.key -out client.csr -subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=EMQX/CN=client"
最后使用先前生成好的服务端 CA 证书来给客户端签名,生成一个客户端证书:
openssl x509 -req -days 3650 -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out client.pem
准备好服务端和客户端证书后,我们就可以在 EMQX 中启用 TLS/SSL 双向认证功能。
SSL/TLS 双向连接的启用及验证
在 EMQX 中 mqtt:ssl 的默认监听端口为 8883。
EMQX 配置
将前文中通过 OpenSSL 工具生成的 emqx.pem、emqx.key 及 ca.pem 文件拷贝到 EMQX 的 etc/certs/ 目录下,并参考如下配置修改 emqx.conf:
## listener.ssl.$name is the IP address and port that the MQTT/SSL
## Value: IP:Port | Port
listener.ssl.external = 8883## Path to the file containing the user's private PEM-encoded key.
## Value: File
listener.ssl.external.keyfile = etc/certs/emqx.key## 注意:如果 emqx.pem 是证书链,请确保第一个证书是服务器的证书,而不是 CA 证书。
## Path to a file containing the user certificate.
## Value: File
listener.ssl.external.certfile = etc/certs/emqx.pem## 注意:ca.pem 用于保存服务器的中间 CA 证书和根 CA 证书。可以附加其他受信任的 CA,用来进行客户端证书验证。
## Path to the file containing PEM-encoded CA certificates. The CA certificates
## Value: File
listener.ssl.external.cacertfile = etc/certs/ca.pem## A server only does x509-path validation in mode verify_peer,
## as it then sends a certificate request to the client (this
## message is not sent if the verify option is verify_none).
##
## Value: verify_peer | verify_none
listener.ssl.external.verify = verify_peer## Used together with {verify, verify_peer} by an SSL server. If set to true,
## the server fails if the client does not have a certificate to send, that is,
## sends an empty certificate.
##
## Value: true | false
listener.ssl.external.fail_if_no_peer_cert = true
创建 MQTT 连接
添加以下依赖到项目 pom.xml 文件中。
<dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse.paho.client.mqttv3</artifactId><version>1.2.5</version></dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on -->
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcpkix-jdk15on</artifactId><version>1.70</version>
</dependency>
然后使用如下代码创建 SSLUtils.java 文件。
import cn.hutool.core.io.FileUtil;
import com.alibaba.cloud.commons.lang.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.springframework.core.io.ClassPathResource;import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileReader;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;/*** @author chenzhongchao* @program hierway-guacamole* @description* @packagename com.hierway.modbus.client* @date 2023-08-25 16:51**/
public class SSLUtils {public static SSLSocketFactory getSocketFactory( String caCrtFile,String crtFile, String keyFile, String password)throws Exception {Security.addProvider(new BouncyCastleProvider());if (StringUtils.isEmpty(password)){password = "";}// load CA certificateX509Certificate caCert = null;String path = System.getProperty("user.dir");if (!FileUtil.exist(caCrtFile)){if (!FileUtil.exist(path+File.separator+caCrtFile)){ClassPathResource classPathResource = new ClassPathResource(caCrtFile);caCrtFile = classPathResource.getPath();}else{caCrtFile = path+File.separator+caCrtFile;}}BufferedInputStream bis = FileUtil.getInputStream(caCrtFile);CertificateFactory cf = CertificateFactory.getInstance("X.509");while (bis.available() > 0) {caCert = (X509Certificate) cf.generateCertificate(bis);}// load client certificateif (!FileUtil.exist(crtFile)){if (!FileUtil.exist(path+File.separator+crtFile)){ClassPathResource classPathResource = new ClassPathResource(crtFile);crtFile = classPathResource.getPath();}else{crtFile = path+File.separator+crtFile;}}bis = FileUtil.getInputStream(crtFile);X509Certificate cert = null;while (bis.available() > 0) {cert = (X509Certificate) cf.generateCertificate(bis);}// load client private keyif (!FileUtil.exist(keyFile)){if (!FileUtil.exist(path+File.separator+keyFile)){ClassPathResource classPathResource = new ClassPathResource(keyFile);keyFile = classPathResource.getPath();}else{keyFile = path+File.separator+keyFile;}}PEMParser pemParser = new PEMParser(new FileReader(FileUtil.file(keyFile)));Object object = pemParser.readObject();JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");KeyPair key = converter.getKeyPair((PEMKeyPair) object);pemParser.close();// CA certificate is used to authenticate serverKeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());caKs.load(null, null);caKs.setCertificateEntry("ca-certificate", caCert);TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");tmf.init(caKs);// client key and certificates are sent to server so it can authenticateKeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());ks.load(null, null);ks.setCertificateEntry("certificate", cert);ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(),new java.security.cert.Certificate[]{cert});KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());kmf.init(ks, password.toCharArray());// finally, create SSL socket factorySSLContext context = SSLContext.getInstance("TLSv1.2");context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);return context.getSocketFactory();}}
MemoryPersistence persistence = new MemoryPersistence();try {//当未设置标识时随机一个if (StringUtils.isEmpty(clientId)) {clientId = UUID.randomUUID().toString().replace("-", "");}mqttAsyncClient = new MqttAsyncClient(serverURI, clientId, persistence);// MQTT 连接选项MqttConnectOptions connOpts = new MqttConnectOptions();//设置MQTT连接账号密码connOpts.setUserName(userName);if (!StringUtils.isEmpty(password)){connOpts.setPassword(password.toCharArray());}connOpts.setCleanSession(true);if (serverURI.indexOf("ssl")>=0){SSLSocketFactory socketFactory = SSLUtils.getSocketFactory(cacert, clientCert, clientKey, clientPassword);connOpts.setSocketFactory(socketFactory);}connOpts.setAutomaticReconnect(true);// 设置心跳秒数connOpts.setKeepAliveInterval(60);// 设置回调mqttReconnectCallback =new MqttReconnectCallback();mqttAsyncClient.setCallback(mqttReconnectCallback);mqttAsyncClient.connect(connOpts);} catch (Exception e) {}
相关文章:
EMQX启用双向SSL/TLS安全连接以及java连接
作为基于现代密码学公钥算法的安全协议,TLS/SSL 能在计算机通讯网络上保证传输安全,EMQX 内置对 TLS/SSL 的支持,包括支持单/双向认证、X.509 证书、负载均衡 SSL 等多种安全认证。你可以为 EMQX 支持的所有协议启用 SSL/TLS,也可…...
4399面试总结C/C++游戏开发
主要流程 首先询问了C/C知识点 然后询问操作系统,计算机组成,数据结构,计算机网络哪两门熟悉 涉及的相关问题 多态的概念 tcp,udp? tcp,udp区别 tcp可靠,udp不可靠 tcp这个链接的过程? 一个TCP连接必须要经过三次“…...
hashlib 模块学习
hashlib 是 Python 标准库中用于散列和摘要算法的模块。散列算法将输入数据转换为固定长度的散列值(也称为摘要),并且对于相同的输入始终生成相同的散列值。这对于存储密码、数字签名、数据完整性验证等领域非常有用。以下是对 hashlib 模块的…...
大模型开发05:PDF 翻译工具开发实战
大模型开发实战05:PDF 翻译工具开发实战 PDF-Translator 机器翻译是最广泛和基础的 NLP 任务 PDF-Translator PDF 翻译器是一个使用 AI 大模型技术将英文 PDF 书籍翻译成中文的工具。这个工具使用了大型语言模型 (LLMs),如 ChatGLM 和 OpenAI 的 GPT-3 以及 GPT-3.5 Turbo 来…...
LeetCode 43题:字符串相乘
题目 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。 注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 "2", num2 "3&…...
基于java Swing 和 mysql实现的飞机订票系统(源码+数据库+ppt+ER图+流程图+架构说明+论文+运行视频指导)
一、项目简介 本项目是一套基于java Swing 和 mysql实现的飞机订票系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含:项目源码、项目文档、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过…...
Jmeter性能综合实战 —— 签到及批量签到
提取性能测试的三个方面:核心、高频、基础功能 签 到 请 求 步 骤 1、准备工作: 签到线程组n HTTP请求默认值n HTTP cookie 管理器n 首页访问请求n 登录请求n 查看结果树n 调试取样器l HTTP代理服务器 (1)创建线程组 …...
燃气管网监测系统,提升城市燃气安全防控能力
燃气是我们日常生活中不可或缺的能源,但其具有易燃易爆特性,燃气安全使用、泄漏监测尤为重要。当前全国燃气安全事故仍呈现多发频发态势,从公共安全的视角来看,燃气已成为城市安全的重大隐忧!因此,建立一个…...
【SQL】1731. 每位经理的下属员工数量 ( 新思想:确定左表,依次添加后续字段)
leetcode题目链接 注意点 确定左表(即,确定result表中的主键),依次添加后续字段。注意:主键可能是一个字段,也可能是多个字段COUNT(DISTINCT()),一般为了防止重复,使用COUNT计数时,…...
AMD Radeon RX 7000/6000系列显卡安装ROCm 调用CUDA
A卡用户画图炼丹、跑大模型甚至是跑机器学习、深度学习的有福了~ 注意:ROCm目前仅限在Linux系统下可用,Windows暂不支持 1. 安装ROCm RX6000系列及以下显卡使用ROCm 5.4.2稳定版本命令 【支持包括桌面级AMD Radeon RX6950XT、RX6900XT、RX6800XT、RX6…...
钉钉小程序引用阿里巴巴图标
2.打开的界面如图,先建一个iconfont.acss文件,全选浏览器打开的样式代码,复制粘贴进新建的iconfont.acss文件中 3.使用...
深入了解Nginx:高性能的开源Web服务器与反向代理
一、Nginx是什么 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,也可以作为负载均衡器和HTTP缓存服务器使用。它采用事件驱动、异步非阻塞的处理方式,能够处理大量并发连接和高流量负载ÿ…...
vue3 自定义显示内容
vue3 自定义显示内容 vue3 自定义显示内容示例uni-app封装自定义内容组件 vue3 自定义显示内容 在 Vue 3 中,你可以通过插槽(Slot)来自定义组件的显示内容。 插槽允许你将额外的内容插入到组件的特定位置,从而实现更灵活的组件…...
视频行为分析——视频图像转换与ffmpeg相关操作
工具类说明 1. 图像视频转换 1.1 视频输出gif from moviepy.editor import VideoFileClip # 设置输入视频文件路径和输出GIF文件路径 input_video video.avi output_gif output.gif # 读取视频文件 video VideoFileClip(input_video) # 将视频保存为GIF文件 video.write_…...
Bean 生命周期
Bean 生命周期 一、Bean 实例化的基本流程 Spring容器在进行初始化时,会将xml配置的的信息封装成一个BeanDefifinition对象,所有的BeanDefifinition存储到一个名为beanDefifinitionMap的Map集合中去,Spring框架在对该Map进行遍历࿰…...
JavaScript原型链污染
前言 在浏览某个论坛的时候,第一次看到了JavaScript原型链污染漏洞。当时非常的好奇,当时我一直以为js作为一种前端语言,就算存在漏洞也是针对前端,不会危害到后端,因此我以为这种漏洞危害应该不大。可当我看到他的漏…...
【Java】设计模式之单例模式与工厂模式
1、设计模式概念及分类 简单来说设计模式是被广大程序员们总结并认可的编码套路,其中最常用的莫过于单例模式与工厂模式,而单例模式也有更加细的分类,一起来学习一下这些模式的用法和特点吧。 2、单例模式 一个类只能被实例化出来一个对象…...
web自动化框架:selenium学习使用操作大全(Python版)
目录 一、浏览器驱动下载二、selenium-python安装(打开网站、操作元素)三、网页解析(HTML、xpath)四、selenium基本操作1、元素定位八种方法2、元素动态定位3、iframe切换4、填充表单_填充文本框5、填充表单_单选按钮6、填充表单_…...
boringssl EVP_aes_128_ecb实现
最近学习boringssl,发现没找到EVP_aes_128_ecb在哪里实现的 饶了一大圈,发现它的定义很无语 #define EVP_CIPHER_FUNCTION(keybits, mode) \const EVP_CIPHER *EVP_aes_##keybits##_##mode(void) { \return aes_##keybits##_##mode##_gene…...
vxe-table中树形结构
如图,同事让帮忙实现一个需求 从二级树节点开始,同时选中的只能有一个二级树节点,选中的二级树节点之下的子节点都可以被选中。否则不能被选中 直接上代码 需要注意的是,文中树状图传递的数据是打平的数据,设置代码是…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
