2021年7月

“欲速则不达,见小利则大事不成。”

如果初始请求通过HTTPS是安全的,但加载了HTTPS 和 HTTP内容以显示网页,则混合内容会发生。 HTTPS内容是安全的。 HTTP内容不安全。

如果安全内容与不安全内容混合在一起,现代浏览器可能会阻止页面显示或显示警告消息。

在chrome中就会提示如标题所示的错误消息:
This request has been blocked; the content must be served over HTTPS.

所以,问题就变成了,如何在chrome浏览器中启用混合内容。

单击锁(警告)图标,然后单击​站点设置。

站点设置>>不安全的内容

滚动到​Insecure content(不安全内容),然后使用下拉列表将“Block(default)”更改为“Allow(允许)”。
chorme-insecure-content-settings.png

重新刷新网页就可以了。

firefox浏览器的话,参考这个链接:在浏览器中启用混合内容

安装软件

sudo apt install mosquitto

生成CA密钥

openssl genrsa -des3 -out ca.key 2048

签发CA证书/根证书

openssl req -new -x509 -days 3650 -key ca.key -out ca.crt

生成服务端密钥

openssl genrsa -out server.key 2048

生成证书签名请求

openssl req -new -out server.csr -key server.key

让CA签发证书

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 360

修改mosquitto.conf内容
listener 8883
cafile /etc/mosquitto/ca_certificates/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
tls_version tlsv1.2

mqtt broker运行测试

mosquitto -c etc/mosquitto.conf 

1626408211: mosquitto version 2.0.11 starting
1626408211: Config loaded from etc/mosquitto.conf.
1626408211: Opening ipv4 listen socket on port 8883.

发布消息

mosquitto_pub -d -h localhost -t "topic/test" -m "hello world" --cafile /etc/mosquitto/ca_certificates/ca.crt -p 8883

订阅消息

mosquitto_sub -d -h localhost -t "topic/test" --cafile /etc/mosquitto/ca_certificates/ca.crt -p 8883

双向认证测试(需要根证书,客户证书,服务器证书以及各自的私钥)

可以使用test.mosquitto.org服务器来测试

测试命令:

openssl s_client -connect test.mosquitto.org:8884 -key test/mosquitto-client.key -cert test/mosquitto-client.crt -CAfile mosquitto.org.crt -showcerts

其中mosquitto-client.key 自己生成,mosquitto-client.crt在mosquitto的网站上签发. 可以参考这篇文章https://const.net.cn/151.html

运行结果:
Acceptable client certificate CA names
C = GB, ST = United Kingdom, L = Derby, O = Mosquitto, OU = CA, CN = mosquitto.org, emailAddress = roger@atchoo.org
Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:RSA+SHA224
Shared Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
...
SSL handshake has read 2741 bytes and written 2690 bytes
Verification: OK
...
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_256_GCM_SHA384
Session-ID: 904A1381D17A1C4432A0DE5EE0D7564C1E4AD4A3E46B7EE173E61B1034A95EB1
Session-ID-ctx:
Resumption PSK: AC27C990D68B5EE27235A5A659EA25CD2D4DE28D233175DFD7ECD09A6FFB4BE418B8C4F1FB87DDF6E666E82EC2A0BC0A
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 7200 (seconds)
TLS session ticket:
0000 - eb 13 3c cb 1b 08 5a 65-79 37 75 92 30 fd 47 46 ..<...Zey7u.0.GF
0010 - e8 09 6a 31 83 91 16 f8-29 9c a8 cc d3 e6 00 ad ..j1....).......

Start Time: 1626417683
Timeout : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no
Max Early Data: 0
使用自签名证书,自己搭建服务器的话

openssl s_server -accept 8888 -key server.key -cert server.crt -CAfile ca.crt -Verify 1

verify depth is 1, must return a certificate
Using default temp DH parameters

openssl s_client -connect localhost:8883 -key client.key -cert client.crt -CAfile ca.crt

...
Start Time: 1626418308
Timeout : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no
Max Early Data: 0
...

mosquitto 使用-q来设置qos参数

-q : quality of service level to use for the subscription. Defaults to 0.
--will-qos : QoS level for the client Will.

注意事项:
If a message is published with QoS 2 and the client is subscribed to a topic with QoS 0, then the message will be delivered to that client with QoS 0;

