在 python 3.7 中使用 paho mqtt 客户端时出现问题

Problems using paho mqtt client with python 3.7

我正在运行使用以下代码连接到 mqtt 服务器。

import paho.mqtt.client as mqtt
import ssl
import uuid

client = mqtt.Client(str(uuid.uuid1()))
client.tls_set(
    "ca.crt",
    "client.crt",
    "client.key",
    cert_reqs=ssl.CERT_REQUIRED,
    tls_version=ssl.PROTOCOL_TLSv1
)
client.connect(
    "127.0.0.1",
    8883,
)
client.loop_forever()

此代码适用于 python2.7 版本。但是当我 运行 它与 python3.7 版本时,我收到以下错误。

Traceback (most recent call last):
  File "test.py", line 29, in <module>
    8883,
  File "virtualenvs/mqtt-xG2h6zri/lib/python3.7/site-packages/paho/mqtt/client.py", line 839, in connect
    return self.reconnect()
  File "mqtt-xG2h6zri/lib/python3.7/site-packages/paho/mqtt/client.py", line 994, in reconnect
    sock.do_handshake()
  File ".pyenv/versions/3.7.0/lib/python3.7/ssl.py", line 1108, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for '127.0.0.1'. (_ssl.c:1045)

请帮我弄清楚如何在 python 3.7.

中完成这项工作

找到答案。

实际上,根据 this link 将服务器 IP 地址与证书的 CN 字段相匹配已被弃用超过 15 年。但是 python 低于 3.7 的版本仍然允许这样做,即使它已被弃用。因此,我必须创建一个证书,并在 SAN 字段中添加服务器的 IP 地址。

this 回答中解释了使用 SAN 字段创建证书。但是答案中的解决方案使用了域名。如果您使用 IP 地址创建证书,请使用此命令而不是该答案中的命令来创建证书。

openssl x509 -req -in server.csr \
        -extfile <(printf "subjectAltName=IP:127.0.0.1") \
        -CA ca.crt \
        -CAkey ca.key \
        -CAcreateserial -out server.crt \
        -days 365

使用这些证书后错误解决。

有类似的问题(尽管使用的是 RabbitsMQ 客户端 Pika 而不是 MQTT),但是在尝试连接时 python 3.8 中的 do_handshake() 方法在 SSL 库中抛出了类似的错误到外部端点而不是本地主机,我无法生成提供给我的新 ssl 证书。

接受的答案对我有帮助,但我认为它可能对其他人有帮助,因为降级到 python3.6 会提供更有用的错误消息:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/root/miniconda/envs/venv3.6/lib/python3.6/site-packages/pika/adapters/blocking_connection.py", line 359, in __init__
    self._impl = self._create_connection(parameters, _impl_class)
  File "/root/miniconda/envs/venv3.6/lib/python3.6/site-packages/pika/adapters/blocking_connection.py", line 450, in _create_connection
    raise self._reap_last_connection_workflow_error(error)
  File "/root/miniconda/envs/venv3.6/lib/python3.6/site-packages/pika/adapters/utils/io_services_utils.py", line 636, in _do_ssl_handshake
    self._sock.do_handshake()
  File "/root/miniconda/envs/venv3.6/lib/python3.6/ssl.py", line 1077, in do_handshake
    self._sslobj.do_handshake()
  File "/root/miniconda/envs/venv3.6/lib/python3.6/ssl.py", line 694, in do_handshake
    match_hostname(self.getpeercert(), self.server_hostname)
  File "/root/miniconda/envs/venv3.6/lib/python3.6/ssl.py", line 331, in match_hostname
    % (hostname, dnsnames[0]))
ssl.CertificateError: hostname '149.176.221.21' doesn't match 'redacted.internal.url'

其中 redacted.internal.url 是您必须为连接设置的主机名(在 ssl 上下文对象中),因为这是证书上的任何内容。

这与您实际连接的 IP address/url 无关,在本例中是 149.176.221.21

