亚马逊AWS官方博客

大规模嵌入式 Linux 设备自动化接入 AWS IoT Core

简介

在当今物联网(IoT)的时代,嵌入式系统的应用越来越广泛,无论是智能家居、工业自动化还是智能城市,嵌入式设备都扮演着至关重要的角色。而随着云计算和物联网的发展,将嵌入式设备与云平台进行连接和通信变得愈发重要。AWS IoT Core 作为亚马逊云科技物联网服务的核心组件,为开发者提供了稳定可靠的基础设施,帮助连接、管理和安全地交互大规模的物联网设备。

ARM(Advanced RISC Machines)处理器架构是嵌入式系统中最为广泛使用的一种架构。ARM 处理器凭借其低功耗、高性能和高性价比等特点,成为了嵌入式设备的首选,这对于电池供电的嵌入式设备尤为重要。嵌入式系统中的 ARM 处理器通常采用 32 位架构,适用于需要高效能耗比和紧凑设计的应用场景。ARM 生态系统非常成熟,拥有丰富的软件开发工具链、广泛的操作系统支持以及庞大的开发者社区。这些都使得基于 ARM 的嵌入式开发更加便捷和高效。目前 ARM 处理器不仅在消费电子、工业控制和智能家居中得到广泛应用,还在自动驾驶、物联网(IoT)和人工智能等前沿领域展现出强大的潜力。

AWS IoT Core 是亚马逊云科技提供的一项托管式物联网平台服务,旨在帮助开发者轻松地连接、管理和安全地交互物联网设备。作为 AWS IoT 服务套件的核心组件之一,AWS IoT Core 提供了丰富的功能和工具,包括设备注册、消息传输、设备影子、规则引擎、安全认证等,使开发者能够快速构建可靠的物联网解决方案。

AWS IoT Core 利用 X.509 证书对设备连接进行身份验证,设备端必须先注册设备证书,然后才能与 AWS IoT Core 进行通信。而 AWS IoT Core 最佳实践就是为每一个设备分配唯一的证书和私钥,但在实际的生产过程中为每一个出厂设备烧录唯一的证书和私钥无疑会造成更高的成本和复杂度。不过借助 AWS IoT Core fleet provisioning 功能,设备只需要出厂时烧录统一的设备预置证书和私钥,即可在首次连接到 AWS IoT Core 时,由 AWS IoT Core 为设备签名设备唯一的证书,并将其安全地交付给设备。这样就可以实现大量设备安全的自动连接 AWS IoT Core 功能。

因为 ARM 架构在嵌入式领域具有很高的普及率和适用性,所以本篇文章中的嵌入式 Linux 设备我们以 Versatile Express A9 开发板为例, 该开发板使用 Cortex–A9 32 位 ARM CPU。我们会使用 AWS IoT Embedded C SDK 代码样例在 x64 机器上进行交叉编译,生成 ARM 32 位 IoT 测试应用。

现在,让我们开始学习如何将大规模的嵌入式 Linux 设备与 AWS IoT Core 进行集成,并实现自动化的设备接入和数据交换。

所需条件

  • 一个 AWS 账号。
  • 具有管理员权限的 IAM 用户。
  • 一台 x64 架构 Ubuntu 22.04 测试机器。

架构图

整体大致流程如下:

  1. 管理员生成 CA 证书,设备预置证书和私钥,并把 CA 证书和设备预置证书上传到 AWS IoT Core。这里以自己生成 CA 及预置证书为例。
  2. 接下来我们创建设备预置证书策略和设备唯一证书策略。预置证书策略允许设备访问 CreateCertificateFromCsr和RegisterThing MQTT API。这两个 API 本质上还是 AWS IoT Core 保留的 MQTT topic,分别具有对证书 csr 文件签名以返回证书文件和执行预定义模板配置的功能。设备唯一证书策略用来配置设备唯一证书对应的权限,给予 IoT 设备实际业务中相关 MQTT topic 访问的权限。
  3. 创建 fleet provisioning 配置模板,以便后续能够自动化完成设备唯一证书激活,AWS IoT Core 物品注册,设备唯一证书策略绑定等。
  4. 利用设备预置证书和私钥连接到 AWS IoT Core。
  5. 设备在本地生成该设备唯一证书 csr 文件和私钥,并访问 CreateCertificateFromCsr MQTT API。
  6. AWS IoT Core 服务对设备唯一证书 csr 文件进行签名,返回设备唯一证书文件和 certificateOwnershipToken。该 token 用来完成下一步 fleet provisioning 模板的执行。
  7. 设备访问 RegisterThing MQTT API,并传递 certificateOwnershipToken,设备序列号参数。
  8. AWS IoT Core 按照 fleet provisioning 模板执行对应的配置。
  9. 设备断开之前的 AWS IoT Core 连接,并使用刚获取的设备唯一证书和对应私钥连接到 AWS IoT Core。
  10. 此时,设备可以根据之前设备唯一证书关联的策略,与 AWS IoT Core 服务互相收发消息了。

