ArduPilot Secure Boot 安全引导完全指南:密钥生成、签名固件构建与远程密钥管理 ArduPilot Secure Boot 安全引导完全指南密钥生成、签名固件构建与远程密钥管理【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot导读Secure Boot安全引导是 ArduPilot 为应对 RemoteID远程识别等高防篡改需求场景提供的一套固件签名与校验机制引导加载器内置最多 10 个公钥任何未经对应私钥签名的固件都会被拒绝启动。本文以 Tools/scripts/signing/README.md 为核心完整讲解从密钥生成、安全引导加载器构建、签名固件构建到 DFU 刷写、状态验证、回退恢复以及基于 MAVLink SECURE_COMMAND 的远程密钥管理的全流程并深入 Tools/scripts/signing 下的三个 Python 脚本与 libraries/AP_CheckFirmware 引导校验实现帮助厂商与开发者一次性掌握 ArduPilot 安全引导的落地细节。为什么需要 Secure BootArduPilot 是一个开放源码自动驾驶仪项目正常情况下任何人都可以编译固件并刷写到飞控上。但当设备用于需要高强度防篡改high levels of tamper resistance的合规场景时——典型代表是 RemoteID 远程识别——普通固件无法向监管方证明运行在飞控上的代码确实是厂商发布的版本。Secure Boot 的解决思路是密码学签名校验引导加载器中内置最多 10 个公钥固件使用对应的一个私钥签名引导加载器在启动固件前校验签名只要签名不匹配引导加载器中任何一个公钥就拒绝启动。也就是说篡改者即使拿到飞控、刷入自编译固件只要没有厂商私钥固件就无法运行。这为 RemoteID 等合规审计提供了可验证的信任根。整体工作流从源码仓库看Secure Boot 的完整链路由以下环节构成生成密钥对generate_keys.py 基于 Monocypher 库生成私钥与公钥文件构建安全引导加载器build_bootloaders.py 编译引导加载器再经 make_secure_bl.py 将公钥写入其中产物落盘到Tools/bootloaders/签名固件./waf configure --signed-fw配置启用签名固件构建最终由 make_secure_fw.py 用私钥对 APJ 固件签名刷写与验证通过地面站常规方式或 DFU 刷入安全引导加载器再加载签名固件远程密钥管理通过 MAVLink2 的 SECURE_COMMAND 消息如 MAVProxysecurecommand模块远程增删公钥甚至实现厂商间的设备移交。一、生成密钥对安装依赖签名工具链依赖pymonocypher且强制要求版本3.1.3.2。在 generate_keys.py 与 make_secure_fw.py 中都做了版本硬校验版本不符会直接退出python3 -m pip install pymonocypher3.1.3.2生成密钥Tools/scripts/signing/generate_keys.py NAME执行后将产生两个文件NAME_private_key.dat—— 私钥文件NAME_public_key.dat—— 公钥文件。NAME可以是任意字符串通常建议使用厂商名称它只影响本地文件名不参与任何密码学运算。密钥文件格式从 generate_keys.py 的实现可以看到密钥文件并非裸二进制而是带类型的文本格式私钥文件内容为PRIVATE_KEYV1:base64编码的32字节私钥公钥文件内容为PUBLIC_KEYV1:base64编码的32字节公钥。这里使用的算法是 Monocypher 的 EdDSA 签名体系monocypher.generate_key()生成私钥monocypher.compute_signing_public_key(private_key)推导公钥。make_secure_bl.py 和 make_secure_fw.py 在读取时会按_KEYV1:前缀校验密钥类型PRIVATE/PUBLIC不匹配会直接报Invalid key type退出。私钥保管要求私钥必须保存在安全位置。公钥只是用来制作只接受对应私钥签名固件的安全引导加载器泄露无妨而私钥一旦泄露攻击者即可签发出合法固件安全引导形同虚设。建议将私钥离线存放、严格访问控制绝不进入版本库或构建服务器明文目录。二、构建安全引导加载器构建命令Tools/scripts/build_bootloaders.py BOARDNAME --signing-keyNAME_public_key.dat该命令会完成以下工作见 build_bootloaders.py检查libraries/AP_HAL_ChibiOS/hwdef/board/hwdef-bl.dat是否存在不存在该文件的板卡没有引导加载器会被跳过以--bootloader模式配置并编译引导加载器将产物build/board/bin/AP_Bootloader.bin复制为Tools/bootloaders/BOARDNAME_bl.bin同时生成.elf调用make_secure_bl.py将公钥写入.bin与.elf通过Tools/scripts/bin2hex.py --offset 0x08000000生成对应的.hex文件。下一次构建该板卡固件时这个安全引导加载器会被自动打进 ROMFS后续可以走固件内嵌引导加载器刷写的方式升级。密钥校验build_bootloaders.py 对--signing-key传入的密钥文件做了两层校验文件必须真实存在必须使用公钥——文件名中带有private字样的私钥文件会被拒绝You must use the public key in the bootloader。注意--signing-key是actionappend参数可以多次使用以同时嵌入多个公钥例如Tools/scripts/build_bootloaders.py BOARDNAME \ --signing-keyNAME1_public_key.dat \ --signing-keyNAME2_public_key.dat默认包含 ArduPilot 官方密钥构建安全引导加载器时默认会同时嵌入 3 个 ArduPilot 官方公钥它们位于 Tools/scripts/signing/ArduPilotKeys/ArduPilot_public_key1.dat、ArduPilot_public_key2.dat、ArduPilot_public_key3.dat。make_secure_bl.py 会遍历该目录自动追加这些密钥。这样做的目的是当用户在 Secure Boot 设置过程中出现失误例如丢失私钥、误操作导致设备变砖时ArduPilot 核心开发团队可以用官方密钥签名固件帮助用户恢复设备也避免出现厂商倒闭/失联后用户无法再获得固件更新的锁死局面。如果确有充分理由不希望包含 ArduPilot 官方密钥可以追加参数Tools/scripts/build_bootloaders.py BOARDNAME \ --signing-keyNAME_public_key.dat \ --omit-ardupilot-keys公钥数量的硬限制make_secure_bl.py 定义了max_keys 10与key_len 32引导加载器中公钥区最多容纳 10 个 32 字节公钥超出会报Too many key files并退出。也就是说厂商密钥 ArduPilot 官方密钥合计不得超过 10 个。签名写入原理make_secure_bl.py 在引导加载器二进制中搜索 8 字节的签名描述符0x4ecf4ea5a6b6f729从描述符之后的位置开始用若干个 32 字节公钥替换原占位数据将公钥表直接烧录进引导加载器镜像。因此引导加载器固件必须在设计时预留出这块密钥区链接脚本 / 描述符结构这也是下文支持板卡一节中要求至少 32KB 引导加载器 Flash 空间的根本原因。三、构建签名固件方法一先构建后签名推荐用于发布以 Copter 固件为例./waf configure --board BOARDNAME --signed-fw ./waf copter ./Tools/scripts/signing/make_secure_fw.py build/BOARDNAME/bin/arducopter.apj NAME_private_key.dat--signed-fw配置项在 chibios_hwdef.py 中定义它让固件构建流程为签名做好准备应用描述符结构启用签名模式。最后一步make_secure_fw.py用私钥对 APJ 固件完成签名。方法二构建参数内置私钥适合开发迭代./waf configure --board BOARDNAME --signed-fw --private-key NAME_private_key.dat ./waf copter --upload这种方式把签名步骤并入waf构建链可以一条命令完成构建 签名 上传适合日常开发调试。签名原理make_secure_fw.pymake_secure_fw.py 的执行流程全部细节都可在源码中核对读取 APJ 文件其本质是 JSONimage字段是zlib 压缩 base64 编码的固件镜像第 41-47 行在解压后的镜像中搜索 8 字节的APP_DESCRIPTOR0x41a3e5f265699207找到固件中的应用描述符位置第 62-68 行把镜像拆成描述符前、后两部分用monocypher.signature_sign(private_key, flash12)对除描述符外的全部固件内容做 EdDSA 签名第 70-74 行用struct.pack(IQ64s, ...)将签名打包进描述符4 字节签名长度72、8 字节签名版本30437、64 字节签名本体第 79-83 行。签名版本号的存在是为了将来升级签名体系时保持向后兼容重新压缩回写 APJ并置signed_firmware true标志第 89-96 行。引导加载器一侧的校验逻辑与之严格对应AP_CheckFirmware.cpp 同样检查signature_length 72、比对sig_version 30437然后用引导加载器中的每个公钥尝试验签任何一个公钥验签通过即放行。加载签名固件签名后的固件含内嵌的安全引导加载器可以像普通固件一样通过地面站加载MissionPlanner 的Load custom firmware功能Linux 下使用 Tools/scripts/uploader.py。四、刷写安全引导加载器将安全引导加载器写入板卡有两种方法。方法一通过 ROMFS 内嵌引导加载器升级最简按上一节步骤构建好签名固件后签名固件的 ROMFS 中已经包含安全引导加载器。之后按常规引导加载器升级流程操作即可向飞控发送 MAVLink 命令让运行中的固件把 ROMFS 内嵌引导加载器刷入引导区。具体可参考 ArduPilot 官方的 bootloader 升级文档MissionPlanner / QGroundControl / MAVProxy 均有对应操作指引。方法二DFU 模式刷写如果板卡的hwdef.dat与hwdef-bl.dat中启用了ENABLE_DFU_BOOT选项且板卡基于STM32H7地面站就可以让板卡进入 DFU 模式随后用任意支持 DFU 的客户端将引导加载器.bin文件刷写到地址0x08000000。重要限制如果飞控上已经在运行安全引导加载器它会拒绝切换到 DFU 模式防止通过 DFU 绕过签名检查直接改写引导区。因此 DFU 方式只适合尚未启用安全引导或已回退到普通引导的板卡。五、如何确认正在使用安全引导安全引导加载器在 USB 枚举时会在产品名中加入Secure字样。Linux断开 USB 后执行sudo dmesg -w再插入 USB终端中会出现类似如下行Product: BOARDNAME-Secure-BL-v10Windows在设备管理器中查看引导加载器运行时的设备属性找到 Bus reported device description其中同样包含上述 Secure 字符串。需要注意该 Secure 字符串只出现在引导加载器阶段USB 设备名中的版本号v10即引导加载器版本标识若想延长停留在引导加载器的时间以便观察可以刷入一个普通未签名固件——安全引导加载器会因签名校验失败而永远停在引导加载器不会启动固件正好方便查看 USB 描述符。六、回退到普通引导模式Secure Boot 一旦启用引导加载器只接受签名固件。要恢复普通模式需要同时做两件事换回普通引导加载器清空引导加载器中的公钥。步骤 1准备普通引导加载器将Tools/bootloaders/BOARDNAME_bl.bin替换为普通未启用安全引导的引导加载器文件。步骤 2MAVProxy 连接并清空公钥用 MAVProxy 连接飞控加载SecureCommand模块并用私钥建立安全会话module load SecureCommand securecommand set private_keyfile NAME_private_key.dat securecommand getsessionkey建立安全会话后先查询当前公钥数量再全部移除securecommand getpublickeys securecommand removepublickeys 0 X其中X是getpublickeys返回的公钥数量。例如标准固件包含 3 个 ArduPilot 公钥加 1 个自有公钥则X 4。移除后再次执行securecommand getpublickeys确认返回 No public keys。这是设计上的关键点当引导加载器中没有任何公钥时ArduPilot 会无条件接受所有 SECURE_COMMAND并且允许引导未签名固件对应源码 AP_CheckFirmware.cpp 的all_zero_public_keys()判定逻辑。步骤 3刷入带普通引导加载器的固件退出 MAVProxy用--signed-fw选项构建仍按签名固件流程但此时 ROMFS 内嵌的是普通引导加载器./waf configure --board BOARDNAME --signed-fw ./waf copter --upload步骤 4升级引导加载器按常规方式更新引导加载器MAVProxy 中直接执行flashbootloader至此板卡恢复普通引导模式可以正常加载和运行未签名固件包括刚刷入的这份固件。源码侧的防呆保护值得注意的是固件在允许刷写新引导加载器之前会做安全检查AP_CheckFirmware_secure_command.cpp 的check_signed_bootloader()规定——如果当前引导加载器已配置公钥则不允许刷入一个不含公钥的引导加载器除非先清空当前公钥。这一机制避免了安全引导设备误刷普通引导加载器导致安全机制被静默移除的失误。七、支持的板卡Secure Boot 要求引导加载器拥有至少 32KB 的 Flash 空间用于存放公钥表与校验逻辑原生支持所有基于STM32H7与STM32F7的板卡其他较老板卡可以自行修改hwdef.dat与hwdef-bl.dat为引导加载器增加更多 Flash 空间后同样可以使用。决定板卡是否有引导加载器构建的判据是libraries/AP_HAL_ChibiOS/hwdef/board/hwdef-bl.dat是否存在build_bootloaders.py。作为参考仓库 CI 脚本 build_ci.sh 对CubeOrange-ODID与MatekL431-DShot两个板卡执行了完整的配置--signed-fw 私钥签名 构建安全引导加载器验证流程可作为落地时的样板。八、通过 MAVLink 远程管理公钥SECURE_COMMAND如果持有引导加载器中某个公钥对应的私钥就可以通过MAVLink2 的 SECURE_COMMAND 消息远程更改公钥甚至移除全部公钥以允许加载未签名固件。这为远程恢复误锁设备厂商间移交设备管理权提供了通道。支持的操作MAVProxy1.8.55 及以上版本内置securecommand模块提供生成会话密钥用于远程更新的安全会话建立获取当前公钥列表设置新的公钥作为追加或替换移除全部公钥。预期未来版本的 MissionPlanner 也会内置功能相同的插件。底层实现从源码看SECURE_COMMAND 的处理链路是完整闭环的地面站消息进入 GCS_Common.cppMAVLINK_MSG_ID_SECURE_COMMAND被转发给AP_CheckFirmwareAP_CheckFirmware_secure_command.cpp 的handle_secure_command()在验签通过后分发到四种操作SECURE_COMMAND_GET_SESSION_KEY生成会话密钥SECURE_COMMAND_GET_PUBLIC_KEYS按索引读取引导加载器首扇区中的公钥跳过全零槽位SECURE_COMMAND_SET_PUBLIC_KEYS写入新公钥校验索引与数量边界最多 10 个槽位SECURE_COMMAND_REMOVE_PUBLIC_KEYS用全零数据清空指定槽位公钥区通过find_public_keys()定位——它假定公钥存放在引导加载器的第一个 Flash 扇区AP_CheckFirmware_secure_command.cpp由链接脚本保证该假设成立。厂商间移交场景利用 SECURE_COMMAND 与 MAVLink 转发MAVLink forwarding组合可以实现将一架飞行器的管理权从一家厂商移交给另一家厂商先由当前厂商用其私钥建立安全会话删除自己的公钥、写入新厂商的公钥之后新厂商即可用自己的私钥正常签名并管理固件。整个过程无需物理接触设备。九、安全注意事项总结私钥即信任根NAME_private_key.dat必须离线安全保管泄露即意味着安全引导可被绕过公钥数量上限 10 个厂商密钥与 ArduPilot 官方密钥合计不超过 10 个默认含 ArduPilot 官方密钥这是刻意的兜底设计除非有非常明确的理由否则不建议使用--omit-ardupilot-keysDFU 与 Secure Boot 互斥运行安全引导加载器的板卡拒绝进入 DFU回退时必须先清空公钥再刷普通引导加载器签名工具链版本锁定pymonocypher必须为3.1.3.2密钥文件必须匹配_KEYV1:格式前缀签名版本号机制固件签名携带版本号30437AP_CheckFirmware.cpp为未来签名算法演进预留了升级空间。十、快速参考完整命令清单# 1. 安装签名依赖版本锁定 python3 -m pip install pymonocypher3.1.3.2 # 2. 生成密钥对 Tools/scripts/signing/generate_keys.py MYVENDOR # 3. 构建安全引导加载器默认附带 3 个 ArduPilot 官方密钥 Tools/scripts/build_bootloaders.py BOARDNAME --signing-keyMYVENDOR_public_key.dat # 4. 构建签名固件先构建后签名适合发布 ./waf configure --board BOARDNAME --signed-fw ./waf copter ./Tools/scripts/signing/make_secure_fw.py build/BOARDNAME/bin/arducopter.apj MYVENDOR_private_key.dat # 4. 或构建参数内置私钥构建 签名 上传一步完成适合开发 ./waf configure --board BOARDNAME --signed-fw --private-key MYVENDOR_private_key.dat ./waf copter --upload # 5. 验证安全引导状态Linux sudo dmesg -w # 插入 USB 后查找 Product: BOARDNAME-Secure-BL-v10围绕本文涉及的工具链仓库中还提供了 Tools/scripts/signing/ArduPilotKeys/ 官方公钥样例、CI 落地样板 Tools/scripts/build_ci.sh以及引导校验核心实现 libraries/AP_CheckFirmware可供进一步研读与复用。【免费下载链接】ardupilotArduPlane, ArduCopter, ArduRover, ArduSub source项目地址: https://gitcode.com/GitHub_Trending/ar/ardupilot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考