ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Arduino-ESP32 签名 OTA 安全更新实战:RSA/ECDSA 固件签名验证完整指南

Arduino-ESP32 签名 OTA 安全更新实战:RSA/ECDSA 固件签名验证完整指南 Arduino-ESP32 签名 OTA 安全更新实战RSA/ECDSA 固件签名验证完整指南【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32本文基于 Arduino-ESP32 仓库中libraries/Update/examples/Signed_OTA_Update/示例及其配套源码系统讲解如何在 ESP32 上实现带数字签名验证的 OTAOver-The-Air安全固件更新。读完后你将掌握如何用bin_signing.py生成 RSA/ECDSA 密钥对、如何对应用固件二进制签名并烧录、如何在 Arduino 草图中安装签名验证器installSignature以及签名二进制在设备端的完整校验链路mbedtls RSA-PSS / ECDSA 验证。1. 为什么需要签名 OTA安全模型与能力概览代码签名确保只有用你的私钥签过名的固件才会被设备接受。即使用户的攻击者接管了你的更新服务器没有私钥他也无法让设备运行恶意固件——这正是签名 OTA 保护的核心边界。示例文档libraries/Update/examples/Signed_OTA_Update/README.md声明的能力矩阵如下RSA 签名验证支持 RSA-2048、RSA-3072、RSA-4096ECDSA 签名验证支持 ECDSA-P256、ECDSA-P384多种哈希算法SHA-256、SHA-384、SHA-512自动签名验证OTA 更新过程中自动完成签名校验默认安全Secure by Default签名验证失败则更新直接失败不会启动未验证固件。前置条件安装cryptography包的 Python 3pip install cryptography带 Update 库的 ESP32 Arduino Core即本仓库。注意一个关键约束签名验证功能由编译宏UPDATE_SIGN控制。示例目录中的 build_opt.h 文件内容为-DUPDATE_SIGN构建系统会自动为这个示例附加该宏因此该示例无需额外配置即可启用签名验证而如果你想在自己的草图中使用这套 API需要自行确保定义了UPDATE_SIGN——Update.h 中installSignature()的声明和 Updater_Signing.h 整个头文件都被#ifdef UPDATE_SIGN包裹。2. 签名二进制文件格式固件 512 字节签名理解整个流程的关键在于签名文件的结构。查看 tools/bin_signing.py 的sign_binary()实现# Pad signature to max size (512 bytes for RSA-4096) max_sig_size 512 padded_signature signature b\x00 * (max_sig_size - len(signature)) # Write signed binary (firmware signature) with open(output_file, wb) as f: f.write(binary_data) f.write(padded_signature)也就是说签名后文件 原始固件字节 固定 512 字节签名区真实签名按方案不同只有 64~512 字节其余用 0x00 填充。这个 512 字节常数贯穿整条链路设备端Updater.cpp 中installSignature()将_signatureSize 512写死示例草图Signed_OTA_Update.ino 中const size_t signatureSize 512;并据此计算真实固件大小firmwareSize contentLength - 512验证时Updater.cpp 在end()阶段从分区偏移_size - 512处用ESP.partitionRead()把签名读回闪存再调用verify()。这个设计意味着Update.begin()必须传入固件 签名的总长度哈希只覆盖固件部分_size - 512字节签名本身不参与哈希。3. 快速上手六步完成签名 OTA以下流程完整继承自示例文档并结合源码补齐了每个步骤的底层行为。Step 1生成密钥对推荐使用 RSA-2048python ARDUINO_ROOT/tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem python ARDUINO_ROOT/tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem或者用更小的 ECDSA-P256签名小、验证快python ARDUINO_ROOT/tools/bin_signing.py --generate-key ecdsa-p256 --out private_key.pem python ARDUINO_ROOT/tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pemARDUINO_ROOT是你的 ESP32 Arduino 安装路径如~/Arduino/hardware/espressif/esp32/对应本仓库根目录下的 tools/bin_signing.py。工具支持的全部密钥类型为rsa-2048 / rsa-3072 / rsa-4096 / ecdsa-p256 / ecdsa-p384见 bin_signing.py 的 --generate-key 参数。一个容易忽略的细节--extract-pubkey除了输出public_key.pem还会自动生成同名.h头文件见 extract_public_key。该头文件把 PEM 公钥转成 C 数组并追加一个\x00结尾符供 mbedtls 的 PEM 解析器使用const uint8_t PUBLIC_KEY[] PROGMEM { 0x2d, 0x2d, /* ... PEM 字节 ... */ 0x00 }; const size_t PUBLIC_KEY_LEN 452;仓库中已预置了一份仅用于测试的 RSA-2048 公钥public_key.h文件内明确警告 THIS IS A TEST KEY - DO NOT USE IN PRODUCTION!生产环境必须替换为你自己生成的版本。重要妥善保管private_key.pem任何拿到它的人都能给你的设备签发合法固件。Step 2配置示例草图把生成的public_key.h复制到示例目录覆盖测试公钥打开 Signed_OTA_Update.ino修改 Wi-Fi 凭据const char *ssid YOUR_SSID; const char *password YOUR_PASSWORD;修改固件 URLconst char *firmwareUrl http://your-server.com/firmware_signed.bin;根据你的密钥类型取消对应宏注释草图第 56~63 行// 密钥类型二选一否则 #error 编译报错 #define USE_RSA // RSA 签名验证 //#define USE_ECDSA // ECDSA 签名验证 // 哈希算法三选一必须与签名时使用的 --hash 一致 #define USE_SHA256 // SHA-256推荐默认 //#define USE_SHA384 //#define USE_SHA512宏选择最终映射为HASH_SHA256/384/512Updater_Signing.h并构造UpdaterRSAVerifier或UpdaterECDSAVerifier对象。Step 3编译并上传初始固件编译并烧录该草图到 ESP32打开串口监视器115200 波特率确认运行正常。草图的setup()会连接 Wi-Fi、打印 IP然后直接执行一次performOTAUpdate()——这是一个上电即尝试更新的最简演示形态实际项目中应加版本比较或触发条件。Step 4构建并签名新固件修改草图比如加版本号重新构建并导出二进制Arduino IDESketch→Export Compiled Binary生成的 application.bin位于草图目录的build文件夹下例如build/espressif.esp32.esp32c6/Signed_OTA_Update.ino.bin。签名python ARDUINO_ROOT/tools/bin_signing.py --bin APPLICATION_BIN_FILE --key private_key.pem --out firmware_signed.bin如需其他哈希算法例如 SHA-384python ARDUINO_ROOT/tools/bin_signing.py --bin APPLICATION_BIN_FILE --key private_key.pem --out firmware_signed.bin --hash sha384从源码看签名方案是自动适配密钥类型的sign_binaryRSA使用RSA-PSS填充MGF1 与签名哈希相同salt_lengthPSS.MAX_LENGTH即key_len - hash_size - 2ECDSA标准 ECDSA输出 DER 编码签名。设备端验证端与之一一对应Updater_Signing.cpp 的 RSA 路径使用MBEDTLS_PK_SIGALG_RSA_PSS/mbedtls_pk_rsassa_pss_optionsexpected_salt_len key_len - hash_size - 2与 Python 侧PSS.MAX_LENGTH严格匹配ECDSA 路径第 198~243 行则先解析 DER ASN.1 序列头求出真实签名长度再调用mbedtls_pk_verify。Step 5托管签名固件将firmware_signed.bin上传到你的 Web 服务器使其可从 Step 2 配置的 URL 访问。Step 6执行 OTA 更新复位 ESP32 后设备将依次连接 Wi-Fi → 下载签名固件 → 验证签名 → 签名有效则应用更新 → 以新固件重启。4. 设备端实现剖析签名验证如何嵌入 Update 状态机示例草图的 performOTAUpdate() 展示了标准调用序列顺序至关重要// 1. 构造验证器公钥来自 public_key.h #ifdef USE_RSA UpdaterRSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, hashType); #elif defined(USE_ECDSA) UpdaterECDSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, hashType); #endif // 2. 必须在 Update.begin() 之前安装签名验证 if (!Update.installSignature(sign)) { /* 处理 Failed to install signature verification */ } // 3. 传入固件签名的总大小 Update.begin(contentLength); // 4. 流式写入 HTTP 响应 while (http.connected() written contentLength) { /* Update.write(...) */ } // 5. end() 触发签名验证 if (Update.end()) { if (Update.isFinished()) { ESP.restart(); } } else if (Update.getError() UPDATE_ERROR_SIGN) { Serial.println(SIGNATURE VERIFICATION FAILED!); }在 Updater.cpp 内部这套调用链对应的行为是阶段源码行为installSignature()保存验证器指针固定_signatureSize 512L240-L267begin(size)校验总大小不小于签名区size 512直接报错L319-L326并按验证器的getHashType()初始化SHA2Builder流式哈希write()数据分两路一路写 Flash一路喂给哈希但只哈希前_size - 512字节L798-L812签名字节不参与end()从分区固件尾部偏移处读回 512 字节签名调用verify()失败则置UPDATE_ERROR_SIGN并中止激活L1007-L1042验证器基类接口定义在 Updater_Signing.hclass UpdaterVerifyClass { public: virtual bool verify(SHA2Builder *hash, const void *signature, size_t signatureLen) 0; virtual int getHashType() const 0; };两个具体实现UpdaterRSAVerifier/UpdaterECDSAVerifier构造时即解析 PEM 公钥并校验密钥类型非 RSA/ECDSA 会置_valid false代码同时兼容 mbedtls 3.x 与 4.x 两套 API4.x 走 PSA 路径需先psa_crypto_init()见 Updater_Signing.cpp 第 25~35 行。5. 签名方案与哈希算法选型示例文档给出的签名方案对比表方案密钥长度签名长度验证速度安全强度RSA-20482048 bits256 bytes中等高RSA-30723072 bits384 bytes较慢很高RSA-40964096 bits512 bytes最慢最高ECDSA-P256256 bits64 bytes快高ECDSA-P384384 bits96 bytes快很高文档推荐RSA-2048 或 ECDSA-P256 在安全性与性能间取得良好平衡。签名长度常量可在 Updater_Signing.h 中核对RSA_2048_SIGNATURE_SIZE 256…ECDSA_P256_SIGNATURE_SIZE 64注意它们都小于设备端统一的 512 字节签名缓冲区短签名靠零填充对齐。哈希算法对比表算法输出长度速度安全强度SHA-25632 bytes快高SHA-38448 bytes中等很高SHA-51264 bytes中等很高文档推荐多数应用 SHA-256 已足够且为 Python 工具与设备端的默认算法。6. 安全管理建议与故障排查私钥管理来自文档 Security Considerations 一节绝不把私钥提交到版本控制加密存储或使用 HSM限制访问范围仅授权人员可接触考虑开发与生产环境使用不同密钥。推荐实践使用 HTTPS签名验证保护固件完整性与来源真实性而 HTTPS 抵御中间人攻击二者职责不同、应叠加使用密钥轮换定期轮换密钥注意轮换需要一次包含新公钥的固件更新来发布新公钥。故障排查速查Signature verification failed错误码UPDATE_ERROR_SIGN值为 14见 Update.h 第 32 行确认固件确实用匹配的私钥签名确认草图中公钥与签名私钥成对确认签名方案RSA/ECDSA与哈希算法在签名侧--hash参数和验证侧USE_SHA*宏一致确认签名二进制在传输过程中未被损坏。Failed to install signature verification检查installSignature()是否在Update.begin()之前调用确认哈希与签名对象初始化正确公钥 PEM 能被解析、PUBLIC_KEY_LEN与实际长度匹配。Public key parsing failed设备日志 Failed to parse RSA/ECDSA public key确认公钥 PEM 格式正确由--extract-pubkey生成的.h文件可直接使用其末尾 0x00 是 mbedtls 解析器所需确认PUBLIC_KEY_LEN与实际密钥长度一致。7. 进阶用法与 API 参考不烧录即可离线验证签名python ARDUINO_ROOT/tools/bin_signing.py --verify firmware_signed.bin --pubkey public_key.pem该命令从文件尾部切出 512 字节签名区去除零填充后按对应方案验签verify_signature 实现是发布流程中很好的 CI 检查点。使用非默认哈希算法签名侧与验证侧必须匹配# 签名侧SHA-384 python ARDUINO_ROOT/tools/bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin --hash sha384// 草图侧定义对应宏 #define USE_SHA384API 参考类UpdaterRSAVerifierRSA 签名验证器构造参数pubkey, pubkeyLen, hashTypehashType缺省HASH_SHA256UpdaterECDSAVerifierECDSA 签名验证器二者均继承UpdaterVerifyClass内部持有 mbedtlspk_context见 Updater_Signing.h。核心方法// 安装签名验证必须在 Update.begin() 之前调用 bool Update.installSignature(UpdaterVerifyClass *sign);相关错误码Update.hUPDATE_ERROR_SIGN (14)签名验证失败与之配合的还有UPDATE_ERROR_SPACE (4)OTA 分区剩余空间不足、UPDATE_ERROR_STREAM (6)流读取超时等可结合Update.errorString()定位问题。8. 小结Arduino-ESP32 的签名 OTA 方案把密钥管理放在离线侧Python 工具 私钥验证逻辑放在设备侧mbedtls Update 库这条边界切得很清晰bin_signing.py负责生成密钥、签名固件 512 字节填充签名Updater.installSignature()begin/write/end状态机负责流式哈希、尾部签名回读与 RSA-PSS/ECDSA 验证任何一环不匹配都会以UPDATE_ERROR_SIGN中止更新。按本文的六步流程你可以把这套未签名不启动的安全机制直接落地到自己的 ESP32 产品线上。【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表