MQTT TLS 连接

MQTT TLS connection

我想将测试 MQTT-Client 作为 MQTT-Broker 连接到我的 Node.js 应用程序。我正在使用来自 moscajs

的 aedes 库

我的 MQTT-Client 是工具“MQTT-Explorer”,这是我的 Node.js 应用程序:

const fs = require('fs');
const aedes = require('aedes')();

const options = {
  key: fs.readFileSync('certs/server_key.pem'),
  cert: fs.readFileSync('certs/server_cert.pem'),
};

// const server = require('net').createServer(aedes.handle);
const server = require('tls').createServer(options, aedes.handle);

const PORT = 8881;

server.listen(PORT, () => {
  console.log(`server is up and running: PORT [${PORT}] - ID [${aedes.id}]`);
});

我可以毫无问题地使用 const server = require('net').createServer(aedes.handle) 连接到 PORT=1881,我也可以使用 const server = require('tls').createServer(options, aedes.handle)

连接到 PORT=8881

使用工具 xca-2.4.0.msi XCA 2.4.0 我创建了一个 ca.pem 证书文件和一个证书 server_cert.pem 和一个 server_key.pem 私钥(由 ca.pem) 作为服务器。 CA 和服务器的密钥不同:

对于我的 MQTT 客户端,我在 ADVANCED, CERTIFICATES, SERVER CERTIFICAT (CA) 下 select 编辑了 ca.pem 文件。如果我 select “加密”,它就可以工作。但是,如果 select“验证证书”,错误:Hostname/IP 与证书的别名不匹配:IP:127.0.0.1 不在证书列表中

不幸的是我不知道我做错了什么,提前致谢:(

MQTT Explorer is built using Node.js and the MQTT library MQTT.js. As per this issue:

Node.js requires the IP address to be in the subjectAltNames for the cert and not in the CN. Maybe MQTT.fx isn't requiring that, but it should.

和:

If your server's certificate says CN=localhost in the Subject field, connect using localhost and not 127.0.0.1 and it should work. If it says CN=127.0.0.1, you have to make a new one because Node.js won't validate the IP address unless it's in the SAN extension. There is a way to work around it in code (I think it's an option called checkServerIdentity), but I would prefer to fix my certificate if I had this problem.

this answer 中阐述了在 Node 中采用的方法的基本原理,其中包括来自 RFC2818 的以下引用:HTTP Over TLS :

In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI.

当您使用基于 TLS 的 MQTT(而不是基于 TLS 的 HTTP)时,您可能会争辩说上述内容不适用,但是,考虑到 TLS 库的主要用途是用于 HTTP 流量,它是有道理的默认确认 RFC。

您有几个选项,包括:

  • 在创建 certificate/connecting.
  • 时使用主机名(例如 localhost)而不是 IP
  • 将 IP 添加为 subjectAltName
  • 修改库以使用 noop checkServerIdentity(参见 this answer)。
  • 使用另一个应用程序进行测试(不推荐,因为有些应用程序可以工作,有些则不能)。上面引用的问题提到 MQTT.fx 有效。