在 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
"
我正在运行使用以下代码连接到 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
"