MQTT研究之EMQ:【SSL双向验证】

EMQ是当前MQTT中,用于物联网领域中比较出色的一个broker,今天我这里要记录和分享的是关于SSL安全通信的配置和注意细节。

环境:

1. 单台Linux CentOS7.2系统,安装一个EMQTTD的实例broker。

2. emq的版本2.3.11。

3. 客户端分为mosquitto_pub,以及MQTT.fx 1.7.1的subscriber。

4. 证书是通过openssl(version:1.0.2k-fips)生成的,rootCA是自签名的,subscriber和publisher的证书是通过rootCA签署的。

1、CA根证书的生成

[root@ws3 certs]# openssl req -x509 -new -days 3650 -keyout ca.key -out rootCA.crt -nodes
Generating a 2048 bit RSA private key
.....................+++
.........................................................................................................................+++
writing new private key to 'ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN  
State or Province Name (full name) []:Hubei
Locality Name (eg, city) [Default City]:Wuhan
Organization Name (eg, company) [Default Company Ltd]:abcdefg
Organizational Unit Name (eg, section) []:Tkcloud
Common Name (eg, your name or your server's hostname) []:10.95.197.3
Email Address []:

2、为server端生成证书

a. 生成私有秘钥

[root@ws3 certs]# openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus
...................................................+++
............+++
e is 65537 (0x10001)

b. 生成证书请求csr文件

[root@ws3 certs]# openssl req -new -key server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Hubei
Locality Name (eg, city) [Default City]:Wuhan
Organization Name (eg, company) [Default Company Ltd]:abcdefg
Organizational Unit Name (eg, section) []:Cloud
Common Name (eg, your name or your server's hostname) []:10.95.197.3
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

c. 生成证书

[root@ws3 certs]# openssl ca -in server.csr -out server.crt -cert rootCA.crt -keyfile ca.key -days 3650
Using configuration from /etc/pki/tls/openssl.cnf
/etc/pki/CA/index.txt: No such file or directory
unable to open '/etc/pki/CA/index.txt'
140188069304208:error:02001002:system library:fopen:No such file or directory:bss_file.c:402:fopen('/etc/pki/CA/index.txt','r')
140188069304208:error:20074002:BIO routines:FILE_CTRL:system lib:bss_file.c:404:

错误分析:
缺乏/etc/pki/CA/index.txt,对应的创建一个空文件即可

再次执行证书生成过程:

[root@ws3 certs]# openssl ca -in server.csr -out server.crt -cert rootCA.crt -keyfile ca.key -days 3650           
Using configuration from /etc/pki/tls/openssl.cnf
/etc/pki/CA/serial: No such file or directory
error while loading serial number
140539902367632:error:02001002:system library:fopen:No such file or directory:bss_file.c:402:fopen('/etc/pki/CA/serial','r')
140539902367632:error:20074002:BIO routines:FILE_CTRL:system lib:bss_file.c:404:

错误分析:
缺乏/etc/pki/CA/serial文件,对应的创建这个文件
再次执行上述生成指令:

[root@ws3 certs]# openssl ca -in server.csr -out server.crt -cert rootCA.crt -keyfile ca.key -days 3650
Using configuration from /etc/pki/tls/openssl.cnf
unable to load number from /etc/pki/CA/serial
error while loading serial number
140553210034064:error:0D066096:asn1 encoding routines:a2i_ASN1_INTEGER:short line:f_int.c:210:

错误分析:
缺乏序号数据,是没有序号数据。写入1,保存后,继续执行上述过程

[root@ws3 certs]# openssl ca -in server.csr -out server.crt -cert rootCA.crt -keyfile ca.key -days 3650
Using configuration from /etc/pki/tls/openssl.cnf
unable to load number from /etc/pki/CA/serial
error while loading serial number
139880943351696:error:0D066096:asn1 encoding routines:a2i_ASN1_INTEGER:short line:f_int.c:210:

错误分析:
写的数据格式不对,需要写入两位的01格式,保存后继续运行。

[root@ws3 certs]# openssl ca -in server.csr -out server.crt -cert rootCA.crt -keyfile ca.key -days 3650
Using configuration from /etc/pki/tls/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Jan 17 14:38:42 2019 GMT
            Not After : Jan 14 14:38:42 2029 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = Hubei
            organizationName          = abcdefg
            organizationalUnitName    = Cloud
            commonName                = 10.95.197.3
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                90:F2:F6:57:31:DB:1A:81:B7:11:08:12:46:A0:FE:DB:6F:84:A9:DE
            X509v3 Authority Key Identifier: 
                keyid:DF:2F:DC:D6:75:2A:06:C0:D0:39:6C:32:11:A8:60:72:F4:9B:EB:DD

Certificate is to be certified until Jan 14 14:38:42 2029 GMT (3650 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

CA这个信息为FALSE,表明这个证书不是根证书

上面的rootCA.crt是根证书,看看内容:

[root@ws3 certs]# openssl x509 -text -in rootCA.crt -noout   
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            db:61:ba:05:39:42:54:21
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=CN, ST=Hubei, L=Wuhan, O=abcdefg, OU=Tkcloud, CN=10.95.197.3
        Validity
            Not Before: Jan 17 15:20:50 2019 GMT
            Not After : Dec 24 15:20:50 2118 GMT
        Subject: C=CN, ST=Hubei, L=Wuhan, O=abcdefg, OU=Tkcloud, CN=10.95.197.3
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:95:b4:50:75:5b:c6:23:b4:67:1c:5f:89:25:b8:
                    bf:8a:9b:e1:fa:95:81:81:87:71:2b:6e:16:b9:07:
                    a2:1c:67:76:35:da:fb:5b:25:6b:db:bd:29:cd:55:
                    98:ca:95:3e:2f:bb:c3:b5:61:4d:c9:d6:9c:1b:b9:
                    06:59:24:e8:ed:5a:d9:ca:84:07:4e:7d:e6:42:8f:
                    7e:98:25:48:17:9c:18:d4:ff:26:c4:aa:dc:dd:de:
                    91:78:33:e2:9c:3d:95:56:4d:d1:5a:78:ea:b8:49:
                    38:1e:b8:89:a3:6f:79:ba:b2:97:02:81:5c:8b:0b:
                    d3:45:be:a1:49:e6:64:26:59:cf:86:18:14:3a:31:
                    f0:e1:c8:04:52:1e:cf:fb:4a:ee:5a:a9:7d:bc:63:
                    d2:fe:2c:f5:8f:a4:b2:cc:52:92:d2:9d:a0:d2:2e:
                    4e:4f:e7:77:6c:0d:81:59:42:13:b6:7c:19:45:f6:
                    e9:c6:33:5e:21:ea:01:02:61:2d:53:e2:f2:bf:06:
                    59:63:7e:37:cf:bd:2a:44:63:77:c1:8f:c5:56:9e:
                    35:f8:26:17:08:79:75:c3:05:2f:b4:fc:d2:95:96:
                    cb:0c:dd:6f:ef:9f:5d:57:48:4e:78:2b:75:2e:3a:
                    37:8e:c9:95:f0:7c:92:80:a3:ac:f7:66:9d:16:59:
                    e7:f9
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                AB:2E:65:BE:15:CB:A1:29:A4:09:97:A8:CB:39:2A:3F:ED:4C:1E:16
            X509v3 Authority Key Identifier: 
                keyid:AB:2E:65:BE:15:CB:A1:29:A4:09:97:A8:CB:39:2A:3F:ED:4C:1E:16

            X509v3 Basic Constraints: 
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         4c:35:ad:41:4f:9d:e6:c3:f2:87:08:50:70:4f:81:09:da:1c:
         c4:d7:25:60:0f:1a:d6:2b:e8:8e:9d:45:6e:47:13:ee:b0:c2:
         21:96:2e:e2:eb:2e:c0:a7:60:ba:00:dc:b6:94:45:f9:7d:ff:
         97:dc:dc:92:d3:aa:85:2a:fe:f4:91:c7:0e:f8:2e:de:2d:28:
         a0:a1:f5:74:f1:cc:8f:2f:00:fe:0c:35:6d:de:00:ff:46:8c:
         22:21:53:22:ea:2b:b1:7d:ac:e4:b4:c0:e7:91:95:40:96:da:
         93:48:57:91:88:0a:6d:3d:a5:de:65:9b:be:72:ef:d0:f4:2a:
         a0:db:47:6e:6c:bf:f6:fb:a7:3f:1c:4b:bd:c6:6c:9f:62:3b:
         d3:d3:b3:7f:ce:b8:83:86:20:c1:28:8e:42:c2:60:d1:26:ee:
         33:27:e2:78:a7:0c:26:3a:b9:94:01:c6:11:19:56:77:76:e7:
         ed:06:fc:76:d9:7e:06:f8:a3:15:8a:a2:89:33:b5:e0:0e:9d:
         4d:3a:b6:15:33:40:0d:26:ac:67:92:0b:96:17:13:66:93:c8:
         0d:ea:ed:68:e9:ff:4a:3e:e5:27:53:71:e2:53:82:83:f1:68:
         01:d9:6b:5b:51:bf:84:7f:ad:0d:2f:98:d6:fb:04:a4:e5:78:
         1c:82:94:de

证书生成完毕后,查看上面创建的index.txt以及serial文件:

[root@ws3 certs]# vi /etc/pki/CA/index.txt
V       290114143842Z           01      unknown /C=CN/ST=Hubei/O=abcdefg/OU=Cloud/CN=10.95.197.3
[root@ws3 certs]# vi /etc/pki/CA/serial
02

注意:起初index.txt是空的,证书创建成功后,写入了一条记录。serial文件,起初是01,然而,现在变成了02.

3. 为客户端生成证书

这里,省略生成私钥,以及证书请求的过程,重点关注基于根证书rootCA.crt生成证书的过程。

[root@ws3 certs]# openssl ca -in client.csr -out client.crt -cert rootCA.crt -keyfile ca.key 
Using configuration from /etc/pki/tls/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Jan 17 14:56:37 2019 GMT
            Not After : Jan 17 14:56:37 2020 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = Hubei
            organizationName          = abcdefg
            organizationalUnitName    = cloud
            commonName                = client
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                D7:A0:13:02:40:47:1F:E1:89:71:EA:60:31:57:3A:A5:50:7D:5B:43
            X509v3 Authority Key Identifier: 
                keyid:DF:2F:DC:D6:75:2A:06:C0:D0:39:6C:32:11:A8:60:72:F4:9B:EB:DD

Certificate is to be certified until Jan 17 14:56:37 2020 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

客户证书只有1年(不加参数-days的话,默认是1年),而服务端证书时候10年的有效期

4. 验证双向SSL的通信过程。

1). 配置emqtt

## Path to a file containing the user certificate.
##
## See: http://erlang.org/doc/man/ssl.html
##
## Value: File
#listener.ssl.external.certfile = /etc/emqttd/certs/cert.pem
listener.ssl.external.certfile = /opt/certs/server.crt

## Path to the file containing PEM-encoded CA certificates. The CA certificates
## are used during server authentication and when building the client certificate chain.
##
## Value: File
## listener.ssl.external.cacertfile = /etc/emqttd/certs/cacert.pem
listener.ssl.external.cacertfile = /opt/certs/rootCA.crt

## The Ephemeral Diffie-Helman key exchange is a very effective way of
## ensuring Forward Secrecy by exchanging a set of keys that never hit
## the wire. Since the DH key is effectively signed by the private key,
## it needs to be at least as strong as the private key. In addition,
## the default DH groups that most of the OpenSSL installations have
## are only a handful (since they are distributed with the OpenSSL
## package that has been built for the operating system it’s running on)
## and hence predictable (not to mention, 1024 bits only).
## In order to escape this situation, first we need to generate a fresh,
## strong DH group, store it in a file and then use the option above,
## to force our SSL application to use the new DH group. Fortunately,
## OpenSSL provides us with a tool to do that. Simply run:
## openssl dhparam -out dh-params.pem 2048
##
## Value: File
## listener.ssl.external.dhfile = /etc/emqttd/certs/dh-params.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).
## You can then also want to specify option fail_if_no_peer_cert.
## More information at: http://erlang.org/doc/man/ssl.html
##
## 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

上述几个红色的部分,是涉及SSL通信要用到的。单双向验证的核心参数是listener.ssl.external.verify = verify_peer,这里是双向验证,即服务端要向客户端发起身份验证工作。verify_none表示服务端不对客户端进行身份验证

2). 这里消费者客户端使用的是MQTT.fx客户端工具(1.7.1),跑在windows机器上。

a. 按照下面的config.jpg图片显示内容,配置好相关信息。

emqtt配置了用户身份认证,是通过用户名和密码做的。必须输入上述参数

上图中配置的证书相关的参数对应的文件,就是前面生成的证书相关文件,copy到了MQTT.fx所在的windows机器上了


b. 按照图connection.jpg点击connect即可,如图所示表示连接成功。


c. 按照图片subscribe.jpg所示,进行订阅。

3). 消息生产者跑在Linux机器上,通过mosquitto_pub实现。

