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

Azure IoT Hub Python SDK X509证书密码校验未生效问题

问题背景

我最初参考微软官方PowerShell版本的X.509证书配置教程搭建测试环境,先后验证了两个设备连接Azure IoT Hub的场景:

  • 场景1:Windows 10笔记本上通过.NET Framework应用连接
    测试代码如下:
    static void Main(string[] args)
    {
        try
        {
            // 创建X.509证书对象
            var cert = new X509Certificate2(@"..\test-device-auth\test-device-auth.pfx", "pass", X509KeyStorageFlags.UserKeySet);
            Console.WriteLine("cert: ");
            Console.WriteLine(cert);
    
            // 使用X.509证书创建认证对象
            var auth = new DeviceAuthenticationWithX509Certificate(deviceId, cert);
    
            // 创建设备客户端
            var deviceClient = DeviceClient.Create("Arduino-IoT-Hub-Temperature.azure-devices.net", auth, TransportType.Mqtt);
    
            if (deviceClient == null)
            {
                Console.WriteLine("Failed to create DeviceClient!");
            }
            else
            {
                Console.WriteLine("Successfully created DeviceClient!");
                SendEvent(deviceClient).Wait();
            }
    
            Console.WriteLine("Exiting...\n");
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error in sample: {0}", ex.Message);
        }
    }
    
    该场景验证结果符合预期:传入正确PFX证书和匹配密码时程序正常运行,传入错误密码或无效PFX文件时程序直接运行失败。
  • 场景2:树莓派3B上通过Python脚本连接
    测试代码如下:
    # -------------------------------------------------------------------------
    # Copyright (c) Microsoft Corporation. All rights reserved.
    # Licensed under the MIT License. See License.txt in the project root for
    # license information.
    # --------------------------------------------------------------------------
    import os
    import uuid
    from azure.iot.device.aio import IoTHubDeviceClient
    from azure.iot.device import Message, X509
    import asyncio
    
    messages_to_send = 10
    
    async def main():
        hostname = "Arduino-IoT-Hub-Temperature.azure-devices.net"
        # 门户上通过X509 CA签名或自签名能力创建的设备ID
        device_id = "test-device-auth"
    
        x509 = X509(
            cert_file="../test-device-auth/test-device-auth-public.pem",
            key_file="../test-device-auth/test-device-auth-private.pem",
            pass_phrase="pass",
        )
    
        # 客户端对象用于和Azure IoT Hub交互
        device_client = IoTHubDeviceClient.create_from_x509_certificate(
            hostname=hostname, device_id=device_id, x509=x509
        )
    
        # 建立客户端连接
        await device_client.connect()
    
        async def send_test_message(i):
            print("sending message #" + str(i))
            msg = Message("test wind speed " + str(i))
            msg.message_id = uuid.uuid4()
            msg.correlation_id = "correlation-1234"
            # msg.custom_properties["tornado-warning"] = "yes"
            msg.content_encoding = "utf-8"
            msg.content_type = "application/json"
            await device_client.send_message(msg)
            print("done sending message #" + str(i))
    
        # 并行发送指定数量的测试消息
        await asyncio.gather(*[send_test_message(i) for i in range(1, messages_to_send + 1)])
    
        # 关闭客户端
        await device_client.shutdown()
    
    if __name__ == "__main__":
        asyncio.run(main())
    
        # Python 3.6版本请用以下代码替换asyncio.run(main())
        # loop = asyncio.get_event_loop()
        # loop.run_until_complete(main())
        # loop.close()
    
    该场景出现异常现象:使用的PEM格式证书私钥没有被pass_phrase参数校验,无论传入正确密码、错误密码,甚至完全不传密码,程序都能正常运行。
问题原因

出现该现象的核心原因是当前使用的私钥PEM文件本身没有被密码加密,pass_phrase参数仅在私钥本身是加密状态时才会生效。
从PFX格式导出PEM公私钥的时候,如果没有给私钥设置加密密码,导出的私钥文件是明文存储的,此时底层TLS库读取私钥时根本不需要密码,传入的pass_phrase参数会被直接忽略,自然不会触发密码校验逻辑。
.NET场景下PFX文件本身是带密码加密的容器格式,加载时必须输入正确密码才能读取到内部的公私钥内容,所以密码校验逻辑正常生效。

修复配置方法

要让Python场景下的私钥密码校验正常生效,只需要导出私钥PEM文件时给私钥设置AES加密密码即可,操作步骤如下:

  1. 如果使用OpenSSL从PFX导出PEM,执行导出私钥的命令时,去掉-nodes参数(该参数的作用就是导出不加密的明文私钥),按照终端提示设置要使用的私钥密码即可,示例命令:
    # 导出加密的私钥文件,执行后按提示输入密码
    openssl pkcs12 -in test-device-auth.pfx -nocerts -out test-device-auth-private.pem
    # 导出公钥证书文件(不需要加密)
    openssl pkcs12 -in test-device-auth.pfx -clcerts -nokeys -out test-device-auth-public.pem
    
  2. 验证私钥文件是否加密:打开导出的test-device-auth-private.pem文件,如果头部是-----BEGIN ENCRYPTED PRIVATE KEY-----就说明私钥已经被加密,此时如果传入的pass_phrase和设置的私钥密码不匹配,程序会直接抛出私钥读取失败的错误,密码校验机制就会正常生效;如果头部是-----BEGIN PRIVATE KEY-----说明还是明文私钥,需要重新导出。

注意:Azure IoT Python SDK的X509类底层依赖系统安装的OpenSSL库加载证书,只要私钥本身是加密格式,传入的密码错误时会直接在证书加载阶段抛出异常,不会建立连接。


内容的提问来源于stack exchange,提问作者VitoCK

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 10:15:30