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消息流上的差异。