FIT Image 构建、配置与验证完整实操指南:从 ITS 文件编写到 U-Boot 验签的安全启动全链路
FIT Image 构建、配置与验证完整实操指南从 ITS 文件编写到 U-Boot 验签的安全启动全链路一、镜像被篡改的风险与安全启动的刚需嵌入式 Linux 设备的固件完整性是系统安全的第一道防线。攻击者如果能修改存储在 Flash 中的内核镜像或设备树就能注入 rootkit、替换文件系统或窃取设备密钥。传统的二进制镜像如zImage、uImage不包含任何完整性校验信息——bootloader 只是从固定地址加载、跳转执行不检查镜像是否被篡改。FIT ImageFlattened Image Tree是 U-Boot 引入的一种容器化镜像格式解决了传统单镜像方案的三个核心缺陷镜像完整性校验通过 RSA/SHA256 数字签名验证镜像来源和完整性。多组件打包将内核、设备树、ramdisk、FPGA 比特流等组件打包为单一文件统一管理。板级配置管理通过 configuration 节点同一个 FIT Image 可以为不同板卡加载不同的内核/设备树组合。本文从 ITSImage Tree Source文件的编写开始覆盖 mkimage 工具打包、签名注入和 U-Boot 验签验证的完整流程以及签名密钥管理和生产部署的最佳实践。二、FIT Image 的文件结构与签名验证原理FIT Image 在格式上是 FDTFlattened Device Tree的一个特定应用——它使用设备树的节点结构来描述镜像包含的组件但根节点不再是/而是/images和/configurations。flowchart TB subgraph 构建阶段 (Host端) A[内核 zImage] -- M[mkimage -f boot.its] B[设备树 .dtb] -- M C[ramdisk.cpio.gz] -- M D[RSA私钥 .key] -- M M -- E[fitImagebr/含签名节点] end subgraph 验证阶段 (目标板 U-Boot) E -- F[U-Boot: bootm 命令] F -- G{检查 required 属性} G --|requiredconf| H[验证配置签名] G --|requiredimage| I[验证各组件签名] H -- J[RSA公钥验证签名] I -- J J --|验签通过| K[加载内核DTBRAMDisk] J --|验签失败| L[停止启动 / 进入恢复模式] K -- M1[启动 Linux 内核] end subgraph 密钥管理 D -- N[私钥: 离线HSM存储] O[RSA公钥.crt] -- P[编译进 U-Boot dtbbr/或存储在 OTP/eFuse] P -- J end style E fill:#e8f5e9 style L fill:#ffcdd2 style K fill:#c8e6c9FIT Image 的签名验证流程是U-Boot 的bootm命令在加载镜像前遍历 FIT 内部节点对每个标记了required conf的 configuration 节点或required image的 image 节点执行签名验证。验证过程如下使用 FIT 镜像内嵌的 RSA 公钥或 U-Boot 设备树中编译的公钥初始化验证器。计算被签名区域的 SHA256 哈希值。签名区域排除了签名节点本身避免循环依赖。使用 RSA 公钥验证签名字段——如果解密后的哈希值与计算的哈希值一致则验证通过。如果任一required节点的验证失败bootm返回错误并停止启动。关键的工程细节FIT 在签名计算时会自动清零签名节点内的value属性用 0x00 填充然后对整个镜像做哈希。这意味着签名字段本身不参与哈希计算——这是签名验证算法的标准做法。三、从 ITS 编写到镜像签名的完整实现Step 1: 编写 ITS 文件/* * boot.its - FIT Image Source 文件 * * 语法: 基于设备树源文件.dts格式 * 根节点: / 下包含 images 和 configurations 两个子节点 * * 注意: ITS 文件使用设备树语法但不是完整的 .dts * 不支持 /include/ 和宏定义需通过 C 预处理扩展 .its → .its */ /dts-v1/; / { description Secure Boot FIT Image for i.MX8M Plus; #address-cells 1; /* * images 节点: 定义所有包含的二进制组件 * 每个子节点代表一个可加载的镜像 */ images { /* * 内核镜像 * arch/arm64: ARM 64-bit 架构标识 * os: 操作系统类型用于 bootm 正确初始化 * compression: 内核压缩方式none/gzip/lzo/lz4/zstd * load: 内核加载地址物理地址必须与内存映射一致 * entry: 内核入口点地址通常与load相同 * hash-1: 哈希校验节点algorithmsha256 * signature-1: 签名节点算法rsa2048, 密钥名dev_key */ kernel { description Linux Kernel 5.15.71; data /incbin/(arch/arm64/boot/Image.gz); type kernel; arch arm64; os linux; compression gzip; load 0x40480000; /* i.MX8M Plus DDR 起始 */ entry 0x40480000; hash-1 { algo sha256; }; }; /* * 设备树镜像 * 可以有多个 .dtb 对应不同板卡版本 */ fdt-imx8mp-evk { description i.MX8MP EVK Device Tree; data /incbin/(arch/arm64/boot/dts/freescale/imx8mp-evk.dtb); type flat_dt; arch arm64; compression none; hash-1 { algo sha256; }; }; fdt-imx8mp-navq { description i.MX8MP NavQ Device Tree; data /incbin/(arch/arm64/boot/dts/freescale/imx8mp-navq.dtb); type flat_dt; arch arm64; compression none; hash-1 { algo sha256; }; }; /* * initramfs可选 * 用于执行早期用户空间初始化 */ ramdisk { description Initial Ramdisk; data /incbin/(initramfs.cpio.gz); type ramdisk; arch arm64; os linux; compression gzip; hash-1 { algo sha256; }; }; }; /* * configurations 节点: 定义不同的启动配置组合 * 每个配置指定使用哪个内核 设备树 ramdisk * * default: 默认配置bootm 不指定配置名称时使用 * signature-1: 对该配置节点的签名 * required conf 表示签名必须验证通过才允许加载 */ configurations { default conf-imx8mp-evk; conf-imx8mp-evk { description i.MX8MP EVK Board Boot; kernel kernel; fdt fdt-imx8mp-evk; ramdisk ramdisk; hash-1 { algo sha256; }; /* * signature-1: 签名节点 * algo: RSA算法和密钥长度 * key-name-hint: 签名密钥标识U-Boot用此名称查找公钥 * sign-images: 需要签名的子镜像列表 * required: conf表示此配置需要签名验证 * - image: 仅需各image单独签名 * - conf: 需要configuration级别的签名 */ signature-1 { algo sha256,rsa2048; key-name-hint dev_key; sign-images kernel, fdt-imx8mp-evk, ramdisk; required conf; }; }; conf-imx8mp-navq { description i.MX8MP NavQ Board Boot; kernel kernel; fdt fdt-imx8mp-navq; signature-1 { algo sha256,rsa2048; key-name-hint dev_key; sign-images kernel, fdt-imx8mp-navq; required conf; }; }; }; };Step 2: 生成 RSA 密钥对# 生成 RSA 2048 位私钥开发密钥 # 生产环境中私钥必须存储在 HSM 中绝对不能提交到代码仓库 openssl genpkey -algorithm RSA \ -out dev_key.key \ -pkeyopt rsa_keygen_bits:2048 \ -pkeyopt rsa_keygen_pubexp:65537 # 从私钥提取公钥证书X.509 DER格式U-Boot要求 openssl req -new -x509 -key dev_key.key -out dev_key.crt \ -days 3650 \ -subj /CNFIT Image Signing Key/OEmbedded Product/STGuangdong # 查看密钥信息 openssl rsa -in dev_key.key -text -noout | head -20Step 3: 使用 mkimage 构建并签名# mkimage 构建 FIT Image # -f boot.its: 指定 ITS 文件 # -r: 要求在 ITS 中对所有 image 都定义哈希防止遗漏 # -K u-boot.dtb: 将公钥嵌入到 U-Boot 设备树中用于目标板验签 # 如果目标板使用独立的公钥存储如 OTP/eFuse可以省略 -K mkimage -f boot.its -r -K u-boot.dtb boot.fit # 查看生成的 FIT Image 结构 mkimage -l boot.fit # 典型输出: # FIT description: Secure Boot FIT Image for i.MX8M Plus # Created: Wed Jul 01 10:30:45 2026 # Image 0 (kernel) # Description: Linux Kernel 5.15.71 # Type: Kernel Image # Compression: gzip compressed # Data Size: 8392704 Bytes # Architecture: AArch64 # OS: Linux # Load Address: 0x40480000 # Entry Point: 0x40480000 # Hash algo: sha256 # Hash value: a3f1b2c4d5e6... # Image 1 (fdt-imx8mp-evk) # ... # Configuration 0 (conf-imx8mp-evk) # Sign algo: sha256,rsa2048:dev_key # Sign value: 3045... (256字节RSA签名) # 验证签名使用公钥验证 # fit_check_sign 可以离线检查签名是否有效 fit_check_sign -f boot.fit -k dev_key.crtStep 4: U-Boot 目标板验签配置在 U-Boot 源码中需要启用以下配置宏来支持 FIT 签名验证/* * U-Boot defconfig 中启用以下选项 * 路径: configs/imx8mp_evk_defconfig */ /* 基础安全启动支持 */ CONFIG_FITy CONFIG_FIT_SIGNATUREy /* FIT Image 签名验证 */ CONFIG_FIT_SIGNATURE_MAX_SIZE0x10000000 /* 签名数据缓冲区最大 256MB */ CONFIG_FIT_VERBOSEy /* 详细的验证日志 */ /* 加密算法支持 */ CONFIG_RSAy /* RSA 非对称加密 */ CONFIG_SHA256y /* SHA256 哈希 */ /* 公钥嵌入方式 */ CONFIG_FIT_SIGNATURE_STRICTy /* 严格模式: required 必须全部通过 */ /* * 公钥来源选择二选一: * 方案A: 嵌入 U-Boot 设备树推荐开发阶段 * → mkimage -K u-boot.dtb 将公钥注入 * 方案B: 存储在 OTP/eFuse 中推荐量产设备 * → 从 SoC 的安全存储区读取公钥 */ CONFIG_SPL_FIT_SIGNATUREy /* SPL 阶段也支持验签 */在 U-Boot 命令行验证# 加载 FIT Image 到内存 fatload mmc 0:1 ${loadaddr} boot.fit # 查看 FIT Image 信息不包含签名值 iminfo ${loadaddr} # 启动自动触发签名验证 bootm ${loadaddr} ## Loading kernel from FIT Image at 40480000 ... Using conf-imx8mp-evk configuration Verifying Hash Integrity ... sha256,rsa2048:dev_key OK Trying kernel kernel subimage ... Verifying Hash Integrity ... sha256 OK ## Starting kernel ... # 手动强制使用特定配置 bootm ${loadaddr}#conf-imx8mp-navq # 查看FIT内部结构 fdt addr ${loadaddr} fdt print /configurations四、安全启动的边界条件与密钥管理陷阱签名密钥泄露的连锁反应FIT Image 的签名安全完全取决于私钥的安全。一旦私钥泄露攻击者可以签名任意恶意镜像并使其通过验证。对于已部署的设备密钥泄露的影响是全局性的——所有出厂设备都需要通过 OTA 更新切换到新密钥。这对于部署量百万级的 IoT 设备召回成本极高。因此生产密钥管理的铁律是私钥绝不能离开 HSM硬件安全模块。开发阶段可以使用软件生成的密钥.key 文件但在进入生产编译流程前必须切换为 HSM 签名方案。流程调整为mkimage 生成未签名的 FIT Image → 通过安全通道发送到 HSM → HSM 内部签名 → 回传签名值 → mkimage 将签名注入 ITS。SHA256 哈希的碰撞风险当前广泛使用的 SHA256 理论上不存在已知的有效碰撞攻击。但这对嵌入式设备的长生命周期10-15 年来说还不够——量子计算的发展可能在设备生命周期内使 SHA256 的安全性降低。对于安全要求极高的场景如汽车 ECU、金融 POS 终端应选择 FIT 支持的sha256,rsa4096组合即使用 RSA 4096 SHA512。U-Boot 设备树中的公钥保护使用mkimage -K u-boot.dtb将公钥嵌入 U-Boot 设备树是开发阶段的常用方案但这意味着公钥存储在可读取的 Flash 中。如果攻击者拥有物理访问权限理论上可以替换 U-Boot 设备树中的公钥为自己的公钥然后为恶意镜像签名。对此的防御手段是SoC 安全启动链从 BootROM掩膜 ROM不可修改开始建立信任链BootROM 验证 SPLSPL 验证 U-Boot。eFuse 存储公钥哈希不存储完整公钥而是存储公钥的 SHA256 哈希值。U-Boot 加载公钥后计算哈希并与 eFuse 比对。Secure Enclave / TEE将验签逻辑放在 TrustZone 安全世界执行防止非安全世界的代码篡改验证流程。启动时间的影响FIT Image 签名验证增加了启动时间。RSA 2048 签名验证在 Cortex-A53 1.2GHz 下单次耗时约 2-5ms。如果 FIT 中有 3 个组件和 1 个配置都标记了 requiredconf总计 4 次 RSA 验证额外启动时间约 8-20ms。在典型的嵌入式 Linux 启动时间5-15 秒中这个开销可以接受。但在有严格冷启动时间要求如汽车后视摄像头必须在 2 秒内显示画面的场景中这个延迟需要纳入预算。五、总结FIT Image 的签名安全启动是一条从编译到部署的完整链路涉及六个关键环节ITS 文件编写定义 images 和 configurations 节点标记required conf的签名节点。密钥生成使用openssl genpkey生成 RSA 2048 私钥建议生产密钥使用 HSM。镜像构建mkimage -f boot.its -K u-boot.dtb一次性完成打包和签名。U-Boot 配置启用CONFIG_FIT_SIGNATURE、CONFIG_RSA、CONFIG_SHA256。公钥分发通过-K嵌入 U-Boot dtb 或存储在 OTP/eFuse。启动验证bootm命令自动执行签名验证失败则拒绝启动。工程实践中的红线是私钥安全——一旦泄露所有已部署的设备都存在被植入恶意固件的风险。生产环境必须使用 HSM 签名方案且建议在 SoC 层建立从 BootROM 开始的完整安全启动链防止单个环节的密钥被绕过。