一、功能介绍
设备证书认证使用平台为每台设备颁发的设备证书和设备私钥验证设备身份。设备连接平台时,通过 CA 证书验证服务器身份,同时向平台提供设备证书并使用设备私钥证明设备身份。双向 TLS 认证通过后,设备即可建立 MQTT 连接并上线。
证书认证采用非对称加密,安全性较高,但要求设备支持 X.509 证书解析及 TLS 连接。
注意:设备私钥属于敏感凭证,请妥善保管。CA 证书和设备证书可以公开分发,但不得泄露设备私钥。
如果尚未确定使用设备证书认证还是设备密钥认证,请参见 认证方式概览。
二、认证流程
设备证书认证流程参见下图:
三、认证参数
设备证书认证涉及“设备身份参数”、“证书文件”和“MQTT 连接参数”三类参数。
如需在控制台创建证书认证设备,并获取设备证书、设备私钥、腾讯云 CA 证书及设备身份信息,请参见 获取设备三元组。
四、设备身份参数
| 参数 | 参数来源 | 说明 | 是否保密 |
| ProductID | 产品详情页或设备详情页。 | 标识设备所属产品。 | 否 |
| DeviceName | 设备详情页。 | 标识产品下的具体设备。 | 否 |
ProductID 与 DeviceName 组合后形成平台中的设备唯一标识。
五、证书文件
| 文件 | 获取方式 | 作用 | 是否保密 |
| 设备证书 | 创建设备后由平台颁发。 | 向平台声明设备身份。 | 否 |
| 设备私钥 | 创建设备后由平台颁发。 | 证明设备持有对应设备证书。 | 是 |
| CA 证书 | 平台证书下载入口。 | 验证物联网开发平台服务器身份。 | 否 |
设备证书与设备私钥必须配套使用,不能混用不同设备的证书和私钥。
注意:请在设备凭证生成后及时下载并安全保存设备私钥。设备私钥不得写入公开代码、日志、截图或普通配置文件。
证书文件参考:
六、MQTT 连接参数
| 参数 | 取值或生成方式 | 说明 |
| Broker Address | 广州:${ProductID}.iotcloud.tencentdevices.com曼谷:${ProductID}.ap-bangkok.iothub.tencentdevices.com | MQTT 服务器地址。 |
| Broker Port | 8883 | 证书认证设备接入端口。 |
| Client ID | ${ProductID}${DeviceName} | ProductID 与 DeviceName 直接拼接。 |
| Username | ${ClientID};${sdkappid};${connid};${expiry} | MQTT 连接用户名。 |
| Password | 可填写任意非空值 | 证书认证不校验 Password 内容。 |
| KeepAlive | 0~900秒 | MQTT 连接保活时间。 |
${}表示变量,不是实际的拼接字符。连接域名和端口应以设备所属地域及控制台当前信息为准。
Username 中各字段的含义如下:
| 字段 | 说明 |
| sdkappid | 固定填写12010126。 |
| connid | 客户端生成的随机字符串,用于区分连接。 |
| expiry | 连接参数的过期时间,使用 Unix 时间戳,单位为秒。 |
证书认证不使用 DeviceSecret,也不需要对 Username 进行 HMAC 签名。Password 不会参与设备证书校验,但仍需按照 MQTT 客户端的要求设置一个非空值。
七、证书有效期与更新
设备证书和平台 CA 证书均存在有效期。生产环境应建立证书有效期检查及更新机制,避免证书到期导致设备无法连接平台。
八、平台 CA 证书
平台 CA 证书由物联网开发平台提供,用于设备验证 MQTT 服务器证书,不属于单台设备的身份凭证,可供使用相同平台信任链的设备复用。
平台 CA 证书发生更新时:
·使用设备端 SDK 的设备,应根据平台通知升级至支持新 CA 证书的 SDK 版本,并通过固件升级部署至设备。
·使用自研 MQTT 客户端的设备,应从物联网开发平台控制台或产品文档获取最新 CA 证书,并通过固件升级或 OTA 更新设备端信任库。
·平台不会自动将新 CA 证书写入存量设备,用户需要自行完成设备侧证书更新。
·生产设备应具备远程更新 CA 证书的能力,并根据平台发布的更新时间安排升级,避免存量设备集中断连。
设备证书和 CA 证书均具有有效期。设备建立 TLS 连接时,平台会校验设备证书是否处于有效期内,设备也会通过 CA 证书校验平台服务器证书。
可以使用以下命令查看证书有效期:
请先下载证书文件,并在证书文件的目录下打开终端,执行:
# Windows PowerShellcertutil -dump "证书文件名"# Linux && macOS Shellopenssl x509 -in "证书文件名" -noout -dates# 查看输出结果…notBefore= # 证书有效起始时间notAfter= # 证书有效结束时间…
# 示例# 腾讯云证书名称:ca.crt# 设备证书名称:certificate_authentication_device_cert.crt# Windows PowerShellcertutil -dump ca.crtcertutil -dump certificate_authentication_device_cert.crt# Linux && macOS Shellopenssl x509 -in ca.crt -noout -datesopenssl x509 -in certificate_authentication_device_cert.crt -noout -dates# 查看输出结果…notBefore= # 证书有效起始时间notAfter= # 证书有效结束时间…
九、生成 MQTT 连接参数
设备详情页如已提供完整 MQTT 连接参数,可直接复制使用。也可以根据 ProductID 和 DeviceName 生成 Client ID 与 Username。
生成关系如下:
Client ID = ProductID + DeviceNameUsername = ClientID + ";12010126;" + connid + ";" + expiryPassword = 任意非空字符串
示例:
ProductID = testProductIDDeviceName = testDeviceNameconnid = A1B2Cexpiry = 1786610239Client ID = testProductIDtestDeviceNameUsername = testProductIDtestDeviceName;12010126;A1B2C;1786610239Password = certificate
注意:说明:
注意:以上参数仅用于说明拼接规则,不能用于连接真实设备。
十、安全注意事项
·每台设备应使用独立的设备证书和设备私钥。
·不得在日志文件、截图、工单或公开的代码仓库中暴露设备私钥。
·设备私钥建议保存在安全芯片或受保护的存储区域中。
·设备证书与设备私钥必须配套使用,不得在不同的设备之间复制使用。
·不得通过 tls_insecure_set(True) 关闭服务器证书校验。
·设备应维护正确的系统时间,避免证书校验或连接参数校验出现异常。
·调试完成后,应删除临时复制的设备私钥文件。
·当设备私钥发生泄露时,应及时禁用设备并重新发放设备凭证。
十一、使用 Python 连接平台
本节使用 Python 脚本连接物联网开发平台。当脚本返回认证成功且控制台设备状态显示为在线时,即表示设备证书认证接入完成。
本示例仅验证设备认证和上线,不包含 Topic 发布、订阅及物模型消息通信。
十二、前提条件
开始接入前,请确认已完成以下准备:
·已创建认证方式为证书认证的产品。
·已在产品下创建设备。
·已获得 ProductID 和 DeviceName。
·已下载当前设备的设备证书和设备私钥。
·已下载平台提供的设备端 CA 证书。
·本地环境已安装 Python 3。
·本地网络能够访问平台 MQTT 接入域名。
·设备系统时间正确。
十三、准备运行环境
建议在虚拟环境中安装依赖和运行代码。
安装 Paho MQTT 客户端:
# Windows powershellpython -m pip install "paho-mqtt>=2.1,<3.0"# Linux && macOS shellpython3 -m pip install "paho-mqtt>=2.1,<3.0"
Paho MQTT 是 Python MQTT 客户端库。本示例使用 MQTT 3.1.1 及 Callback API VERSION2。
安装完成后,可执行以下命令确认版本:
# Windows PowerShellpython -m pip show paho-mqtt# Linux && macOS Shellpython3 -m pip show paho-mqtt# 输出中的 Version 应为 2.1.0。
十四、创建连接脚本
将以下代码保存为mqtt_cert_connect.py:
#!/usr/bin/env python3# -*- coding: utf-8 -*-import secretsimport sslimport stringimport sysimport timefrom pathlib import Pathtry: import paho.mqtt.client as mqttexcept ImportError: mqtt = NoneSDK_APP_ID = "12010126"# 凭证只能从脚本同级的cert目录中读取。SCRIPT_DIR = Path(__file__).resolve().parentCERT_DIR = (SCRIPT_DIR / "cert").resolve()def required_input(prompt): """读取必填参数并清除首尾空格。""" value = input(prompt).strip() if not value: raise ValueError("输入内容不能为空") return valuedef get_cert_file(prompt): """根据用户输入的文件名,从cert目录中读取凭证文件。""" filename = required_input(prompt) # 只允许输入文件名,不允许输入目录或绝对路径。 if ( Path(filename).is_absolute() or "/" in filename or "\\\\" in filename or filename in {".", ".."} ): raise ValueError( "只允许输入cert目录中的文件名,不能包含路径" ) file_path = (CERT_DIR / filename).resolve() # 防止通过符号链接等方式访问cert目录外的文件。 try: file_path.relative_to(CERT_DIR) except ValueError as exc: raise ValueError( "凭证文件必须位于脚本同级的cert目录中" ) from exc if not file_path.is_file(): raise FileNotFoundError( f"在cert目录中未找到文件:{filename}" ) try: with file_path.open("rb") as file: file.read(1) except OSError as exc: raise PermissionError( f"无法读取凭证文件:{filename}" ) from exc return file_pathdef generate_connid(length=5): """生成用于区分MQTT连接的随机字符串。""" alphabet = string.ascii_uppercase + string.digits return "".join(secrets.choice(alphabet) for _ in range(length))def main(): if mqtt is None: raise ImportError( '未安装paho-mqtt,请执行:' 'python3 -m pip install "paho-mqtt>=2.1,<3.0"' ) if not CERT_DIR.is_dir(): raise FileNotFoundError( f"未找到cert目录,请创建:{CERT_DIR}" ) print("请输入cert目录中的凭证文件名。") print(f"凭证目录:{CERT_DIR}\\n") ca_file = get_cert_file("CA证书文件名:") device_cert_file = get_cert_file("设备证书文件名:") device_key_file = get_cert_file("设备私钥文件名:") print("\\n已加载以下凭证文件:") print(f"- CA证书:{ca_file.name}") print(f"- 设备证书:{device_cert_file.name}") print(f"- 设备私钥:{device_key_file.name}") broker = required_input("\\nMQTT服务器地址:") port_text = input("端口号(直接回车使用8883):").strip() port = int(port_text) if port_text else 8883 product_id = required_input("ProductID:") device_name = required_input("DeviceName:") client_id = f"{product_id}{device_name}" connid = generate_connid() expiry = int(time.time()) + 60 * 60 username = ( f"{client_id};{SDK_APP_ID};{connid};{expiry}" ) # 证书认证不校验Password内容。 password = "certificate" def on_connect( client, userdata, connect_flags, reason_code, properties, ): if reason_code == 0: print("\\n认证成功,设备已连接物联网开发平台。") print("请前往控制台查看设备在线状态。") else: print(f"\\nMQTT连接失败,返回码:{reason_code}") client.disconnect() def on_connect_fail(client, userdata): print( "\\n网络连接或TLS握手失败," "请检查服务器地址、端口及凭证文件。" ) def on_disconnect( client, userdata, disconnect_flags, reason_code, properties, ): if reason_code != 0: print(f"\\n连接异常断开,返回码:{reason_code}") client = mqtt.Client( callback_api_version=mqtt.CallbackAPIVersion.VERSION2, client_id=client_id, clean_session=True, protocol=mqtt.MQTTv311, ) client.username_pw_set( username=username, password=password, ) client.tls_set( ca_certs=str(ca_file), certfile=str(device_cert_file), keyfile=str(device_key_file), cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLS_CLIENT, ) # 启用服务器证书及域名校验。 client.tls_insecure_set(False) client.on_connect = on_connect client.on_connect_fail = on_connect_fail client.on_disconnect = on_disconnect print(f"\\n正在连接:{broker}:{port}") print(f"ClientId:{client_id}") print("程序将保持连接,按Ctrl+C退出。") client.connect( host=broker, port=port, keepalive=60, ) try: client.loop_forever() finally: client.disconnect()if __name__ == "__main__": try: sys.exit(main()) except KeyboardInterrupt: print("\\n用户终止连接。") sys.exit(0) except Exception as exc: print(f"\\n运行失败:{exc}") sys.exit(1)
该脚本仅读取证书文件,不会复制或保存证书内容,也不会将认证参数写入配置文件。
十五、准备证书文件
将以下文件放置在mqtt_cert_connect.py 同目录的cert中
├─ mqtt_cert_connect.py└─ cert/ ├─ ca.crt ├─ device.crt └─ device.key
其中:
·ca.crt 表示平台 CA证书。
·device.crt 表示当前设备的设备证书。
·device.key 表示与设备证书配套的设备私钥。
注意:说明:
注意:以上名称仅为示例,运行脚本时请输入实际文件名。
十六、运行连接脚本
# Windows PowerShellpython mqtt_cert_connect.py# Linux && macOS Shellpython3 mqtt_cert_connect.py
运行后依次输入:
# 运行示例凭证目录:D:\\test\\shell\\test_cert\\certCA证书文件名:ca.crt设备证书文件名:certificate_authentication_device_cert.crt设备私钥文件名:certificate_authentication_device_private.key已加载以下凭证文件:- CA证书:ca.crt- 设备证书:certificate_authentication_device_cert.crt- 设备私钥:certificate_authentication_device_private.keyMQTT服务器地址:ProductID.iotcloud.tencentdevices.com端口号(直接回车使用8883):8883ProductID:ProductIDDeviceName:certificate_authentication_device
连接成功时,程序输出类似:
正在连接:******.iotcloud.tencentdevices.com:8883ClientId:******程序将保持连接,按Ctrl+C退出。认证成功,设备已连接物联网开发平台。请前往控制台查看设备在线状态。
程序会持续运行并保持 MQTT 连接。需要断开连接时,按下Ctrl+C。
十七、验证设备上线
设备发起 MQTT 连接后,如需在控制台确认设备状态及查看上下线记录,请参见 查看设备状态和上下线日志。
十九、网络连接失败
检查以下内容:
·Broker Address 是否与设备详情页或当前地域接入地址一致。
·端口号是否为证书认证端口 8883。
·本地 DNS 是否能够解析 MQTT 服务器域名。
·防火墙或网络策略是否限制对目标域名和端口的访问。
·相关域名是否已加入访问白名单。
20、TLS 握手失败
检查以下内容:
·是否加载了平台提供的正确的 CA 证书。
·设备证书与设备私钥是否属于同一台设备。
·设备证书文件或私钥文件是否损坏。
·证书文件的路径是否正确。
·设备是否具有读取证书文件和私钥文件的权限。
·设备的系统时间是否正确。
·是否错误关闭了服务器证书校验或域名校验。
21、MQTT 连接失败
检查以下内容:
·ProductID 和 DeviceName 是否属于当前的设备。
·Client ID 是否由 ProductID 和 DeviceName 直接拼接而成。
·Username 中指定的 Client ID 是否与连接时使用的 Client ID 一致。
·Username 中的 sdkappid 是否为 12010126。
·Username 中的 expiry 字段是否已过期。
·当前产品是否采用了证书认证。
·设备证书是否已经失效。
22、证书与私钥不匹配
如果 TLS 握手提示证书错误或私钥错误,请确认:
·未混用不同设备的证书和私钥。
·私钥文件的内容完整。
·文件的格式能够被当前的 TLS 客户端识别。
·私钥未在复制、换行或编码转换的过程中损坏。
23、脚本连接成功,但设备未显示在线
检查以下内容:
·是否进入了正确的实例和正确的设备详情页。
·Client ID 是否对应了当前的 DeviceName。
·连接脚本是否仍在运行。
·设备的上下线日志中是否存在新的连接记录。
·是否有另一个客户端使用相同的 Client ID 建立了连接。