[root@ws3 certs]# mosquitto_pub -h 10.95.197.3 -t taikang/rulee --cafile /opt/certs/rootCA.crt --cert /opt/certs/client.crt --key /opt/certs/client.key -u water -P water -m "hellooooo my iot platform"

此时,MQTT.fx客户端上收到了刚才发送的数据,结果如下图.

4). 这里,基于上面listener.ssl.external.verify = verify_peer (broker要验证client的身份)做些方向测试验证,主要验证是不是双向验证逻辑

a. MQTT.fx上,将证书中client.key换成其他的key,和client.crt不配对的,看能否建立连接。效果如图下图. 而且,配置中根证书,客户证书,客户私钥,这三个任何一个配置不匹配,会出现不同的错误信息,自行尝试验证。

b. Mosquitto_pub端,选择将cert的参数选成另外一个证书,和上述的client.key不匹配即可,错误如下:

[root@ws3 certs]# mosquitto_pub -h 10.95.197.3 -t taikang/rulee --cafile /opt/certs/rootCA.crt --cert /etc/emqttd/certs/client-cert.pem --key /opt/certs/client.key -u water -P water -m "hellooooo5" -d                 
Error: Unable to load client key file "/opt/certs/client.key".
OpenSSL Error: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch
Unable to connect (A TLS error occurred.).

从上面a和b两边的测试验证,说明自行生成的客户端和服务端证书,在emqtt配置成双向验证的情况下,工作是符合设定的业务逻辑的。

下一篇博文,将从wireshark抓包的角度分析SSL通信模式下,单向验证和双向验证,在SSL/TLS消息流上的差异。

原文地址:https://www.cnblogs.com/shihuc/p/10285836.html