操作步骤

  1. 创建 CA 证书,设备预置证书和私钥。

本文为了演示,使用 OpenSSL 生成演示用的 CA 根证书,设备预置证书和私钥。 但在实际生产环境中相关证书的生成,管理都是非常重要且严格控制的,因此生产环境我们可以使用 AWS Private CA 服务来完成相关证书的创建和管理,具体信息请参考 AWS Private CA 官方文档。另外尽管 AWS IoT Core 服务已经内置了 AWS 的 CA 证书,且也可以直接创建设备证书。但 AWS IoT Core 服务不同账号不同区域内置的 CA 证书都是不同的,如果考虑到业务以后全球拓展,设备就近接入各个区域的 AWS IoT Core 服务,那么使用自己的 CA 证书,设备预置证书和私钥无疑会更加方便简单。我们只需要重新在其他账号或其他区域导入这些证书即可,不需要修改设备的出厂流程。

# 创建CA证书
mkdir cert && cd cert
openssl ecparam -name prime256v1 -genkey -noout -out root_ca.key
openssl req -x509 -new -nodes -key root_ca.key -sha256 -days 3650 -out root_ca.crt -subj "/CN=AnyCompany Root CA"
# 创建设备预置证书和私钥
openssl ecparam -name prime256v1 -genkey -noout -out provisoning.key
openssl req -new -key provisoning.key -out provisoning.csr -subj "/CN=AnyCompany Provisioning Cert"
openssl x509 -req -in provisoning.csr -CA root_ca.crt -CAkey root_ca.key -CAcreateserial -out provisoning.crt -days 1825 -sha256

至此我们已经创建了 CA 证书 root_ca.crt,设备预置证书 provisoning.crt,设备预置证书私钥 provisoning.key。

  1. 在 AWS 控制台 IoT 服务界面导入上一步生成的 CA 证书,设备预置证书。

这里我们选择了在多账户模式下注册 CA,该方式只需要上传 CA 证书即可,无需提供验证证书或访问根证书私有密钥的权限。但该模式要求设备连接到 AWS IoT Core 的时候发送 Server Name Indication (SNI) extension。如果我们的设备不支持发送 SNI 扩展的话,请选择在单个账户模式下注册 CA,该方式需要我们使用 CA 证书,私钥来生成验证证书以确认用户拥有 CA 证书的控制权。

接下来我们导入设备预置证书。

  1. 创建一个 IoT policy 以限制预置证书的权限,并关联该 policy 到设备预置证书。

请注意该策略中有一处需要修改,策略里面的 templateName 需要替换为实际的 fleet provisioning 模板名字,目前我们还没有创建模板,暂时以 templateName 代替,等后面我们创建好模板后,会再来修改该策略,具体策略文档如下:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": ["iot:Connect"],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": ["iot:Publish","iot:Receive"],
            "Resource": [
                "arn:aws:iot:*:*:topic/$aws/certificates/create/*",
                "arn:aws:iot:*:*:topic/$aws/certificates/create-from-csr/*",
                "arn:aws:iot:*:*:topic/$aws/provisioning-templates/templateName/provision/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "iot:Subscribe",
            "Resource": [
                "arn:aws:iot:*:*:topicfilter/$aws/certificates/create/*",
                "arn:aws:iot:*:*:topicfilter/$aws/certificates/create-from-csr/*",
                "arn:aws:iot:*:*:topicfilter/$aws/provisioning-templates/templateName/provision/*"
            ]
        }
    ]
}

关联该策略到设备预置证书。

  1. 再次创建一个 IoT policy,目的是当 AWS IoT Core 为设备分配唯一证书时,能够自动为设备唯一证书关联该 policy,使设备唯一证书能够具有相应的访问权限。

