WS63 访问 HTTPS 握手失败(-0x7780)根治

文章目录

    • [1. 现象复盘](#1. 现象复盘)
    • [2. 错误码 -0x7780 到底是什么意思](#2. 错误码 -0x7780 到底是什么意思)
    • [3. 为什么去年的改法没用:WS63 的 mbed TLS 是"两层编译"](#3. 为什么去年的改法没用:WS63 的 mbed TLS 是"两层编译")
    • [4. 根因:ECDHE 只在 HiLink 分支启用](#4. 根因:ECDHE 只在 HiLink 分支启用)
    • [5. 完整根治方案(两处配置 + 强制全量重编)](#5. 完整根治方案(两处配置 + 强制全量重编))
      • [5.1 主配置文件(决定性)](#5.1 主配置文件(决定性))
      • [5.2 硬件适配配置文件(保持一致)](#5.2 硬件适配配置文件(保持一致))
      • [5.3 必须强制全量重编(很容易漏!)](#5.3 必须强制全量重编(很容易漏!))
    • [6. 一个差点误导我们的"乱码"假象](#6. 一个差点误导我们的"乱码"假象)
    • [7. 总结与坑位清单](#7. 总结与坑位清单)
    • 附录:关键文件清单

去年我写过一篇博文,记录了 WS63 访问公网 HTTPS 时 TLS 握手失败的问题,但当时只停留在"现象 + 试改配置"的层面,没能找到真正的根因,改完也不稳定。今天在做一个"WS63 + 温湿度传感器 + 蜂鸣器 + 华为云 MaaS 大模型对话"的样例时,把这个问题从编译链路层面彻底定位并稳定修复了。本文复盘整个排查过程,给出可复用的根治方案,并顺带澄清一个差点误导判断的"乱码"假象。


1. 现象复盘

在 WS63 上用 Mbed TLS 直连华为云 MaaS 的公网接口 api.modelarts-maas.com(OpenAI 兼容的 /v2/chat/completions)时,Wi-Fi 关联、DHCP 拿 IP、外设初始化都正常,但一进入 TLS 握手就失败:

复制代码
APP|[LLMCHAT] connecting HTTPS to api.modelarts-maas.com
[ERR] signal NOT SUPPORT
APP|[LLMCHAT] TLS handshake with api.modelarts-maas.com
APP|[LLMCHAT] TLS handshake failed: -0x7780

日志里还会伴随一行 [ERR] signal NOT SUPPORT,先说结论:这行是 LiteOS 不支持 signal() 系统调用的桩警告 (Mbed TLS 的 net_sockets.c 里会调用 signal(SIGPIPE, SIG_IGN) 来忽略管道断开信号,而 LiteOS 的 signal() 是空实现),它与握手失败无关 ,完全可以忽略。真正的元凶是 -0x7780。


2. 错误码 -0x7780 到底是什么意思

在 Mbed TLS 头文件 include/mbedtls/ssl.h 里,错误码的高位是模块标识、低位是具体错误:

复制代码
-0x7780 = MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE

注意:它不是 UNEXPECTED_MESSAGE (那是 -0x7700),而是 FATAL_ALERT_MESSAGE ------意味着服务端在握手过程中向我们发来了一个 fatal alert 。公网 TLS 服务器发 fatal alert 最常见的原因就是 handshake_failure(我们发过去的 ClientHello 它不接受)。

所以这是一个服务端拒绝 问题,而不是本地把数据解析坏了。方向立刻清晰:问题出在 ClientHello 里提供的密码套件/密钥交换方式与服务器对不上。


3. 为什么去年的改法没用:WS63 的 mbed TLS 是"两层编译"

这是本次排查最关键、也最容易被忽略的点。WS63 的 Mbed TLS 并不是单一一套配置编译出来的,它被拆成了两层静态库,各自用不同的配置文件:

库 作用 配置文件(编译宏)
libmbedtls_v3.6.0.a 核心 TLS 状态机:握手流程、密码套件选择、密钥交换 MBEDTLS_CONFIG_FILE → drivers/chips/ws63/porting/mbedtls/config-ws-iot_v3.6.0.h
libmbedtls_harden.a 硬件加解密适配层(ECP/RSA 的硬件/软件 offload 副本) MBEDTLS_USER_CONFIG_FILE → drivers/drivers/driver/security_unified/mbedtls_harden_adapt/mbedtls_platform_hardware_config.h

在 build/cmake/open_source/mbedtls_v3.6.0.cmake 第 112 行可以确凿看到:

cmake 复制代码
MBEDTLS_CONFIG_FILE="${ROOT_DIR}/drivers/chips/ws63/porting/mbedtls/config-ws-iot_v3.6.0.h"

核心库只认这一个主配置文件,根本不读取硬件适配头。 这意味着:决定"客户端能用哪些密码套件"的代码(ssl_client.c / ssl_tls.c 里的套件协商逻辑),只受主配置 config-ws-iot_v3.6.0.h 影响。硬件适配头里对 MBEDTLS_KEY_EXCHANGE_ECDHE_*、MBEDTLS_SSL_CIPHERSUITES 的 #undef/#define,碰不到核心握手机,改了也白改。

去年那篇博文(包括我早期的一次尝试)只改了硬件适配层那个头文件,所以"看起来改对了、实际握手照样失败"。


打开主配置文件 drivers/chips/ws63/porting/mbedtls/config-ws-iot_v3.6.0.h,关于密钥交换与密码套件是这样写的(节选):

c 复制代码
#if defined(CONFIG_SUPPORT_HILINK)
#define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_SSL_CIPHERSUITES                            \
    MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,    \
    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,          \
    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
#elif defined(CONFIG_SAMPLE_SUPPORT_xxx)   /* 仅个别官方示例开启 */
...
#endif

也就是说:ECDHE 密钥交换 + ECDHE 系列密码套件,只有在 CONFIG_SUPPORT_HILINK 或少数官方示例分支下才会被定义。 当你自己新建一个需要访问公网 HTTPS 的样例(既不是 HiLink,也不在官方示例分支里)时,这一整段会落到"空分支":

  • 客户端不提供任何 ECDHE 套件,只能提供 RSA 密钥交换类套件;
  • 而华为云 MaaS 这类现代公网端点只接受 ECDHE(出于前向安全等要求);
  • 于是服务器在收到 ClientHello 后直接回 handshake_failure 的 fatal alert;
  • Mbed TLS 在期待 ServerHello 时却收到了 alert → 报 MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE (-0x7780)。

硬件适配头 mbedtls_platform_hardware_config.h 里还有第二道"暗杀":

c 复制代码
#if !defined(CONFIG_SUPPORT_HILINK) && !defined(CONFIG_SAMPLE_SUPPORT_xxx)
#undef MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
#undef MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
#undef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#endif

即使你在别处开了 ECDHE,这个 #undef 也会把它灭掉(仅对硬件适配库生效,但同样说明 SDK 默认态度是"非 HiLink 就不开 ECDHE")。


5. 完整根治方案(两处配置 + 强制全量重编)

把你的样例配置符号(下面以 CONFIG_SAMPLE_SUPPORT_LLMCHAT 为例,你换成自己的 Kconfig 开关名即可)加入两处配置:

5.1 主配置文件(决定性)

drivers/chips/ws63/porting/mbedtls/config-ws-iot_v3.6.0.h:

c 复制代码
#if defined(CONFIG_SUPPORT_HILINK)
#define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_SSL_CIPHERSUITES                            \
    MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,    \
    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,          \
    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
#elif defined(CONFIG_SAMPLE_SUPPORT_LLMCHAT)   /* ← 新增:你的公网 HTTPS 样例 */
/* 公网 TLS 1.2 端点(如华为云 MaaS)要求 ECDHE 密钥交换 */
#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_SSL_CIPHERSUITES                            \
    MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,        \
    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,          \
    MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
#endif

⚠️ 这一处才是决定核心握手库行为的关键。 如果你的 SDK 里这段原本还包含其它联网示例的符号,请保留原有符号、仅追加 LLMCHAT,不要误删别人分支。

5.2 硬件适配配置文件(保持一致)

drivers/drivers/driver/security_unified/mbedtls_harden_adapt/mbedtls_platform_hardware_config.h,把两处条件都补上你的符号:

c 复制代码
/* (a) 不要再把 ECDHE 关掉 */
#if !defined(CONFIG_SUPPORT_HILINK) && !defined(CONFIG_SAMPLE_SUPPORT_LLMCHAT)
#undef MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
#undef MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
#undef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#endif

/* (b) 对本样例使用软件 ECP 乘法(与官方联网示例一致) */
#if !defined(CONFIG_SAMPLE_SUPPORT_LLMCHAT)
#define MBEDTLS_ECP_MUL_ALT
#endif

5.3 必须强制全量重编(很容易漏!)

改的是 Mbed TLS 配置头,而 libmbedtls_v3.6.0.a 是预编译静态库,增量构建经常不会重编它------这正是一开始"改了配置还是报同样错误"的真凶。务必:

  1. 删除核心库产物,例如 output/ws63/acore/ws63-liteos-app/libmbedtls_v3.6.0.a;
  2. 整体重建 ws63-liteos-app 并烧写。

烧写成功后,串口应看到:

复制代码
APP|[LLMCHAT] TLS handshake with api.modelarts-maas.com
APP|[LLMCHAT] MaaS request body sent (454 bytes)
APP|[LLMCHAT] MaaS response received (1838 bytes)
APP|[LLMCHAT] LLM: <大模型建议文本>

握手通过,公网 HTTPS 调用成功。


6. 一个差点误导我们的"乱码"假象

握手修好之后,又冒出一个新问题:大模型返回的中文里偶尔出现 (U+FFFD 替换符),而且每次乱的位置都不一样;英文和十六进制却完全正常。第一反应自然是"解析/发送把多字节 UTF-8 写坏了"。

为了不被直觉骗,我在应用里加了一段十六进制 dump ,把抽取出的回复前若干字节原样打印出来。结果证明内存里的字节 100% 是合法 UTF-8 三字节序列:

复制代码
e6 b9 bf = 湿   e5 ba a6 = 度   e4 bd 8e = 低   e6 84 9f = 感 ...

字节层面毫无损坏,问题只出在显示 。最终确认:乱码是 HiSpark Studio 插件自带串口终端对多字节 UTF-8 解码不完善 导致的------同样是这批数据,换到 MobaXterm(UTF-8) 显示就完全正常。

经验法则:排查串口中文乱码,先打十六进制确认字节是否完好。只要 hex 是合法 UTF-8,那固件/协议层就没问题,纯粹是终端编码的事,千万别去乱改固件的发送逻辑。

(顺带提一句:WS63 的 print_str 走阻塞轮询的 uapi_uart_write,逐字节写 TX FIFO、关中断,绝不丢字节 ;它的单条打印缓冲上限 UART_TRANS_LEN_MAX 是 128 字节,超长会被静默截断,但那只会"少显示"不会"乱码"。所以显示类问题优先怀疑终端。)


7. 总结与坑位清单

WS63 访问公网 HTTPS 握手失败(-0x7780)的根治要点:

  1. -0x7780 = 服务端 fatal alert(通常是 handshake_failure),不是本地解析错误,根因是 ClientHello 的密码套件不被接受。
  2. ECDHE 默认只对 HiLink 开启。自建公网 HTTPS 样例必须自己把 ECDHE 密钥交换 + ECDHE 套件打开。
  3. Mbed TLS 是两层编译 :决定套件协商的核心库只认主配置 config-ws-iot_v3.6.0.h,硬件适配头改了也不影响握手------这是去年没修好的关键。
  4. 必须改两处:主配置(决定性)+ 硬件适配配置(保持一致、走软件 ECP)。
  5. 改配置头后必须删掉 libmbedtls_v3.6.0.a 全量重编,否则增量构建不会重编核心库,错误照旧。
  6. [ERR] signal NOT SUPPORT 是 LiteOS 不支持 signal() 的无害警告,与握手无关。
  7. 串口中文乱码先打 hex 验证:字节正确就是终端编码问题(如 HiSpark Studio 自带终端),换 MobaXterm 即可,别动固件。

附录:关键文件清单

文件 作用
drivers/chips/ws63/porting/mbedtls/config-ws-iot_v3.6.0.h 主配置 :核心 TLS 库 MBEDTLS_CONFIG_FILE,决定 ECDHE/套件(改这里才有效)
drivers/drivers/driver/security_unified/mbedtls_harden_adapt/mbedtls_platform_hardware_config.h 硬件适配配置 MBEDTLS_USER_CONFIG_FILE,需同步保持一致
build/cmake/open_source/mbedtls_v3.6.0.cmake 核心库编译脚本,确认 MBEDTLS_CONFIG_FILE 指向主配置
open_source/mbedtls/mbedtls_v3.6.0/include/mbedtls/ssl.h -0x7780 = MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE 的定义所在
output/ws63/acore/ws63-liteos-app/libmbedtls_v3.6.0.a 核心库产物,修复后需删除并全量重编

希望这篇能帮到同样被 WS63 + 公网 HTTPS 折磨的同学。如果重编后仍失败,建议在 MBEDTLS_DEBUG_* 打开的情况下,用 mbedtls_strerror() 把 alert 的具体类型打印出来,定位会更快。

相关推荐
Patrick在香港1 小时前
时间戳凭空早了 8 小时:datetime.utcnow() 弃用实测与漂移复盘
数据库·python·标准库·datetime·时区·弃用
李兆龙的博客9 小时前
问津集 #26:Lakebase——Postgres 的版本化页面存储、数据库分支与计算弹性
数据库
木子n111 小时前
车载以太网与SOME/IP服务化通信实战-01.从字节到服务:SOME/IP报文解析与四通道开销账
网络·网络协议·tcp/ip·车载以太网·some/ip
倔强的石头_12 小时前
聊聊金仓KFS:一款把数据同步软件做扎实的产品
数据库
闲云野鹤在人间12 小时前
MySQL|从理论、安装、备份到主从复制、MHA高可用详解
linux·运维·数据库·mysql·云计算
禾小西12 小时前
Redis:从两大维度和三大主线建立知识体系
数据库·redis·缓存
禾小西13 小时前
Redis 数据结构:快速的 Redis 有哪些慢操作?
数据结构·数据库·redis
数据库小学妹13 小时前
数据共享交换平台选型:交换方式对比与避坑指南
数据库·信创·数据同步·数据交换平台·政务数据共享·数据共享交换平台·数据库底座
Knight_AL14 小时前
基于 Netty 实现的 WebSocket 服务端
网络·websocket·网络协议