You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Python脚本生成证书配置Mosquitto时遭遇SSL证书验证失败问题求助

使用Python脚本生成证书配置Mosquitto时遭遇SSL证书验证失败问题求助

问题描述

我正尝试为Mosquitto配置SSL,用Python脚本生成证书:

# This script will generate a CA's private key and self-signed certificate,
# a server's private key and CSR, and finally, a server's certificate signed by the CA.
# You can then use these certificates for secure communication in your applications.

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
from datetime import datetime, timedelta

print("Generate the CA's private key and self-signed certificate (ca_cert.pem,ca_key.pem)")
print("\tGenerate CA's private key")
ca_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

print("\tGenerate CA's self-signed certificate")
ca_subject = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, u"BE"),
    x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Wallonia"),
    x509.NameAttribute(NameOID.LOCALITY_NAME, u"Herstal"),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My CA"),
    x509.NameAttribute(NameOID.COMMON_NAME, u"localhost"), #u"myca.example.com"),
])
ca_certificate = x509.CertificateBuilder().subject_name(
    ca_subject
).issuer_name(
    ca_subject
).public_key(
    ca_key.public_key()
).serial_number(
    x509.random_serial_number()
).not_valid_before(
    datetime.utcnow()
).not_valid_after(
    datetime.utcnow() + timedelta(days=365)
).add_extension(
    x509.BasicConstraints(ca=True, path_length=None), critical=True,
).sign(ca_key, hashes.SHA256())

print("\tSave CA's private key and certificate to files")
with open("ca_key.pem", "wb") as f:
    f.write(ca_key.private_bytes(
        encoding=Encoding.PEM,
        format=PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=NoEncryption()
    ))

with open("ca_cert.pem", "wb") as f:
    f.write(ca_certificate.public_bytes(Encoding.PEM))
    
print("Generate the server's private key and certificate signing request (CSR) (server_csr.pem,server_key.pem)")

print("\tGenerate server's private key")
server_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

print("\tGenerate server's CSR")
server_subject = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, u"BE"),
    x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"Wallonia"),
    x509.NameAttribute(NameOID.LOCALITY_NAME, u"Herstal"),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My Server"),
    x509.NameAttribute(NameOID.COMMON_NAME, u"mmqttmaster"),#u"myserver.example.com"),
])
csr = x509.CertificateSigningRequestBuilder().subject_name(
    server_subject
).sign(server_key, hashes.SHA256())

print("\tSave server's private key and CSR to files")
with open("server_key.pem", "wb") as f:
    f.write(server_key.private_bytes(
        encoding=Encoding.PEM,
        format=PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=NoEncryption()
    ))

with open("server_csr.pem", "wb") as f:
    f.write(csr.public_bytes(Encoding.PEM))

print("Sign the server's CSR with the CA's private key to generate the server's certificate (server_cert.pem)")
print("\tSign server's CSR with CA's private key")
server_certificate = x509.CertificateBuilder().subject_name(
    csr.subject
).issuer_name(
    ca_certificate.subject
).public_key(
    csr.public_key()
).serial_number(
    x509.random_serial_number()
).not_valid_before(
    datetime.utcnow()
).not_valid_after(
    datetime.utcnow() + timedelta(days=365)
).add_extension(
    x509.BasicConstraints(ca=False, path_length=None), critical=True,
).sign(ca_key, hashes.SHA256())

print("\tSave server's certificate to file")
with open("server_cert.pem", "wb") as f:
    f.write(server_certificate.public_bytes(Encoding.PEM))

然后我在Hyper-V的Debian12虚拟机中配置Mosquitto:

sudo nano /etc/mosquitto/mosquitto.conf
cafile /etc/mosquitto/ca_certificates/ca_cert.pem
certfile /etc/mosquitto/ca_certificates/server_cert.pem
keyfile /etc/mosquitto/ca_certificates/server_key.pem

sudo systemctl restart mosquitto

执行以下发布命令时:

cd /etc/mosquitto/ca_certificates/
mosquitto_pub -h localhost -p 8883 -t test_topic -m "I am healthy" --cafile ca_cert.pem -d

出现了以下错误:

Client (null) sending CONNECT
OpenSSL Error[0]: error:0A000086:SSL routines::certificate verify failed
Error: A TLS error occurred.

我怀疑是COMMON_NAME配置错了,但不确定应该用什么值。


解决方案

嘿,你猜的没错,问题确实出在证书身份匹配上,我来一步步帮你解决:

核心问题原因

你生成的服务器证书的COMMON_NAME是mmqttmaster,但你用mosquitto_pub连接时指定的主机是localhost。TLS证书验证的关键规则是:客户端会检查服务器证书里的身份标识(优先看主题备用名称SAN,其次是旧的COMMON_NAME)是否和连接的主机名完全一致,不匹配就会触发certificate verify failed错误。

另外要注意:现在主流的TLS客户端(包括mosquitto_pub依赖的OpenSSL)已经更倾向于使用SAN(Subject Alternative Name)扩展来验证身份,而不是仅依赖COMMON_NAME,所以我们最好配置SAN来兼容所有场景。

具体修复方案

你可以选下面任意一种方案,推荐第一种更灵活:

方案1:修改Python脚本,让服务器证书支持多个主机名(推荐)

修改你的证书生成脚本,在签发服务器证书时添加SAN扩展,同时包含localhost和mmqttmaster,这样不管用哪个主机名连接都能通过验证:

找到脚本中生成server_certificate的代码块,在现有的BasicConstraints扩展后面,添加SAN扩展的代码:

server_certificate = x509.CertificateBuilder().subject_name(
    csr.subject
).issuer_name(
    ca_certificate.subject
).public_key(
    csr.public_key()
).serial_number(
    x509.random_serial_number()
).not_valid_before(
    datetime.utcnow()
).not_valid_after(
    datetime.utcnow() + timedelta(days=365)
).add_extension(
    x509.BasicConstraints(ca=False, path_length=None), critical=True,
# 新增以下SAN扩展代码
).add_extension(
    x509.SubjectAlternativeName([
        x509.DNSName(u"localhost"),
        x509.DNSName(u"mmqttmaster")
    ]),
    critical=False,
).sign(ca_key, hashes.SHA256())

修改完成后,重新运行脚本生成新的服务器证书,替换Mosquitto目录下的旧证书,然后重启服务:

sudo systemctl restart mosquitto

之后再用原来的发布命令测试,就能正常连接了。

方案2:客户端使用服务器证书的COMMON_NAME连接

如果你不想重新生成证书,可以调整客户端的连接方式:

  1. 先在客户端的/etc/hosts文件中添加映射,让mmqttmaster指向本地:
    sudo sh -c 'echo "127.0.0.1 mmqttmaster" >> /etc/hosts'
    
  2. 使用这个主机名来发布消息:
    cd /etc/mosquitto/ca_certificates/
    mosquitto_pub -h mmqttmaster -p 8883 -t test_topic -m "I am healthy" --cafile ca_cert.pem -d
    

额外必检查项

除了证书身份匹配,还要确认这两个关键配置:

  • 证书文件权限:确保Mosquitto服务有证书文件的读取权限,执行以下命令修正:
    sudo chown mosquitto:mosquitto /etc/mosquitto/ca_certificates/*.pem
    sudo chmod 600 /etc/mosquitto/ca_certificates/*.pem
    
  • Mosquitto监听端口:确认mosquitto.conf中已经开启了8883端口的SSL监听,添加以下配置:
    listener 8883
    
    否则Mosquitto不会在8883端口提供SSL服务。

备注:内容来源于stack exchange,提问作者rems

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 11:28:01