STM32H573安全芯片上TLS 1.3配置与PSA Crypto集成

发布时间:2026/8/31 21:39:47
STM32H573安全芯片上TLS 1.3配置与PSA Crypto集成 1. 先说清楚这次折腾的背景手上这个项目用到了STM32H573主控是Cortex-M33带TrustZone跑的是ST官方的Secure Manager也就是TrustZone固件基础上那套安全服务。因为客户要求传输链路必须上TLS 1.3而密钥、签名这些敏感操作又不能直接暴露在非安全侧于是我们在non-secure侧跑Mbed TLS 1.3客户端密码学运算统一走PSA Crypto API由Secure Manager在secure侧执行。听起来挺干净的架构但实际调通花了不少力气。这篇文章就把我踩过的坑、梳理过的调用关系、以及最终能在H573上稳定跑TLS 1.3的配置方式整理出来给同样在搞“安全芯片 TLS”组合的人一个参考。不管你是刚开始接触STM32H573还是已经在H5系列上做过TLS 1.2、现在要升级到TLS 1.3我相信这篇都有可复用的东西。尤其是“Secure Manager下PSA Crypto能力边界”这一点官方文档写得很隐晦我在这里把哪些能做、哪些做不了、哪几种算法组合最容易出问题一条条说清楚。2. 整体设计思路Secure Manager、PSA Crypto和TLS 1.3怎么凑到一起2.1 先搞懂三者的分工STM32H573内部其实分成两个执行环境安全侧Secure World和非安全侧Non-Secure World。Secure Manager跑在安全侧相当于一个常驻的、不可篡改的安全服务进程。它对外提供的不是普通函数库而是一套基于PSA Crypto API的服务接口。Non-secure侧的应用程序比如Mbed TLS、网络协议栈、业务代码调用PSA Crypto API时实际请求会被转发到Secure Manager去执行密钥材料、哈希运算、签名、随机数生成这些敏感动作全都在安全侧完成。TLS 1.3是传输层协议本身不关心密钥放在哪里但它对“安全的随机数、安全的密钥派生、数字签名”要求很高。Mbed TLS在TLS 1.3握手过程中要不断调用哈希、HMAC、HKDF、ECDHE、签名验证这些原语。如果这些原语由Secure Manager通过PSA Crypto提供普通攻击者即使控制了Non-secure侧代码也无法导出私钥也无法在握手过程中篡改密钥派生材料。所以这个架构的价值一句话就能总结TLS 1.3握手在Non-secure侧跑逻辑密码学底子在Secure侧做隔离两边通过PSA Crypto API通信。这个设计比“把私钥放在Flash里、用软件算法签名”要安全一个量级。2.2 为什么TLS 1.3比TLS 1.2更容易踩到Secure Manager的雷很多人一开始会觉得TLS 1.2能跑通TLS 1.3不就是换个协议版本吗实际上差别非常大。TLS 1.3握手里的密码学操作数量和种类比TLS 1.2多很多。TLS 1.3握手需要做两次密钥派生Early Secret、Handshake Secret、Master Secret每一次都用HKDF-Extract和HKDF-Expand。此外还有ECDHE临时密钥对生成、证书签名校验、Finished消息的HMAC计算。这些操作在TLS 1.2里一部分是可选的在TLS 1.3里全部是强制且不可省略的。更要命的是TLS 1.3里所有对称密钥的生成都依赖“足够好的随机数”。Secure Manager的随机数发生器TRNG确实很好但如果Random函数没有正确接到PSA APIMbed TLS会用默认的弱随机源去生成ECDHE私钥和密钥盐这会导致握手时密钥可预测。另外TLS 1.3对签名算法有了严格的规定必须使用数字签名算法且要显式声明。如果Secure Manager只支持ECDSA P-256而证书链里用的是RSA 2048握手直接失败因为TLS 1.3的签名算法协商阶段会直接排除掉不支持的类型。更隐蔽的问题是buffer和内存。Secure Manager作为独立运行时它分配给PSA Crypto请求的buffer是有限的。TLS 1.3的握手消息尤其是Certificate消息包含整条证书链很容易超过默认buffer上限于是表现为“请求发过去了但是报错返回”。这个问题我调试了将近一天才定位。3. 我遇到的核心问题PSA Crypto与TLS 1.3磨合期的几个典型障碍3.1 问题一非安全侧的Mbed TLS没法直接用完整的PSA加密数学库这是最底层的一个问题。Mbed TLS的Crypto库有两个接口层一层是传统APImbedtls_ecdsa_sign()、mbedtls_rsa_private()这种另一层是PSA APIpsa_sign_hash()、psa_asymmetric_encrypt()这种。在STM32H573上使用Secure Manager时非安全侧不能用传统API去访问硬件密钥因为这些API依赖的算法实现直接编译在用户代码里私钥也会暴露在Non-secure内存里。但Mbed TLS的TLS 1.3实现默认情况下是走传统API的。换句话说你需要把Mbed TLS配置成“所有密码学操作都通过PSA API完成”的模式。如果你不进行这一步编译能通过但运行的时候会跑到软件算法分支私钥不经过Secure Manager隔离就形同虚设。这个问题的外在表现是TLS 1.3握手能跑通但性能极差且私钥明文出现在内存中。如果项目安全评估严格的话这属于直接不通过的问题。解决方法是在Mbed TLS配置头文件里打开MBEDTLS_USE_PSA_CRYPTO并且要确保所有调用点都改成PSA接口。但这里有个很深的坑Mbed TLS 3.x版本里TLS 1.3的部分路径即使开了这个宏仍然保留了直接调用底层摘要函数的代码比如mbedtls_sha256()。你要么打补丁要么把整个TLS层强制改为“所有摘要走psa_hash_compute()”。我最终的做法是给Mbed TLS打了个小补丁把所有哈希、HKDF、签名验证路径都改为走PSA API具体改动点后面实操部分会详细列出。3.2 问题二Secure Manager里的签名算法和TLS 1.3的协商不匹配TLS 1.3握手阶段客户端会发送“supported_signature_algorithms”扩展列出自己能接受的签名算法。服务端根据这个列表和证书类型选择签名算法并返回结果。如果客户端列出的算法Secure Manager不支持那么握手在CertificateVerify阶段就会失败。Secure Manager支持的签名算法以PSA Crypto的算法表示为准。在STM32H573上实测以下算法是可用的PSA_ALG_ECDSA(PSA_ECC_FAMILY_SECP_R1) SHA256PSA_ALG_RSA_PKCS1V15_SIGN(SHA256)注意TLS 1.3不支持PKCS1 v1.5PSA_ALG_RSA_PSS(SHA256)问题就出在TLS 1.3只允许RSA-PSS签名不允许RSA-PKCS1-v1.5。很多人在TLS 1.2时代签字使用的是RSA PKCS1到了TLS 1.3Mbed TLS默认会从证书的密钥用途和签名算法里筛选结果发现“Secure Manager能做的”和“协议允许的”交集为空于是握手失败。我建议的做法是如果你是客户端直接使用ECDSA P-256 SHA256证书如果你是服务端配置也优先用ECC密钥。不是因为RSA不行而是因为RSA-PSS签名在Secure Manager的调用链路中对数据和盐长度的处理容易出边界问题。3.3 问题三私钥导入和密钥存储位置决定握手能不能稳定复现TLS 1.3的握手对时间非常敏感。如果你每次建立连接前都临时生成密钥对、导入Secure Manager再签名性能会非常差而且还有可能因为Secure Manager内部事务冲突导致偶发失败。更合理的做法是预先生成密钥对持久化存储到Secure Manager的密钥存储区然后在TLS回调里引用key_id来使用。我的测试场景里密钥分区存储了客户端证书的私钥和服务端CA的信任锚初始化的时候只导入一次之后TLS握手的所有签名操作都通过psa_sign_hash() key_id完成。但这里面有个坑Mbed TLS的标准证书解析和密钥加载函数通常期望的是PKCS#1/PKCS#8编码的私钥明文。如果直接把私钥导入到Secure Manager再从PSA key_id反向获取公钥、证书指纹有些接口是不支持的。你需要把证书加载和密钥加载切成两个路径证书由tls层正常解析只用于携带公钥和身份信息私钥交给Secure ManagerTLS层只持有key_id不直接接触私钥内容。4. 实操记录从故障现象到稳定运行的完整过程4.1 用CubeMX配置H573工程和Secure Manager第一步打开STM32CubeMX选择STM32H573xx系列芯片。在Middleware和Software Packs里启用Secure Manager服务。这里需要注意Secure Manager需要和芯片的RoTRoot of Trust配合ST官方要求先烧录Secure Manager固件再开发Non-secure应用。具体烧录方法使用STM32CubeProgrammer先连接H573的ST-LINK用“Secure Manager”烧录模式把SM固件加载进去。烧录完成后芯片内部会创建好安全分区和密钥存储区。之后开发Non-secure应用时CubeMX会生成PSA API的头文件和IPC通信代码。如果你的项目不是在CubeMX里从头开发而是想集成到一个已有的CMake工程那就要手动把STM32Cube_FW_H5的Middleware里的SecureManager库路径加进去同时链接psa库函数接口。4.2 配置Mbed TLS 3.5及以上版本开启TLS 1.3Mbed TLS从3.1开始实验性支持TLS 1.3从3.4开始基本稳定。我这边用的是3.6版本。配置项如下直接在mbedtls_config.h里改#define MBEDTLS_SSL_PROTO_TLS1_3 #define MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE #define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL #define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL #define MBEDTLS_SSL_EARLY_DATA #define MBEDTLS_USE_PSA_CRYPTO #define MBEDTLS_PSA_CRYPTO_C #define MBEDTLS_PSA_CRYPTO_CONFIG重点说明一下MBEDTLS_USE_PSA_CRYPTO这个宏决定TLS层是否把密钥操作路由到PSA API。默认情况下Mbed TLS在非安全侧直接用软件实现我们必须在编译阶段打开它确保所有密钥管理和密码学操作都经过PSA。MBEDTLS_PSA_CRYPTO_CONFIG这个宏允许你通过PSA_CRYPTO_CONFIG.h文件自定义可用的算法集。在H573的Secure Manager环境下因为算法能力是Secure Manager决定的不是所有Mbed TLS内置的算法都能调用。通过这个配置文件可以把不支持的算法裁剪掉避免编译器把代码编进去后运行时报错。4.3 打通RNG、签名、密钥派生三条关键调用链我花了比较多时间梳理的是三件事随机数、签名、密钥派生。TLS 1.3对随机数和签名较真密钥派生则是握手性能的关键。第一步把RNG接入PSA。在Mbed TLS中通过mbedtls_psa_crypto_free()和mbedtls_psa_crypto_init()初始化后还必须设置随机数源。static int secure_rng(void *ctx, unsigned char *buf, size_t len) { (void)ctx; psa_status_t status psa_generate_random(buf, len); if (status ! PSA_SUCCESS) { return -1; } return 0; }然后在TLS配置里mbedtls_ssl_conf_rng(ssl_conf, secure_rng, NULL);这里要注意Mbed TLS的默认RNG接口可能是mbedtls_ctr_drbg_random()但那个随机数源在Secure Manager方案里是不合适的。非安全侧的DRBG也需要安全种子其种子应该由PSA接口生成否则这个安全链路就有缺口。第二步把私钥签名路由到PSA。在Mbed TLS里如果要用外部PSA密钥做客户端证书签名需要实现回调static int psa_sign_callback(void *p_ctx, mbedtls_md_type_t md_alg, const unsigned char *hash, size_t hash_len, unsigned char *sig, size_t sig_size, size_t *sig_len) { psa_key_id_t key_id *(psa_key_id_t *)p_ctx; psa_algorithm_t alg; if (md_alg MBEDTLS_MD_SHA256) { alg PSA_ALG_ECDSA(PSA_ALG_SHA_256); } else { return -1; } psa_status_t status psa_sign_hash(key_id, alg, hash, hash_len, sig, sig_size, sig_len); if (status ! PSA_SUCCESS) { return -1; } return 0; }然后把回调挂到SSL配置里mbedtls_ssl_conf_psa_cert_sign_cb(ssl_conf, psa_sign_callback, key_id);这一步特别关键。如果不挂这个回调Mbed TLS会尝试用私钥明文去签名而私钥在Non-secure侧根本没有于是直接报错。第三步确认HKDF不是瓶颈。TLS 1.3握手中的HKDF是纯哈希计算不涉及私钥。如果哈希计算也用PSA接口性能会比软件慢一些但安全性会好。实测下来H573在Secure Manager里做SHA-256大约每秒几百次对握手的延迟影响不大完全可以接受。4.4 跑实际TLS 1.3握手测试我用的是最经典的openSSL s_server作为对端跑在PC上板子作为客户端。openssl s_server -accept 4433 -cert server.crt -key server.key -tls1_3 -www板子端使用自研客户端程序指定TLS 1.3版本连接PC的IP。第一次跑的时候直接卡在CertificateVerify报错。用逻辑分析仪抓串口日志发现Mbed TLS在收到服务端Certificate消息后解析出了证书链但在验证服务端签名时回调返回not supported。排查过程如下确认服务端证书是ECDSA还是RSA。如果是RSA-PSS需要检查Secure Manager是否支持RSA-PSS的导入。确认Mbed TLS的签名算法协商列表里包含“ecdsa_secp256r1_sha256”。检查证书里的签名算法是否被PSA配置允许。最终发现问题出在Mbed TLS的签名算法协商列表默认是按配置裁剪的。我们的配置里没有打开MBEDTLS_X509_RSASSA_PSS_SUPPORT导致TLS 1.3的签名协商阶段直接过滤掉了RSA-PSS。但由于服务端证书链是RSA于是协商结果为空。处理方法很简单加上这个宏重新编译。第二版测试握手已经能成功但复测20次出现2次偶发失败报错位置在KeyUpdate或NewSessionTicket阶段。后来查明是Secure Manager的IPC缓冲区太小在握手过程中如果两次PSA请求靠得太近前一个请求的缓冲区还没释放后一个请求就阻塞了导致超时。解决方案是在CubeMX的Secure Manager配置里把IPC消息队列长度调大同时把连接数限制为1。TLS 1.3场景下握手期间会连续发起多个PSA调用随机数、DH共享密钥计算、签名、HKDFIPC队列的长度至少要比TLS 1.2多3到4个位置。5. 常见问题与排查技巧实录5.1 问题速查表现象根本原因解决方法TLS 1.3握手时提示 no suitable signature algorithm签名算法协商列表中不包含服务端证书支持的算法打开MBEDTLS_X509_RSASSA_PSS_SUPPORT或改用ECDSA P-256证书导入私钥后签名报错 PSA_ERROR_NOT_PERMITTED私钥的key_policy限制了用途或证书标识不匹配在导入密钥时指定usage为SIGN_HASH不要指定为EXPORT握手过程中的PSA调用偶发超时Secure Manager的IPC缓冲区太小在CubeMX里加大IPC队列长度或减少并发连接数Mbed TLS一直说密钥无效私钥没有加载到PSA key_id只加载到了软件层确认mbedtls_ssl_conf_psa_cert_sign_cb已经设置TLS 1.3握手成功但性能极差每次握手都重新导入密钥将密钥做成持久化存储重启后依然可用Finished消息校验失败HMAC计算走了非安全侧的软件实现把所有哈希/HMAC操作都改为PSA APINewSessionTicket解析异常会话恢复机制和Secure Manager的多任务冲突关闭会话恢复功能或调整缓冲区大小5.2 避坑心得先做最小闭环再谈整体调优我很建议在一个最简单的echo服务器上先测试TLS 1.3握手不要一开始就上完整的业务协议。这样可以快速定位问题出在密码学层还是业务层。我用最小闭环测试时直接忽略HTTPS请求只验证SSL读写的打通。当握手稳定后再逐步增加HTTP解析、会话管理、密钥轮换等功能。不要一上来就打开SessionTicketSecure Manager在存储会话票据时也要使用PSA密钥保护对密钥存储区的空间有额外要求会产生很多边界问题。另外一个经验是串口日志要打全。H573开发板串口打印别只打错误码要把PSA的错误码也翻译成字符串打出来。PSA错误码很多是负数不翻译根本看不懂。比如-135表示PSA_ERROR_COMMUNICATION_FAILURE-151表示PSA_ERROR_BUFFER_TOO_SMALL。开发阶段可以把所有p调用包一层日志函数否则调试到半夜你会很崩溃。5.3 内存和栈深度的坑TLS 1.3对内存的需求比TLS 1.2高。Mbed TLS在握手过程中需要保存多个中间状态比如ClientHello、ServerHello、EncryptedExtensions、Certificate、CertificateVerify、Finished。这些消息加起来轻松超过4KB。Non-secure侧RAM要至少留出16KB给SSL上下文。Secure Manager虽然跑在独立安全侧但非安全侧的Mbed TLS堆栈设置一样会出问题。如果栈深度不够很容易在HKDF扩展阶段触发栈溢出表现为随机的HardFault。建议给SSL握手线程分配8KB以上栈空间并且在每个关键函数入口检查返回值。提示可以在启动阶段用软件填充0xAA模式跑完一次握手后检查栈水位就能粗略估算峰值栈使用量。这个方法简单且有效。6. 一些值得展开说的安全细节6.1 不要把私钥加载到非安全侧有些开发者的做法是在Non-secure侧的Flash里放置证书私钥TLS握手时直接拿私钥在本地签名。这在Secure Manager方案里属于错误做法。虽然功能和性能上没问题但安全评估不会通过。因为Non-secure侧一旦被攻击者拿下私钥就直接暴露了。正确的方式是私钥只以持久化key_id的形式存在Non-secure侧只能引用它没有内容访问权。6.2 密钥的访问策略要精确限定导入Secure Manager的密钥是可以设置访问策略的比如psa_key_attributes_t attributes PSA_KEY_ATTRIBUTES_INIT; psa_set_key_usage_flags(attributes, PSA_KEY_USAGE_SIGN_HASH); psa_set_key_algorithm(attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); psa_set_key_type(attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(attributes, 256);这里建议只设置SIGN_HASH和对应的签名算法不要设置EXPORT不要设置VERIFY_HASH除非你的业务模型必须要。最小权限原则在这里不是一句空话。如果签名算法和密钥位数配置不一致会在握手时产生很奇怪的错误比如签名验证成功但消息完整性校验失败。6.3 安全启动和信任根STM32H573的Secure Manager会使用芯片内部的RoTRoot of Trust来验证固件镜像。如果你更新了Non-secure侧的固件但没有更新签名Secure Manager会拒绝加载非安全侧程序。这个机制本身非常安全但对开发调试很不友好。在开发期我建议保留一个未启用安全启动的调试版本否则每改一次代码要重新签名一次效率很低。等所有功能稳定后再切到强制安全启动模式。7. 最后想说的STM32H573 Secure Manager PSA Crypto TLS 1.3这套组合安全性确实没得说。但代价是调试复杂度比普通的“Mbed TLS单芯片跑软算法”高了不少主要难点就在于所有密码学运算都变成了跨安全侧调用而TLS 1.3的握手又特别依赖这些跨侧调用的连续性和稳定性。我个人的经验是做这类项目一定要先把PSA Crypto的底层能力摸清再碰TLS协议层。比如你先单独用psa_sign_hash()跑通签名用psa_generate_random()跑通随机数最后再联调握手。任何一层不干净都会在TLS 1.3握手时以莫名其妙的方式暴露出来。另外建议不要忽略Secure Manager的IPC缓冲区、Mbed TLS的PSA宏开关、以及证书签名算法的协商细节。这三块是我实际踩坑最深的地方也是官方文档最含糊的地方。希望这篇整理能帮你少走弯路。