这里配置的策略文档允许设备连接到 AWS IoT Core,且只允许订阅前缀为${iot:ClientId}/的 topic,另外允许向前缀为${iot: ClientId}的 topic 发送和接收消息。${iot:ClientId}是 AWS IoT Core 服务的策略变量,它的值为设备连接到 AWS IoT Core 时指定的 client id,该值可以设置为设备的序列号,保证每个设备都是独一无二的,具体变量信息可以查看策略变量文档。该策略里的 Resource 可以根据实际情况进行修改,可以添加其他需要使用的 topic,也可以直接使用 * 来表示所有的 topic,json 策略文档如下:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Receive"
      ],
      "Resource": "arn:aws:iot:*:*:topic/${iot:ClientId}/*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": "arn:aws:iot:*:*:topicfilter/${iot:ClientId}/*"
    }
  ]
}
  1. 创建 fleet provisioning 配置模板。

这里的陈述证书就是我们在第二步导入的设备预置证书。

AWS IoT Core 为用户提供了一个管理事物的注册表,事物是 AWS IoT Core 对特定设备或逻辑实体的表示形式。使用 AWS IoT Core 提供的注册表可让您在云端更轻松地管理和搜索设备。这里我们设置了事物名称前缀 ProductA-,这样每个设备连接到 AWS IoT Core 时,控制台上对应的事物名称就具有了该前缀,便于我们后续管理相关的设备。

这里我们增加了一个可选的配置,创建并关联了一个事物组。事物组允许我们通过将事物分组来同时管理多个事物。

修改第 3 步创建的 device-fleet-provisioning-policy 策略,将之前策略中的 templateName 更改为上面创建的模板名字 device-fleet-provisioning-template。

  1. 在 Ubuntu 22.04 测试机器上交叉编译 AWS IoT Core Embedded C SDK 代码样例。

大部分嵌入式设备使用的 Linux 系统都是精简后甚至是芯片厂商编译的最小化的 Linux 操作系统,该操作系统缺乏编译相关的软件包及依赖,并且嵌入式设备的硬件计算资源有限,编译一个简单的程序需要花费大量的时间,因此开发人员经常需要跨平台开发,提高开发效率,简化开发流程。该步骤使用 x64 机器来进行交叉编译。

首先安装 ARM 交叉编译软件包和相关的依赖软件包,本文安装了 gcc-arm-linux-gnueabi,如果您的 ARM CPU 具有 fpu 浮点计算单元,也可以使用 gcc-arm-linux-gnueabihf 编译软件包。当然如果是 64 位的 ARM CPU,则可以使用 gcc-aarch64-linux-gnueabi 编译软件包,只需要将后续命令中的 arm-linux-gnueabi 进行相应的替换即可。

apt update && apt install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi make cmake libssl-dev

该 SDK 测试样例要求 OpenSSL 1.1.0 及以上依赖,所以我们需要先交叉编译 OpenSSL,这里我使用 OpenSSL 最新版本 3.3.0。

git clone https://github.com/openssl/openssl.git
cd openssl

如果是 aarch64 架构,则下面的命令应为./Configure linux-aarch64 –cross-compile-prefix=aarch64-linux-gnu- shared –prefix=/usr/aarch64-linux-gnu/

./Configure  linux-armv4 --cross-compile-prefix=arm-linux-gnueabi-  shared --prefix=/usr/arm-linux-gnueabi/
make -j 2 && make install		# 这里的 2 为编译 job 数量,数量越多,速度越快,可以设置为 CPU 核数。

此时在/usr/arm-linux-gnueabi/lib/目录下已经存在了 openssl 的共享库文件 libssl.so.3,证明 OpenSSL 编译成功。

接下来开始编译样例代码,下载 AWS IoT Core Embedded C SDK。

git clone --recurse-submodules https://github.com/aws/aws-iot-device-sdk-embedded-C.git

修改 demos/fleet_provisioning/fleet_provisioning_with_csr/demo_config.h 文件,根据自己账号实际情况进行配置。这里 AWS_IOT_ENDPOINT 可以在 AWS IoT Core 控制台的左下角设置界面获取,PROVISIONING_TEMPLATE_NAME 配置为我们第 5 步创建的模板名称,DEVICE_SERIAL_NUMBER 生产环境请配置为实际的设备序列号,CSR_SUBJECT_NAME 可以配置 CN 的值也为实际设备序列号,具体配置样例如下:

File: demo_config.h

#define AWS_IOT_ENDPOINT "xxxx-ats.iot.ap-northeast-1.amazonaws.com"
#define CLAIM_CERT_PATH "certificates/provisoning.crt"
#define CLAIM_PRIVATE_KEY_PATH "certificates/provisoning.key"
#define PROVISIONING_TEMPLATE_NAME "device-fleet-provisioning-template"
#define DEVICE_SERIAL_NUMBER "1234567890"
#define CSR_SUBJECT_NAME "CN=1234567890"