我重新创建了服务器证书,其中 Common Name 字段作为服务器机器(安装了 mosquitto 代理的地方)的“主机名”,而不是 IP 地址。
基本上 x.x.x.x 主机名
但是,它适用于 windows

此外,如果有人在使用 TLS 证书,将 tls_insecure_set() 设置为 true 没有任何意义。

这里写的信息对我帮助很大,经过大约2天的努力,我找到了解决方案。

我为我的成功实验制作了一个脚本。 希望它对您的业务有益。

#!/bin/bash

wd="`dirname [=10=]`"
if [ ! -z "$wd" ]; then
    if [ $wd == "." ];then wd=`pwd`;fi  
fi

certDir="${wd}/certs";
conf_dir="/mosquitto/config" #WD

rm -rf ${certDir} 

mkdir -p ${certDir}

cd ${certDir}


#subjectAltName olmazsa self signet sertifika ile bağlanamıyor.
#Server IP - Port bilgileri

#~ IP="mqtt.eclipseprojects.io"

IP="192.168.1.10"
PORT="58883"

SUBJECT_CA="/C=TR/ST=Istanbul/L=Istanbul/O=Example/OU=CA/CN=$IP"
SUBJECT_SERVER="/C=TR/ST=Istanbul/L=Istanbul/O=Example/OU=server/CN=$IP"
SUBJECT_CLIENT="/C=TR/ST=Istanbul/L=Istanbul/O=Example/OU=client/CN=$IP"

MAX_DAYS=3650
 
SB_NAME="subjectAltName = IP:127.0.0.1" #"subjectAltName=DNS:example.com,IP:$IP"
space="################################################################################################################"

function generate_CA () {
   echo -e "$SUBJECT_CA\n$space" 
   openssl req -x509 -nodes -sha256 -newkey rsa:2048 -subj "$SUBJECT_CA" -addext  "${SB_NAME}" -days ${MAX_DAYS} -keyout ca.key -out ca.crt 
   
}

function generate_server () {
    echo -e "$SUBJECT_SERVER\n$space"

    openssl req -nodes -sha256 -new -subj "$SUBJECT_SERVER" -keyout server.key -out server.csr -addext "${SB_NAME}"

    openssl x509 -req -in server.csr \
    -extfile <(printf "${SB_NAME}") \
    -CA ca.crt \
    -CAkey ca.key \
    -CAcreateserial -out server.crt \
    -days ${MAX_DAYS}
}

function generate_client () {
   echo -e "$SUBJECT_CLIENT\n$space"
   openssl req -new -nodes -sha256 -subj "$SUBJECT_CLIENT" -out client.csr -keyout client.key 
   openssl x509 -req -sha256 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days ${MAX_DAYS}
}

function clean () {
   echo -e "$SUBJECT_CLIENT\n$space"
    rm client.csr client.key client.crt 
    rm server.csr server.key server.crt 
    rm ca.srl ca.key ca.crt 
}


#~ clean

generate_CA
generate_server
generate_client
############################################
cd ${wd}

echo "port ${PORT}

#require_certificate true

cafile ${conf_dir}/certs/ca.crt
certfile ${conf_dir}/certs/server.crt
keyfile ${conf_dir}/certs/server.key

tls_version tlsv1.2

use_identity_as_username true
password_file ${conf_dir}/password.txt
listener 1883

persistence false
persistence_location /mosquitto/data/

" > mosquitto.conf



echo "Use Examples
#Server

mosquitto -c ${certDir}/mosquitto.conf -v

#Subcriber

mosquitto_sub -h ${IP} -p ${PORT}  --cafile ${certDir}/ca.crt --cert ${certDir}/client.crt --key ${certDir}/client.key -t temperature

#Publisher

mosquitto_pub  -h ${IP} -p ${PORT} --cafile ${certDir}/ca.crt --cert ${certDir}/client.crt --key ${certDir}/client.key -t temperature -m test_message
"