尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

软件加密与硬件加密:嵌入式设备防抄板与固件保护实战解析

软件加密与硬件加密:嵌入式设备防抄板与固件保护实战解析 保护自家产品不被逆向、不被抄板、固件不被随意提取是很多嵌入式工程师迟早要面对的事。网上关于“硬件加密”和“软件加密”的讨论一直挺热闹但大部分说法都比较含糊比如有人说“软件加密就是容易被破解硬件加密就安全了”也有人说“加了AES就是加密用芯片就是硬件加密”。这两种说法都不算准确甚至会把项目带偏。这篇文章我想把这两者的区别讲透包括它们各自的原理、攻击模型、选型思路以及从密钥生成到产线烧录的落地步骤。内容主要面向做嵌入式开发、物联网设备、工控产品、消费电子的工程师或产品经理也适合刚接触安全设计、准备给产品加防护的团队参考。我尽量少用教科书式定义多讲实际项目里会遇到的问题比如为什么固件加密了还是被抄为什么一颗几块钱的加密芯片能挡住大部分攻击者以及什么时候其实软件加密完全够用。1. 先搞明白一件事软件加密和硬件加密根本不是同一个层面很多人把“软件加密”理解成“用软件实现加密算法”把“硬件加密”理解成“用硬件实现加密算法”然后对比谁快谁安全。这个认知不能说错但它混淆了两个完全不同的安全模型。1.1 软件加密算法在CPU上跑钥匙也在旁边所谓软件加密指的是加密运算由主控CPU或MCU通过执行代码来完成密钥以某种形式存放在设备里通常是Flash、eMMC、Nor Flash或外部存储中。这种方案的典型形态包括对固件文件进行AES解密、对通信数据进行TLS/SSL加密、对本地配置文件做加解密等。这类方案在开发上非常顺手因为不额外增加硬件成本几乎为零。比如很多产品用ESP32、STM32或GD32做主控里面跑一个软件AES配合简单的密钥混淆就能应付一般需求。问题在于软件加密的安全边界是“代码本身”。只要攻击者能拿到固件就能在本地逆向出密钥或算法流程加密等于白做。在实际项目里常见做法是把密钥硬编码在源码里这基本等于把家门钥匙放在门垫下面。稍微有点经验的逆向者用串口、JTAG或直接读取Flash镜像就能把固件拖出来分析。就算不硬编码密钥存在普通Flash里也等于明文因为Flash在物理上被读出后里面的数据对攻击者来说就是可见的。1.2 硬件加密把钥匙和锁放进保险柜硬件加密的核心不是“用硬件跑AES”而是“密钥存在于一个外部无法直接读取的安全区域内运算也在该区域内完成”。这个安全区域可能是一颗独立的加密芯片也可能是主控内部划出的安全岛比如TrustZone、HSM硬件安全模块或安全元件Secure Element。这类方案的关键点在于密钥从头到尾不出安全芯片攻击者即使拿到完整固件、完整的PCB图也无法通过读Flash或监听总线得到密钥。密钥只能在芯片内部参与运算外部只能拿到结果比如加密后的密文、签名值或认证通过的信号。这种“运算本地化、密钥不可读”的模型才是硬件加密和软件加密最本质的区别。举个例子用外部加密芯片做启动认证时主控向芯片发送一串随机数芯片内部用存储的密钥对随机数进行签名或生成MAC然后把结果返回给主控。主控验证通过后才继续运行。这个过程中密钥数据从未在I2C或SPI总线上出现过攻击者即使抓总线看到的也只是一串随机数和一个签名结果无法反推出密钥。1.3 芯片里的硬件加密引擎和“加密芯片”要分清还有一个常见的混淆点很多MCU内部自带硬件AES引擎、TRNG真随机数发生器或CRC模块商家宣传时会说“支持硬件加密”。从效率角度看这确实比纯软件跑AES快很多也不占CPU资源。但从安全模型上看如果密钥仍然存放在普通Flash中那么它本质上还是软件加密的安全等级只是加解密运算更快而已。真正的硬件加密通常指两类一是主控内部具备独立的安全子系统和密钥存储区且该区域对CPU本身也不直接开放比如通过OTP、eFuse或隔离内存来管理密钥二是外部挂一颗具备安全存储能力的独立加密/安全芯片密钥只存在于这颗芯片内部。我给客户做方案评估时通常会先问一个问题密钥被读走的风险有多大如果密钥放在普通Flash里那么无论算法跑得再快、用了多少位AES都应该视为软件加密方案只有当密钥被封闭在独立安全介质中才算是真正的硬件加密。这个判断标准比“是不是用了硬件AES”要准确得多。2. 从攻击者的视角看区别到底有多大判断安全方案靠不靠谱不能只看自己怎么设计还得把自己代入攻击者的位置捋一遍拿到产品后会用哪些手段拆解。你会发现软件加密和硬件加密在对抗中的表现差距非常大。2.1 固件提取第一步就决定成败对绝大多数嵌入式产品来说攻击者最常用的手段就是提取固件。方法很多比如拆下Flash芯片用编程器直接读或者通过烧录接口、BootROM漏洞、调试接口把固件导出再进行静态分析。软件加密方案在这一步基本就崩了。因为固件是完整可读的里面包含代码、常量表、配置信息。逆向工程师用IDA、Ghidra这类工具打开固件找到加密逻辑和密钥都比较直接。哪怕你在固件里做了层壳、做了自解密只要代码最终要在CPU上完整执行就一定有办法在运行过程中把内存或代码状态抠出来。我参与过的固件安全项目中遇到的很多案例都是客户说“我们固件是加密的”但实际只是对固件文件做了简单异或或AES密钥就写在源码里。攻击者不需要什么高级手段直接翻固件就能定位到密钥然后解开整个镜像。硬件加密方案则会明显增加这个环节的难度。如果密钥在独立安全芯片里攻击者提取的固件只是一堆密文或动态数据无法独立还原出完整的可执行逻辑。就算把Flash内容完整读出也缺少运行所需的密钥材料整个系统就没法复现。2.2 密钥存放位置决定安全的底线软件加密和硬件加密在密钥存放上的差异比很多人想象中更关键。软件加密的密钥通常以静态数据形式出现在Flash或外部存储中攻击者可以通过读Flash、借助调试接口dump内存拿到。某些方案里还喜欢把密钥拆成几段放在不同位置但这只是增加了搜索成本并没有改变“密钥最终以明文形态躺在存储介质里”的事实。硬件加密的密钥存放方式则完全不同。独立加密芯片内部有专用的安全存储区通常是一次性可编程OTP的写入后无法通过外部接口读出。主控SoC内部的安全岛也有类似的机制比如eFuse或专用安全RAMCPU运行在普通世界时无法访问这部分区域。我在评估方案时常会跟团队说一句话不要看加密算法有多强要看密钥是否处于“人无法直接触摸到”的状态。软件加密的密钥相当于放在桌上硬件加密的密钥相当于放进保险柜两者在物理安全性上的差距是数量级的。2.3 软硬件方案在攻击面下的对抗对比下面这张表是我在做安全评审时常用的对比框架能比较直观地看出两类方案在常见攻击路径下的表现攻击路径软件加密方案硬件加密方案读取Flash/eMMC提取固件密钥和密文同时暴露容易全盘失守密文可读但密钥不可得单独提取无意义调试接口注入或读取内存可能直接dump解密后的代码和密钥安全芯片不暴露密钥主控内部数据有限总线监听SPI/I2C/外部存储只要通信发生在外部容易截获明文或密钥密钥不出芯片总线只有数据或认证结果物理剖片/聚焦离子束分析开销极高通常不会用于普通消费产品可抵抗大部分商业级物理攻击绕过认证逻辑攻击者可用脚本直接跳过校验每一步关键操作都需要芯片参与跳过难度大需要注意的是最后一行“绕过认证逻辑”在两类方案中都有可能发生。如果主控软件本身没有做好校验分级比如只在启动开头认证一次之后所有操作都默认可信那么攻击者即使没有破解芯片也能通过补丁或修改控制流来绕过保护。这是产品设计时必须考虑的问题不能把宝全部押在加密芯片上。3. 真正做产品选型时我一般这样权衡聊完攻击模型回到实际项目里该怎么选方案我的经验是不要一上来就讨论用哪颗芯片而是先把产品形态、安全预算、开发周期和量产条件摆出来再确定安全层级。3.1 产品形态决定安全预算不同产品对安全的需求差异很大。一个消费级智能灯泡被抄板后最坏结果就是多出几个仿制品损失有限一套工业控制器或医疗设备如果固件泄露甚至被篡改可能会引发严重售后甚至安全问题。前者可能软件加密就够后者就需要认真考虑硬件加密。我的判断维度大致有三个产品单价和利润单价高、利润空间大的设备更容易被专业团队盯上硬件加密成本摊销后可以接受。生命周期和升级能力如果产品需要长期远程升级固件签名和认证是刚需单纯靠软件加密很难形成完整链路。被仿冒后的影响涉及安全、合规或品牌风险的产品建议直接上独立加密芯片或安全元素不要省那几块钱。比如做NVR或者是边缘网关这类设备产品本身就要存储大量配置和算法一旦被复制直接影响整套解决方案价值。我在RK3588这类平台做方案时通常会引导客户用SoC内部的安全启动TrustZone做基础防护再根据业务需求决定是否挂外部加密芯片。3.2 开发周期和量产流程也要算进去硬件加密听着安全但它对开发流程的影响比想象中要大。独立加密芯片虽然大部分都有成熟的SDK但你在项目里仍然要做密钥生成、烧录、生命周期管理、密钥备份、产线流转等一堆额外工作。如果团队没有相关经验这些动作很容易卡在量产阶段。软件加密则简单得多基本可以在固件工程里直接完成不额外增加硬件调试方便产线也不需要增加烧录步骤。对于小批量产品或原型验证阶段这无疑是最省事的选项。我建议团队在项目初期就做一次内部评估产线是否有能力执行密钥注入是否需要额外购买烧录器或授权工具产品返修时如何恢复密钥这几个问题如果回答不了硬件加密方案后续会很痛苦。实际项目中我见过不少团队把密钥管理想得太简单以为买几颗加密芯片就能一劳永逸。结果到了产线发现每台设备都需要单独烧录唯一密钥返修时还要重新匹配流程混乱到一度推迟交付。硬件加密不是买芯片就完事它是一整套设计变更。3.3 常见平台上的落地组合不同平台能提供的安全资源不一样选型时的组合方式也不同。如果你用的是普通MCU比如STM32、GD32这类Cortex-M内核产品芯片内部通常会有读保护、OTP、唯一ID部分型号带硬件AES。中低安全需求下可以先用读保护加唯一ID绑定固件防止直接通过调试接口读取Flash。高安全需求时建议外挂一颗独立加密芯片比如常见的SMEC98SP这类国产防抄板加密芯片通过I2C或SPI做认证和业务数据保护。如果平台是高性能SoC比如RK3588、瑞芯微其他系列或全志平台通常具备强化的安全启动、OTP密钥、TrustZone/ATF/OP-TEE这些机制。在这种平台上硬件加密不一定表现为外挂芯片更多是启用SoC内部安全世界能力让密钥只存在于安全内存中普通世界拿不到。ESP32这类Wi-Fi/蓝牙SoC也提供了Flash加密和安全启动功能密钥存放在eFuse中。虽然它的安全等级相比独立安全芯片还有差距但在大部分物联网场景里已经能挡住绝大多数攻击者关键是开发成本还不高。这里我有一个比较推荐的做法把安全需求分成“防普通人”和“防专业人士”两档。防普通人软件加密加适当混淆就够防专业人士必须上真正的硬件密钥存储方案并配合安全启动、签名校验、动态认证等多层机制。4. 落地部署的关键步骤从密钥生成到产线烧录不管选哪种方案密钥管理都是整个安全设计里最容易被忽略、也最容易出事故的环节。我在这里把一套比较通用的落地流程拆开讲很多步骤可以直接抄到项目里。4.1 先建立一条可信的启动与密钥分级链安全设计不能只在某一步做加密而是要从启动开始建立信任链。以带安全SoC的设备为例典型流程是BootROM先校验Bootloader签名Bootloader再校验内核和文件系统签名每一级都使用上一级信任的密钥来验证下一级。在这个过程中密钥分级非常重要。最底层的根密钥通常固化在芯片eFuse或OTP里它不参与业务数据加解密只用来验证Bootloader或派生下一级密钥。业务密钥则可以由根密钥派生出来专门用于加密具体文件或通信数据。我在RK3588平台上做过类似部署启用ATF和OP-TEE后普通Linux内核运行在非安全世界安全世界维护密钥和一些关键运算。这样即使内核被攻破攻击者能接触到的也只是安全世界提供的接口密钥本身不会暴露。对于不带安全世界的MCU也可以用类似思路外部加密芯片中存放设备唯一密钥配合固定算法完成挑战-应答认证业务数据密钥则由认证通过后派生不直接存放在Flash中。4.2 密钥注入产线时最容易出的幺蛾子密钥注入是整个流程里坑最多的一步。很多项目前期开发一切正常一到量产就发现注入效率低、密钥丢失、设备无法返修等问题。第一个坑是密钥没有独立的生成和记录机制。正确做法是先在一台离线电脑或专用密钥管理工具上生成密钥对或随机密钥然后通过烧录器写入设备同时将密钥相关信息加密存档。不要让产线工人直接打开密钥文件更不要把同一个密钥写到所有设备上。第二个坑是烧录顺序和硬件绑定不一致。很多安全芯片支持一次性写入写入后无法再修改。如果产线先组装后烧录一旦出现芯片虚焊或贴错位置整板就报废。我比较推荐先完成所有焊接测试再做密钥烧录和最终测试减少因硬件故障导致的密钥浪费。第三个坑是返修流程没有提前设计。设备返修时主控或加密芯片可能已经损坏如果密钥没有备份或无法重新下发设备基本只能报废。提前规划好返修时的密钥恢复策略比如通过安全通道重新注入密钥能降低不少售后成本。实际项目中我曾见过一个团队因为密钥管理混乱把加密芯片的密钥全部写成了同一个值导致所有设备共用一把钥匙安全防护形同虚设。这种问题在开发阶段很难发现因为功能测试全部通过直到出现恶意仿冒才开始追责。4.3 防抄板场景一颗认证芯片的典型跑法聊到防抄板很多人的第一反应是“把固件加密”但前面分析过固件加密并不解决根因。更常见的做法是使用具备挑战-应答认证机制的加密芯片让主控在关键节点实时确认整机合法性。具体流程大致是这样的主控上电读取加密芯片的唯一ID确认芯片存在。主控生成一个随机数挑战发送给加密芯片。加密芯片使用内部密钥对随机数做运算返回认证码。主控用自己持有的公钥或共享密钥验证认证码验证通过才继续启动业务逻辑。这里有个容易被忽略的细节认证不能只在启动时做一次。攻击者完全可以先让设备正常启动然后通过热补丁方式跳过后续校验。更稳妥的做法是在关键业务操作前周期性认证比如每执行一次核心函数就发起一次挑战-应答握手虽然会占用少量时间但显著提高了绕过难度。以SMEC98SP这类防抄板加密芯片为例它在主控端一般通过I2C或SPI接口通信SDK会提供接口函数开发量并不大。我通常建议把认证代码分散到多个业务模块里不要在启动阶段集中调用这样攻击者就算定位到认证函数也难以通过简单patch彻底绕过。// 伪代码示意挑战-应答认证 uint8_t challenge[16]; uint8_t response[16]; trng_generate_random(challenge, sizeof(challenge)); secure_chip_auth(challenge, sizeof(challenge), response, sizeof(response)); if (verify_response(challenge, response) ! OK) { // 认证失败停止运行或进入降级模式 system_halt(); }如果主控本身资源紧张也可以把认证频率降低但至少要在最关键的算法入口、配置读取和生产功能前各做一次校验避免一个点被patch就全线崩溃。5. 踩过的坑与几个容易被忽略的细节前面聊了原理和流程这一部分我想集中讲一些实际项目里容易踩的坑。有些坑是认知层面的有些是操作层面的都会直接影响最终安全效果。5.1 硬件加密不等于绝对安全先泼一盆冷水使用了独立加密芯片并不代表设备就攻不破。芯片本身再安全如果主控和芯片之间的认证逻辑设计得粗糙攻击者依然能找到突破口。最常见的问题是“认证结果可预测”或“认证只在启动时做一次之后所有操作默认可信”。比如主控每次调用加密芯片发送的都是固定序列号或固定命令返回结果也可预测。攻击者不需要破解芯片直接模拟这些交互或者把认证代码patch成永远返回成功就能绕过整个保护体系。另一个常见认知误区是把“硬件加密”和“通信加密”混淆。有的项目对主控和加密芯片之间的I2C通信做了额外加密这本身没坏处但要注意加密密钥如果还是存放在普通Flash中那就只是给攻击者增加了一点逆向成本并没有提高真正的安全等级。5.2 调试接口、日志和串口打印把底裤漏光了很多设备的加密方案本身设计得不错结果败在调试接口上。JTAG/SWD接口没有锁死攻击者直接连接调试器就能读取内存、修改寄存器和绕过校验。启用读保护后也要注意部分MCU的读保护等级设置不当或者固件升级逻辑存在漏洞依然可能被降级攻击。串口日志也是常见泄露点。有些开发者在代码里打印了大量调试信息包括密钥、认证结果、内存地址等上线后也没有关闭。攻击者连上串口就能看到整个运行过程这等于把内部逻辑主动交了出去。我在项目自测阶段会加一条固定流程产品进入量产形态后调试接口必须锁定串口日志必须关闭或只保留非敏感信息固件里不得出现明文密钥、证书或内部地址符号。虽然这会增加一些调试成本但相比安全泄露这点成本完全值得。5.3 算法选型上的常见错误算法选型也是安全设计里的重头戏而且很多错误属于“看起来很努力实际没用”。比如使用已经被攻破的算法像DES、MD5或者自己魔改一套AES。任何自研加密算法在没有经过专业密码分析之前都不要用。安全领域有一条铁律不要自己发明密码算法。我建议在项目里统一采用成熟、公开的算法比如AES-128/256做数据加密SHA-256做完整性校验ECDSA或RSA做签名认证。加密芯片或安全芯片一般都会内置这些算法调用标准接口即可。参数选择上能用256位就不要用128位虽然128位在计算资源紧张时也能接受但从安全冗余角度看256位更稳妥。还需要注意随机数质量。很多软件加密方案里使用的“随机数”其实是伪随机数如果种子可预测那么整个密钥体系都可以被推算出来。硬件加密芯片通常带真随机数发生器建议优先使用硬件TRNG产出的随机数尤其是在挑战-应答认证中随机数质量直接决定认证强度。5.4 一份自查清单做硬件安全评估时挨个过这些年我评审过不少项目的安全方案也总结了一份简单清单每次做安全评估时都会逐项过。这里分享出来供有需要的团队参考密钥是否只存在于受保护的安全介质中普通内存和Flash中没有明文设备是否启用了安全启动/签名校验BootROM到应用层每一级都有验证调试接口JTAG/SWD/串口在量产版本中是否已关闭或降权日志中是否包含敏感信息是否有开关可以在量产时关闭认证过程是否为挑战-应答模式是否周期性地在关键路径上执行固件中是否存在硬编码密钥、证书、口令随机数是否来自硬件TRNG序列是否可预测返修流程中是否能安全地恢复或重签密钥而不影响其他设备如果这些项目里有三到四项回答不了那说明安全方案还需要进一步打磨。硬件加密不是加上芯片就万事大吉它需要配合启动链、调试接口管控、密钥生命周期管理等多角度设计才能发挥真正作用。我在实际项目中的体会是安全防护没有一劳永逸只有不断根据威胁模型调整方案。大部分消费类产品并不需要面对国家级攻击者但至少要挡住通过读Flash、抓串口、简单patch这种方式来仿制和篡改的普通团队。搞清楚软件加密和硬件加密的本质差异后再结合自身产品和生产条件去做选择就不会在方案评审时被各种概念带偏了。
返回列表