if a subscriber is registered with QoS 2 and the message is published with QoS 0, the message will be delivered with QoS 0.

MQTT设计了一套保证消息稳定传输的机制,包括消息应答、存储和重传。在这套机制下,提供了三种不同层次QoS(Quality of Service):

QoS0,At most once,至多一次;
QoS1,At least once,至少一次;
QoS2,Exactly once,确保只有一次。
QoS 是消息的发送方(Sender)和接受方(Receiver)之间达成的一个协议:

QoS0 代表,Sender 发送的一条消息,Receiver 最多能收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,也就算了;
QoS1 代表,Sender 发送的一条消息,Receiver 至少能收到一次,也就是说 Sender 向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,但是因为重传的原因,Receiver 有可能会收到重复的消息;
QoS2 代表,Sender 发送的一条消息,Receiver 确保能收到而且只收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,同时保证 Receiver 不会因为消息重传而收到重复的消息。
注意:
QoS是Sender和Receiver之间的协议,而不是Publisher和Subscriber之间的协议。换句话说,Publisher发布了一条QoS1的消息,只能保证Broker能至少收到一次这个消息;而对于Subscriber能否至少收到一次这个消息,还要取决于Subscriber在Subscibe的时候和Broker协商的QoS等级。

MQTT QoS 等级的选择
QoS 级别越高,流程越复杂,系统资源消耗越大。应用程序可以根据自己的网络场景和业务需求,选择合适的 QoS 级别。

以下情况下可以选择 QoS 0
可以接受消息偶尔丢失。
在同一个子网内部的服务间的消息交互,或其他客户端与服务端网络非常稳定的场景。
以下情况下可以选择 QoS 1
对系统资源消耗较为关注,希望性能最优化。
消息不能丢失,但能接受并处理重复的消息。
以下情况下可以选择 QoS 2
不能忍受消息丢失(消息的丢失会造成生命或财产的损失),且不希望收到重复的消息。
数据完整性与及时性要求较高的银行、消防、航空等行业。

服务器端要求双向验证

mosquitto -c etc/mosquitto.conf

使用sub订阅时出现错误

mosquitto_sub -L "mqtts://localhost:8883/const.net.cn" --cafile ca.crt -d

Client (null) sending CONNECT
Error: host name verification failed.
OpenSSL Error[0]: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
Error: A TLS error occurred.

在服务器端看到标题所示提示信息
1626420060: New connection from 127.0.0.1:37766 on port 8883.
1626420060: OpenSSL Error[0]: error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error
1626420060: Client <unknown> disconnected: Protocol error.
1626420068: New connection from 127.0.0.1:37768 on port 8883.
1626420068: OpenSSL Error[0]: error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error
1626420068: Client <unknown> disconnected: Protocol error.

修改sub订阅中,加上--insecure,错误信息就变了

mosquitto_sub -L "mqtts://localhost:8883/const.net.cn" --cafile ca.crt -d --insecure

Client (null) sending CONNECT
OpenSSL Error[0]: error:1409445C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required
Error: The connection was lost.
服务器端日志
1626420113: New connection from 127.0.0.1:37770 on port 8883.
1626420113: OpenSSL Error[0]: error:1417C0C7:SSL routines:tls_process_client_certificate:peer did not return a certificate
1626420113: Client <unknown> disconnected: Protocol error.

这个是提示没有提供客户端的证书,因为服务器采取的双向认证嘛。

加上客户端证书与密钥的命令

mosquitto_sub -L "mqtts://localhost:8883/const.net.cn" --cafile ca.crt --key client.key --cert client.crt -d --insecure

Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending SUBSCRIBE (Mid: 1, Topic: const.net.cn, QoS: 0, Options: 0x00)
Client (null) received SUBACK
Subscribed (mid: 1): 0
完美解决问题。

https://snowyang.com/2020/12/11/Network/MQTT/mosquitto/也有提到这个问题。作者还推荐了一个单片机的实现mqtts的方案(mbedtls+lwip+mqtt)
https://mcuoneclipse.com/2017/04/23/tuturial-mbedtls-sll-certificate-verification-with-mosquitto-lwip-and-mqtt/

最后给上参数说明

--insecure : do not check that the server certificate hostname matches the remote
              hostname. Using this option means that you cannot be sure that the
              remote host is the server you wish to connect to and so is insecure.
              Do not use this option in a production environment.