作为基于现代密码学公钥算法的安全协议,TLS/SSL能在计算机通讯网络上保证传输安全,EMQ的MQTT broker支持TLS,也可以用这种方式来确保传输安全。
参考官网:https://www.emqx.io/cn/blog/emqx-server-ssl-tls-secure-connection-configuration-guide
接下来EMQ君将介绍TLS/SSL协议以及如何在EMQ上启用。
TLS/SSL协议下的通讯过程分为两部分,第一部分是握手协议。握手协议的目的是鉴别对方身份并建立一个安全的通讯通道。握手完成之后双方会协商出接下来使用的密码套件和会话密钥;第二部分是record协议,redord和其他数据传输协议非常类似,会携带内容类型,版本,长度和荷载等信息,不同的是它所携带的信息是加密了的。
下面的图片描述了TLS/SSL握手协议的过程,从客户端的“hello”一直到服务器的“finished”完成握手。有兴趣的同学可以放狗找更详细的资料看。对这个过程不了解也并不影响我们在EMQ中启用这个功能。
通常来说,我们会需要数字证书来保证TLS通讯的强认证。数字证书的试用本身是一个三方协议,除了通讯双方,还有一个颁发证书的受信第三方,有时候这个受信第三方就是一个CA。和CA的通讯,一般是以预先发行证书的方式进行的。也就是在开始TLS通讯的时候,我们需要至少有2个证书,一个CA的,一个EMQ的,EMQ的证书由CA颁发,并用CA的证书验证。如果要做双向认证,还会用到一个客户端证书。
获得一个真正受外界信任的证书会有一定的开销。在实验室环境,我们可以用自己生成的证书来模拟这个过程。接下来的演示,EMQ君就是用的这个方法。
在这里,我们假设您的系统已经安装了OpenSSL。试用OpenSSL附带的工具集就可以生成我们需要的证书了。
首先,我们需要一个自签名的CA证书。生成这个证书需要有一个私钥为它签名,如果还没有合适的私钥的话,可以在命令行上执行以下命令来生成一个:
openssl genrsa -out MyRootCA.key 2048
这个命令将生成一个密钥长度2048的密钥并保存在MyRootCA.key,
有了这个密钥,就可以用它来生成EMQ broker的根证书了,请求中需要填写一些信息,尽量和下面新建的openssl.cnf
文件中的信息一样:
openssl req -x509 -new -nodes -key MyRootCA.key -sha256 -days 3650 -out MyRootCA.pem
根证书是整个信任链的起点,如果一个证书的每一级签发者向上一直到根证书都是可信的,那个我们就可以认为这个证书也是可信的。有了这个根证书,我们就可以用它来给其他实体签发实体证书了。
实体(在这里就是EMQ Broker)也需要一个自己的私钥对来保证它对自己证书的控制权。生成这个密钥的过程和上面类似:
openssl genrsa -out MyEMQ.key 2048
新建 openssl.cnf
文件,
BROKER_ADDRESS
修改为 EMQ X 服务器实际的 IP 或 DNS 地址,例如:IP.1 = 127.0.0.1,或 DNS.1 = broker.xxx.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 = Jiangsu
localityName = Suzhou
organizationName = test.io
commonName = www.test.com
[req_ext]
subjectAltName = @alt_names
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.test.com
然后以这个密钥签发一个证书请求,请求中需要填写一些信息,尽量和上面新建的openssl.cnf
文件中的信息一样:
openssl req -new -key ./MyEMQ
.key -config openssl.cnf -out MyEMQ
.csr
然后以根证书来签发EMQ Broker的实体证书了:
openssl x509 -req -in ./MyEMQ
.csr -CA MyRootCA
.pem -CAkey MyRootCA
.key -CAcreateserial -out MyEMQ
.pem -days 3650 -sha256 -extensions v3_req -extfile openssl.cnf
现在,我们可以启用EMQ的TLS/SSL连接了
启用TLS/SSL的过程是比较简单的,只需要修改emq.conf
文件中的一些配置就可以了:
listener.ssl.external = 8883
#private key for emq cert:
listener.ssl.external.keyfile = etc/certs/MyEMQ.key
#emq cert:
listener.ssl.external.certfile = etc/certs/MyEMQ.pem
#CA cert:
listener.ssl.external.cacertfile = etc/certs/MyRootCA.pem
重启EMQ后,用mosquittu_sub
来验证TLS是否正常启动:
然后以根证书来签发EMQ Broker的实体证书了:
mosquitto_sub -t abc -h 192.168.238.132 -p 8883 -d --cafile ~/test_certs/MyRootCA.pem --insecure
Client mosqsub|10617-Zhengyus- sending CONNECT
Client mosqsub|10617-Zhengyus- received CONNACK
Client mosqsub|10617-Zhengyus- sending SUBSCRIBE (Mid: 1, Topic: abc, QoS: 0)
Client mosqsub|10617-Zhengyus- received SUBACK
Subscribed (mid: 1): 0
这个结果和我们期待的一样。moqsuitto客户端通过TLS/ssl正常连接并订阅主题。
如果我们从EMQ的Dashboard上看的话,可以看到在8883端口上有一个mqtt:ssl连接:
在一些对于身份验证要求比较严格的场景,也会要求EMQ对客户端的身份用证书进行验证。这时候就需要启动双向验证了。
在emq.conf
中启动对对端的证书验证:
#enable the client side certificates
listener.ssl.external.verify = verify_peer
在emq.conf
中强制对对端的证书验证:
#set it to 'true' to allow the ssl with client side certificate only
listener.ssl.external.fail_if_no_peer_cert = true
完成上面的步骤后,如果我们再想用之前的方式连接EMQ,客户端就会被拒绝:
mosquitto_sub -t abc -h emq1 -p 8883 -d --cafile ~/test_certs/MyRootCA.pem --insecure
Client mosqsub|10738-Zhengyus- sending CONNECT
Error: A TLS error occurred.
现在我们试用和生成服务器端证书一样的方法来生成客户端证书:
openssl genrsa -out MyClient.key 2048
openssl req -new -key ./MyClient
.key -config openssl.cnf -out MyClient
.csr
openssl x509 -req -in ./MyClient
.csr -CA MyRootCA
.pem -CAkey MyRootCA
.key -CAcreateserial -out MyClient
.pem -days 3650 -sha256 -extensions v3_req -extfile openssl.cnf
修改mosquitto_sub
的命令行启动客户端证书验证:
mosquitto_sub -t abc -h 192.168.238.132 -p 8883 -d --key ~/test_certs/MyClient.key --cert ~/test_certs/MyClient.pem --cafile ~/test_certs/MyRootCA.pem --insecure
Client mosqsub|10796-Zhengyus- sending CONNECT
Client mosqsub|10796-Zhengyus- received CONNACK
Client mosqsub|10796-Zhengyus- sending SUBSCRIBE (Mid: 1, Topic: abc, QoS: 0)
Client mosqsub|10796-Zhengyus- received SUBACK
Subscribed (mid: 1): 0
也可以尝试python客户端:
import paho.mqtt.client as mqttimport json, ossub_list = [("abc", 0), ('pub', 2)]def on_connect(client, userdata, flags, rc): print("Connected with result code " + str(rc)) # 连接成功回调 result, mid = client.subscribe(sub_list) # print('result:', result) # print('mid:', mid)# 接收到消息的回调方法def on_message(client, userdata, msg): payload = msg.payload.decode() print(msg.topic) print(payload) # print(msg.topic + ":" + payload)if __name__ == '__main__': client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message # client.username_pw_set("username", "pwd") crtPath = os.path.dirname(os.path.abspath(__file__)) + r"\crt" ca_certs = "%s\ca\MyRootCA.pem" % crtPath certfile = "%s\client\MyClient.pem" % crtPath keyfile = "%s\client\MyClient.key" % crtPath print(ca_certs) client.tls_set(ca_certs=ca_certs, certfile=certfile, keyfile=keyfile, ) client.tls_insecure_set(True) HOST = "192.168.238.132" client.connect(HOST, 8883, 30) # client.loop_forever() client.loop_start() while True: str = input() if str: client.publish("abc", str, 0)
手机扫一扫
移动阅读更方便
你可能感兴趣的文章