说在前面
数据在网络上传输,如果是明文传输,肯定是不安全的,所以得将数据进行加密。现在主流的加密方式,就是利用SSL/TLS协议加密,其实SSL和TLS可以看做是一个协议,它运行在传输层和应用层之间的一层协议,通过将TCP/UDP传输的数据加密之后,再传送到另一端。这样数据就安全了。
现在的https传输,底层就是利用了SSL/TLS协议进行了加密。当然,Mail,FTP等其他基于TCP/UDP传输协议之上构建的应用程序,也都是可以使用这种加密协议的。那么MQTT在数据传输中,是否也可以进行传输加密呢?答案是肯定的,因为MQTT也是基于TCP协议之上实现的,所以想要实现起来,并不难。
在开始进行MQTT数据传输加密的实操之前,我们先来储备点SSL/TLS协议的知识,以便于更好的进行理解。
说道SSL/TLS协议,其诞生可以追溯到前浏览器霸主NetScape,为了解决数据传输安全问题而发明了SSL协议,先是产生了SSL1.0,然后产生了SSL2.0,之后是SSL3.0,由于协议成熟且使用量大,后来被IETF对SSL3.0进行了标准化并添加了少量其他机制,更名成了TLS1.0。更多的细节,可以百度“RFC2246-TLS加密协议详解”,也可以去www.rfc-editor.org网站,通过搜索RFC2246来了解更多细节。
OPENSSL,是对SSL/TLS协议进行实现的开源软件,其中包含了加解密所用到的各种算法,算是密码学这块的集大成者。
今天我们要用它来通过特定的流程来生产出我们需要的加密机制,从而使得MQTT的传输更安全。
握手流程
首先来说明一下SSL/TLS协议的经典握手流程,只有通过此流程,后续的数据传输才能正常的进行加密并传输。整体流程如下:
由于上图描述都是英文,理解起来比较困难,我就拿阮一峰的描述来进行,Vistor我们称为Alice,CloudFlare我们称为Bob,描述如下:
第一步,爱丽丝给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。
第二步,鲍勃确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。
第三步,爱丽丝确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给鲍勃。
第四步,鲍勃使用自己的私钥,获取爱丽丝发来的随机数(即Premaster secret)。
第五步,爱丽丝和鲍勃根据约定的加密方法,使用前面的三个随机数,生成"对话密钥"(session key),用来加密接下来的整个对话过程。
可以看到整体流程比较复杂,而且涉及到了加密方法,数字证书,公钥,私钥等名词,我大概做一下解释。
加密方法:数据传输加密,可以用多种方法,图例中选用的是RSA,即非对称加密算法。
公钥:公开的密匙文件,任何用户都可以自由访问,通常以.pem结尾
私钥:非公开的密匙文件,通常由加密方自己保管,必须得保证其私密性,通常以.key结尾
数字证书:包含数字签名和公钥的证书,客户端可以通过CA来验证数字签名,通常以.cer, .crt结尾
大致知道这些名词的意思后,接下来我们通过更加具体的流程讲解,来逐渐清晰化。
授权流程
CA中心的授权流程
由于CA证书要进行证书授权,所以必须先创建一个自己的私钥,然后通过此私钥生成自己的自签证书。由于这里我们无法真正的CA中心来模拟,因为是收费的,好在openssl为我们提供了自建CA中心的选项,使得可以使用如下的命令来模拟。
1.创建私钥,执行命令:
openssl genrsa -out D:/sslkey/ca/MyRootCA.key 2048
可以看到在文件夹中生成了私钥
2.生成自签证书,执行命令:
openssl req -x509 -new -nodes -key D:/sslkey/ca/MyRootCA.key -sha256 -days 3650 -subj "/CN=www.scy.com" -out D:/sslkey/ca/MyRootCA.pem
填写好相关信息后,最终在文件夹中生成了自签证书。需要说明的是,由于是模拟的CA中心,所以这里稍显繁琐,如果使用的是外部CA中心,则无需关心这些问题。同时,如果用户想自定义自己的信息,请把-subj配置去掉即可,后面的请求也类似。
CA证书整体的准备工作到此。
Server端的授权流程
Server端的授权流程也比较简单,首先是创建server端使用的私钥,然后通过此私钥创建证书请求,请求会被推送到CA中心进行处理,处理完毕后,会将证书颁发给Server端。
1.创建私钥,执行命令:
openssl genrsa -out D:/sslkey/server/MyEMQ1.key 2048
2.创建证书请求,执行命令:
openssl req -new -key D:/sslkey/server/MyEMQ1.key -out D:/sslkey/server/MyEMQ1.csr -subj "/CN=127.0.0.1"
3.CA中心颁发证书,执行命令:
openssl x509 -req -in D:/sslkey/server/MyEMQ1.csr -CA D:/sslkey/ca/MyRootCA.pem -CAkey D:/sslkey/ca/MyRootCA.key -CAcreateserial -out D:/sslkey/server/MyEMQ1.pem -days 3650 -sha256
执行这三步完毕之后,整体流程完毕,我们接下来将这些文件配置到EMQ的配置文件中来启动SSL。
打开etc目录中的emqx.conf文件,修改选项为以下三个配置(需要提前将授权文件拷贝到certs目录):
#ssl port:
listener.ssl.external = 8883
#private key for emq cert:
listener.ssl.external.keyfile = etc/certs/MyEMQ1.key
#emq cert:
listener.ssl.external.certfile = etc/certs/MyEMQ1.pem
#CA cert:
listener.ssl.external.cacertfile = etc/certs/MyRootCA.pem
之后,重启EMQX服务器即可。
这样,此根证书生成完毕,然后就能为后续进来的请求进行授权操作。
接下来打开MQTTX客户端程序,建立SSL链接来试试是否可以正常操作:
配置好之后,点击连接按钮,进行连接,可以看到已经连接到服务器且在在ssl端口8883进行监听了。
然后尝试向/test/say管道发送消息,可以看到订阅者已经能接收到了:
发布者界面如下:
订阅者界面如下:
双向认证流程
上面配置好Server端的授权流程后,我们成功的使用客户端通过SSL连接上了服务端并进行了消息操控。但是上面只是单向认证流程,因为Client端并未做认证操作。这里我们将来说下怎么进行双向认证流程。
Server端的授权流程,我就不用说了,上面有讲解到。按照流程操作即可。
Client端的授权流程,其实和Server端的授权流程一模一样,我们来依葫芦画瓢一下,进入C:\Program Files\OpenSSL-Win64\bin目录,执行下面的命令。
1.创建私钥,执行命令:
openssl genrsa -out D:/sslkey/client/MyClient1.key 2048
2.创建证书请求,执行命令:
openssl req -new -key D:/sslkey/client/MyClient1.key -out D:/sslkey/client/MyClient1.csr -subj "/CN=127.0.0.1"
3.CA中心颁发证书,执行命令:
openssl x509 -req -in D:/sslkey/client/MyClient1.csr -CA D:/sslkey/ca/MyRootCA.pem -CAkey D:/sslkey/ca/MyRootCA.key -CAcreateserial -out D:/sslkey/client/MyClient1.pem -days 3650 -sha256
全部执行完毕,则Client端的授权流程完毕,结果如下:
文件生成如下:
之后,我们需要配置一下,以便于启动EMQ的客户端SSL认证,同样的打开etc目录中的emqx.conf文件,修改如下配置:
#enable the client side certificates
listener.ssl.external.verify = verify_peer
#set it to 'true' to allow the ssl with client side certificate only
listener.ssl.external.fail_if_no_peer_cert = true
然后重新启动EMQX即可。
上图为配置方式,配置完毕之后,点击连接按钮,就可以连接到Server了。之后模拟信息收发:
题外话
单向认证和双向认证,少了一个认证步骤,一般在物联网Client设备较多的情况下,使用单向认证则可以获得更好的性能。如果数据非常重要且不会有性能问题的时候,使用双向设备则更合适。
考虑到CA颁发的证书都有时效性,物联网设备特别多的情况下,这种情况显得尤为明显,所以针对自己的设备规模,选择合适的认证,也是非常重要的。
本节就到这里,希望能抛砖引玉。
参考资料:
Securing EMQ Connections with SSL
图解ssl