同上一步修改 demos/mqtt/mqtt_demo_mutual_auth/demo_config.h 文件如下所示,后面我们会使用编译好的 mqtt_demo_mutual_auth 程序使用设备获取的唯一证书连接到 AWS IoT Core 服务:

#define AWS_IOT_ENDPOINT "xxxx-ats.iot.ap-northeast-1.amazonaws.com"
#define CLIENT_CERT_PATH "certificates/device.crt"
#define CLIENT_PRIVATE_KEY_PATH "certificates/device.key"
#define CLIENT_IDENTIFIER    "1234567890"

复制设备预置证书和私钥到 demos/certificates/ 目录下。

回到 aws-iot-device-sdk-embedded-C 目录下,进行配置

mkdir build && cd build

创建交叉编译工具链文件 arm-toolchain.cmake

touch arm-toolchain.cmake

File:  arm-toolchain.cmake

SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabi-gcc)

开始编译

cmake .. -DCMAKE_TOOLCHAIN_FILE=./arm-toolchain.cmake
make -j 2

如果出现了 multiple definition of `expirationLen’ 错误,则修改 demos/http/common/include/http_demo_s3_utils.h 文件中的 size_t expirationLen 为extern size_t expirationLen,再重新执行 make 命令即可。

执行成功后我们可以看到在 build/bin 目录下已经生成了我们需要的可执行文件。

  1. 移植样例二进制程序和相关库文件等到 32 位 ARM 开发板上。

复制 build 目录下的 bin 目录,lib 目录,以及/usr/arm-linux-gnueabi/lib 目录到 ARM 开发板上。 /usr/arm-linux-gnueabi/lib 目录下的 ld-linux,libcrypto,libssl 和 libatomic 相关的库文件是 AWS IoT Core 二进制程序实际需要的库文件,只复制这些文件也可以。

复制 arm-linux-gnueabi/lib 和 lib 目录下的文件到当前嵌入式系统的 /lib 目录下。这里我使用的精简嵌入式 Linux 系统不支持使用 LD_LIBRARY_PATH 环境变量设置共享库路径,如果您使用的系统支持该环境变量使用,则直接执行 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/share/iot-core-arm-bin/lib:/mnt/share/iot-core-arm-bin/arm-linux-gnueabi/lib 即可,请替换变量中对应的库文件目录为实际目录。

  1. 执行 fleet provisioning 样例程序获取设备证书和私钥,并执行 mqtt_demo_mutual_auth 测试连接程序。执行前请确保该嵌入式系统的日期和时间保持正确。

可以看到我们已经成功连接到 AWS IoT Core 服务并且已经获取到了设备唯一证书和私钥了。corePKCS11_Certificate.dat 就是 DER 格式的设备唯一证书,corePKCS11_Key.dat 则是 DER 格式的设备私钥。我们需要把这两个文件转换为 PEM 类型格式的证书和私钥,以便后续的 mqtt_demo_mutual_auth 二进制文件使用。实际生产系统中我们可以直接修改样例代码,在代码中完成格式转换,并直接将证书文件和私钥改名为 device.crt 和 device.key 并存储到 bin/certificates 目录下。这里的 device.crt 和 device.key 文件名跟第 6 步中 demos/mqtt/mqtt_demo_mutual_auth/demo_config.h 文件中的配置保持一致。转换命令如下:

openssl x509 -inform DER -outform PEM -in corePKCS11_Certificate.dat -out device.crt
openssl ec -inform DER -outform PEM -in corePKCS11_Key.dat -out device.key

移动 device.key 和 device.crt 文件到 bin/certificates 目录下。

执行 mqtt_demo_mutual_auth 程序,结果如下。

另外从控制台上我们也可以看到已经自动生成了对应的证书,物品等。

至此本次的演示就已经成功完成了。

总结

在本篇博客中,我们介绍了如何使用 AWS IoT Core fleet provisioning 功能来自动化的为大规模的嵌入式 Linux 设备分配设备唯一证书和私钥,并接入到 AWS IoT Core 服务。同时我们也展示了如何针对 AWS IoT Core 的 embedded C SDK 进行交叉编译,在 x64CPU 架构机器上生成 32 位 ARM CPU 的可执行二进制程序。

本篇作者

张春明

亚马逊云科技解决方案架构师,对于操作系统,网络,数据库,安全都有深入的了解。他帮助客户设计云上解决方案,并致力于解决客户使用过程中遇到的各种疑难杂症,加速客户云上业务构建。在业余时间,他喜欢学习新技术、听歌、阅读小说、骑行和滑雪等。他信奉的人生格言是人生苦短,何妨一试。