K230 CanMV 物联网通信安全实战:TLS 加密 + 应用层 HMAC 认证方案全解析
在受限的 MicroPython 系统上,如何用"曲线救国"的方式达到企业级通信安全?这篇文章记录了我从踩坑到落地的完整历程。
一、背景:一块开发板的"网线焦虑"
1.1 系统长什么样
先交代一下基本盘。我手上是一套物联网数据采集系统,部署在远端无人值守的站点。每个采集节点里塞一块嘉楠科技的 K230D CanMV 开发板,运行 MicroPython 固件。这块板子负责采集温度、湿度、门锁状态,拍照片录视频,然后通过网络把数据扔回 PC 服务端。
通信分两路走:
| 连接类型 | 协议 | 用途 | 特点 |
|---|---|---|---|
| TCP 长连接 | 自定义二进制协议 | 心跳包、传感器数据上报、门锁状态同步 | 长时间保持,收发二进制帧 |
| HTTP 短连接 | HTTP/1.1 + multipart | 图片、视频文件上传 | 按需建立,传完即断 |
┌──────────────────────────┐ ┌──────────────────────┐
│ K230 (采集节点) │ │ PC 服务端 (中心端) │
│ · 温度 / 湿度 / 门锁 │ TCP ←──→│ · 接收传感器数据 │
│ · 摄像头拍照 / 录像 │ HTTP ──→│ · 存储图片 / 视频 │
│ · Wi-Fi / 以太网 │ │ · 下发控制指令 │
└──────────────────────────┘ └──────────────────────┘
1.2 安全不做会怎样
设备走的是公网,不是内网专线。通信如果不做任何保护,等于把家门钥匙插在锁孔上出门——两个致命问题:
窃听:二进制协议是明文的。交换机旁路上抓个包,温度多少度、门锁是不是开着、心跳间隔多久,一清二楚。视频文件上传的 HTTP 请求也不加密——图片内容直接可见。
冒充:服务端就是个 socket 监听端口,没有任何身份检查。只要你知道 IP 和端口,telnet 上去发一串数据,服务端就当合法设备处理。攻击者可以伪造传感器读数让你误判现场状态,或者更粗暴——上传垃圾文件直接把磁盘撑爆。
1.3 标准答案是什么
做通信安全的人都会脱口而出:双向 TLS (mTLS)。
标准 mTLS 方案:
┌──────────┐ ┌──────────┐
│ K230 │ ←── server.crt ──── │ 服务端 │ ← 服务端出示证书,客户端验证
│ 客户端 │ ─── client.crt ───→ │ │ ← 客户端出示证书,服务端验证
└──────────┘ └──────────┘
一次 TLS 握手,同时完成:
✅ 加密通道建立
✅ 服务端身份验证(防中间人)
✅ 客户端身份验证(防冒充设备)
加密和认证在一个握手包里全部搞定,零业务代码侵入。这就是教科书方案,也是我最初想直接拿来用的方案。
但现实是——K230 上面的 MicroPython,根本不支持客户端证书。这就意味着 mTLS 这条路从一开始就走不通。
那替代方案是什么?如果用不了客户端证书,怎么证明"这个设备是真的"?TCP 和 HTTP 两种连接形态,认证方式能不能一样?CA 根证书放在设备上,攻击者把它提取出来能不能做中间人?
带着这些问题,我开始了整整三天的踩坑和验证。下文按照实际探索顺序,把每一步的测试代码、报错信息、以及最终的可行方案一一记录下来。
二、第一道坎:K230 的 SSL 模块到底支持什么?
2.1 三层接口:ussl → ssl → 用户代码
K230 CanMV 的 TLS 实现底层是 mbedtls,对用户暴露了两个 Python 模块:
用户代码
├── import ssl ← Python 封装层 (ssl.py)
│ └── import _ussl ← C 扩展层 (overlay_modssl_mbedtls.c)
│
└── import ussl ← 直接调 C 层 (旧接口)
Python 封装层 ssl.py 的 wrap_socket 签名如下:
# ssl.py — K230 固件中的 Python 封装层
def wrap_socket(sock, keyfile=None, certfile=None, server_side=False,
cert_reqs=CERT_NONE, *, ca_certs=None, server_hostname=None):
kw = {}
for k, v in (("keyfile", keyfile), ("certfile", certfile),
("server_side", server_side),
("cert_reqs", cert_reqs), ("ca_certs", ca_certs),
("server_hostname", server_hostname)):
if v is not None:
kw[k] = v
return _ussl.wrap_socket(sock, **kw)
虽然 Python 层定义了 keyfile 和 certfile 参数,但这不代表底层支持。底层 C 代码 _ussl.wrap_socket() 实际接收的参数有一份"白名单",超出白名单的参数就会报 TypeError: extra keyword arguments given。
2.2 关键参数的实测结果
通过分析 C 源码和反复试验,得出各参数的实际支持情况:
| 参数 | K230 是否支持 | 作用 |
|---|---|---|
server_hostname | ✅ | SNI 域名验证 |
cert_reqs | ✅ | CERT_NONE / CERT_REQUIRED 控制是否验证服务端证书 |
ca_certs / cadata | ✅ | 传递 CA 根证书(PEM 字符串)用于验证服务端证书链 |
keyfile / certfile | ❌ | 不支持! 客户端证书(双向 TLS 所需)无法使用 |
这意味着什么?K230 只能做单向 TLS——验证服务端身份,但服务端无法在 TLS 层面验证客户端身份。
三、验证 mTLS 是否真的不可行
“既然 ssl.py 里有 keyfile/certfile 参数,万一能用呢?” —— 抱着这种想法,我专门写了一组验证代码。
3.1 客户端测试代码
# tcp_client_mtls_test.py — 核心测试代码
CERT_DIR = '/sdcard/mtls_test'
# 将 CA 证书、客户端证书、客户端私钥写入 SD 卡
# CA_CERT, CLIENT_CERT, CLIENT_KEY 均为硬编码的 PEM 字符串
# ...
ssock = ssl.wrap_socket(
sock,
server_hostname=SERVER_HOST,
cert_reqs=ssl.CERT_REQUIRED,
ca_certs=CERT_DIR + '/ca.crt',
keyfile=CERT_DIR + '/client.key', # ← 尝试客户端私钥
certfile=CERT_DIR + '/client.crt' # ← 尝试客户端证书
)
3.2 服务端配合代码
# tcp_server_mtls_test.py — 服务端配置 CERT_REQUIRED
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(SERVER_CERT_FILE, SERVER_KEY_FILE)
context.load_verify_locations(cadata=CA_CERT)
context.verify_mode = ssl.CERT_REQUIRED # 要求客户端出示证书
3.3 结果:一锤定音
[2] TLS 双向认证握手...
参数列表:
server_hostname = 192.168.7.159
cert_reqs = ssl.CERT_REQUIRED
ca_certs = /sdcard/mtls_test/ca.crt
keyfile = /sdcard/mtls_test/client.key ← 客户端私钥
certfile = /sdcard/mtls_test/client.crt ← 客户端证书
❌ TypeError: extra keyword arguments given
╔══════════════════════════════════════╗
║ 结论: keyfile/certfile 不被 C 层支持 ║
║ K230 无法做 TLS 双向认证 ║
║ 需使用 TLS 加密 + HMAC 应用层认证 ║
╚══════════════════════════════════════╝
TypeError: extra keyword arguments given——C 层的 _ussl.wrap_socket() 直接拒绝了 keyfile 和 certfile 参数。K230 无法实现 mTLS,铁板钉钉。
四、分层安全方案:既然一条路走不通,就分两条路走
既然 TLS 层只能解决"服务器是谁"的问题,那"客户端是谁"就必须在应用层解决。安全方案被拆成两层:
┌──────────────────────────────────────────┐
│ 应用层 → 设备认证(代替客户端证书) │
│ · TCP: Challenge-Response HMAC │
│ · HTTP: HMAC 请求签名 Header │
│ → 解决 "这个请求是谁发的" │
├──────────────────────────────────────────┤
│ 传输层 → 加密 + 服务端验证(均可实现) │
│ ssl.wrap_socket(cadata=CA_CERT, │
│ cert_reqs=CERT_REQUIRED) │
│ → 解决 "服务器是真的" + "数据不被窃听" │
└──────────────────────────────────────────┘
4.1 密钥/证书分工
| 密钥/证书 | 保存位置 | 是否传输 | 泄露后果 |
|---|---|---|---|
server.crt + server.key | 仅 PC 服务端 | 证书公开传输 | key 泄露 → 可解密 TLS 会话 |
ca.crt (CA 根证书) | 客户端预置(硬编码 PEM) | 不传输 | 无,只含公钥,只能验证不能签发 |
ca.key (CA 私钥) | 离线保存(仅签发证书时使用) | 永不传输 | 泄露可签发假证书,体系崩溃 |
SECRET_KEY (HMAC 共享密钥) | 双方各存一份 | 从不传输 | 泄露可冒充合法设备 |
五、证书验证入坑记:ca_certs ≠ cadata
5.0 前因:方案有了,参数怎么写?
上一节定下了架构——传输层用 ssl.wrap_socket() 验证服务端证书。翻译成代码就是三个要素:
ssl.wrap_socket(sock,
server_hostname=host, # ① 验证证书域名
cert_reqs=ssl.CERT_REQUIRED, # ② 强制要求验证
???=CA_CERT_PEM # ③ 把 CA 根证书传进去——但参数名叫什么?
)
域名和强制验证这两个参数没什么歧义。问题出在第三个:CA 根证书是一段 PEM 字符串,应该填到哪个参数名里?
翻 Python 官方文档,ssl.wrap_socket() 接收两个看起来都像的参数:
| 参数 | CPython 语义 |
|---|---|
ca_certs | 文件路径——指向 CA 证书文件的路径 |
cadata | PEM 数据——直接把证书内容作为字符串传入 |
按 CPython 标准,我们是硬编码 PEM 字符串(不是读文件),所以应该用 cadata。但这毕竟是 MicroPython 的裁剪版 ssl 实现,K230 到底认哪个?之前有份验证过的测试代码 25_06 里用的也是 cadata,但那是配合自签名服务器证书用的,我们的服务端证书是 CA 签发的,会不会有区别?
带着这些疑问,我开始了三轮试错——每一轮都是把代码烧进 K230、看报错、分析、再改的过程。
5.1 失败的两次尝试
第一轮,用 ca_certs 传 PEM 字符串。服务端用的是 CA 签发的服务器证书(非自签名),客户端预置了 CA 根证书。
尝试 ①: ca_certs=CA_CERT (PEM 字符串)
→ 服务端报: EOF occurred in violation of protocol
→ ca_certs 在 K230 mbedtls 上可能被当作文件路径而非 PEM 数据
尝试 ②: cadata=SERVER_CERT (直接钉选服务器证书)
→ 客户端报: X509 - Certificate verification failed
→ 服务器证书是 CA 签发的(非自签名),mbedtls 要求信任锚必须是 CA 证书
5.2 为什么 25_06 测试文件能工作?
之前有个验证过的测试文件 25_06客户端https-进行证书验证-发送服务器.py,它用的是自签名服务器证书:
# 25_06 的做法(针对自签名证书有效)
ssock = ssl.wrap_socket(sock,
server_hostname=host,
cert_reqs=ssl.CERT_REQUIRED,
cadata=self.ca_cert) # self.ca_cert 实际是 SERVER_CERT(自签名)
自签名证书的签发者就是自己,mbedtls 可以直接用它作为信任锚。但我们的项目使用 CA 签发的证书体系,服务器证书由 CA 签发,所以信任锚必须是 CA 根证书本身。
5.3 最终的可行方案
# 尝试 ③: cadata=CA_CERT(正确的组合)
CA_CERT = """-----BEGIN CERTIFICATE-----
MIIFhzCCA2+gAwIBAgIUTzVj2l...
-----END CERTIFICATE-----"""
ssock = ssl.wrap_socket(
sock,
server_hostname=host,
cert_reqs=ssl.CERT_REQUIRED, # 必须验证服务端证书
cadata=CA_CERT # CA 根证书 PEM,作为信任锚
)
| 尝试 | 参数 | 证书内容 | 结果 |
|---|---|---|---|
| ① | ca_certs=CA_CERT | CA 根证书 | ❌ TLS 握手断开 |
| ② | cadata=SERVER_CERT | 服务器证书 | ❌ 证书验证失败 |
| ③ | cadata=CA_CERT | CA 根证书 | ✅ 成功 |
教训:K230 的 mbedtls 对参数名和证书类型都很敏感。cadata 是正确的参数名,而证书内容必须是能验证目标证书的信任锚——对于 CA 签发体系就是 CA 根证书,对于自签名体系就是服务器证书本身。
六、TCP 方案:TLS 加密 + Challenge-Response 认证
TCP 是面向连接的,认证只需在连接建立后做一次。最适合的认证方式就是经典的挑战-应答 (Challenge-Response) 机制。
6.1 完整协议流程
客户端 (K230) 服务端 (PC)
─────── TCP connect() ─────────────→ accept()
│
╔═══ ssl.wrap_socket(cadata=CA_CERT) ═══════════╗
║ · 服务端出示 server.crt ║
║ · 客户端用 CA_CERT 验证签发链 ║
║ · 验证通过 → 协商对称密钥 ║
║ · 加密通道建立 ║
╚══════════════════════════════════════════════╝
│
←── [16 bytes] 随机挑战码 ───────── os.urandom(16)
│
HMAC-SHA256(secret_key, │
challenge + device_id) │
─── [1B len][N B id][32B HMAC] ────→ 验证签名
│
←── 0x01 (成功) / 0x00 (失败) ────
│
╔═══ 认证通过,正常收发业务数据 ═══════════════╗
║ 所有数据均由 TLS 加密传输 ║
╚════════════════════════════════════════════╝
6.2 客户端核心代码
首先要在 MicroPython 上手动实现 HMAC-SHA256,因为 K230 没有 uhmac 模块:
def hmac_sha256(key, msg):
"""HMAC-SHA256 手动实现 — MicroPython 无 uhmac 模块"""
import uhashlib
block_size = 64
if len(key) > block_size:
key = uhashlib.sha256(key).digest()
if len(key) < block_size:
key = key + b'\x00' * (block_size - len(key))
o_key_pad = bytes([k ^ 0x5c for k in key])
i_key_pad = bytes([k ^ 0x36 for k in key])
inner_hash = uhashlib.sha256(i_key_pad + msg).digest()
return uhashlib.sha256(o_key_pad + inner_hash).digest()
然后实现完整 TLS + 认证流程:
def tcp_auth_connect(host, port, device_id, secret_key):
# [1/4] TCP 连接(明文)
addr = socket.getaddrinfo(host, port)[0][-1]
sock = socket.socket()
sock.settimeout(10)
sock.connect(addr)
# [2/4] TLS 加密握手 + 服务端证书验证
ssock = ssl.wrap_socket(
sock,
server_hostname=host,
cert_reqs=ssl.CERT_REQUIRED,
cadata=CA_CERT # ← CA 根证书验证服务端
)
# [3/4] Challenge-Response 认证(在 TLS 加密通道内执行)
challenge = ssock.read(16) # 接收 16 字节随机挑战码
signature = hmac_sha256(secret_key, challenge + device_id.encode())
auth_msg = bytearray()
auth_msg.append(len(device_id)) # [1B] ID 长度
auth_msg.extend(device_id.encode()) # [N B] 设备 ID
auth_msg.extend(signature) # [32B] HMAC 签名
ssock.write(bytes(auth_msg))
# [4/4] 接收认证结果
result = ssock.read(1)
if result == b'\x01':
return ssock # 认证成功,返回已认证的加密 socket
else:
ssock.close()
return None
6.3 服务端核心代码
def handle_client(conn, addr):
# Step 1: 发送 16 字节随机挑战码
challenge = os.urandom(16)
conn.sendall(challenge)
# Step 2: 接收认证回应
id_len = recv_exact(conn, 1)[0]
device_id = recv_exact(conn, id_len).decode()
client_sig = recv_exact(conn, 32)
# Step 3: 验证 HMAC 签名
secret = DEVICES.get(device_id)
expected = hmac.new(secret, challenge + device_id.encode(),
hashlib.sha256).digest()
if hmac.compare_digest(expected, client_sig):
conn.sendall(b'\x01') # 认证成功
# 进入正常数据收发...
else:
conn.sendall(b'\x00') # 认证失败
6.4 实测输出
[2/4] TLS 加密握手 (含服务端证书验证)...
✅ TLS 加密通道建立 (已验证服务端身份 + 加密)
[3/4] Challenge-Response 认证 (在加密通道内)...
收到挑战码: 18443a8a242017fca41c1560d95030b7 (每次连接不同)
计算签名: HMAC-SHA256(secret, 18443a8a...+k230_001)
已发送认证回应 (41 字节)
[4/4] ✅ 认证成功!可以收发数据
>>> 发送: b'Hello Server! This is k230_001.'
<<< 收到: b'ACK:Hello Server! This is k230_001.'
>>> 发送: b'Sending binary data: \x00\x01\x02\x03\xff\xfe\xfd'
<<< 收到: b'ACK:Sending binary data: \x00\x01\x02\x03\xff\xfe\xfd'
服务端日志:
收到认证请求: device_id=k230_001
认证成功 (设备: k230_001)
收到 31 字节: b'Hello Server! This is k230_001.'
七、HTTP 方案:TLS 加密 + HMAC 请求签名
HTTP 是无状态的短连接,每个请求独立。Challenge-Response 需要两轮交互,对 HTTP 来说太浪费。所以改用 HMAC 签名 Header 的方式,每个请求自带认证凭证。
7.1 签名计算过程
def build_auth_headers(method, path, body_bytes):
"""构建认证 Headers — 每个请求独立签名"""
timestamp = int(time.time())
nonce = random_hex(8) # 16 字符随机 hex
body_hash = sha256_hex(body_bytes) # 请求体的 SHA256
# 拼接签名字符串
string_to_sign = f"{method}|{path}|{timestamp}|{nonce}|{body_hash}"
# 例: "POST|/upload|1716713271|a1b2c3d4e5f6a7b8|e3b0c44298fc..."
signature = hmac_sha256_hex(SECRET_KEY, string_to_sign.encode())
return {
'X-Device-Id': DEVICE_ID, # 设备标识
'X-Timestamp': str(timestamp), # Unix 时间戳(防重放)
'X-Nonce': nonce, # 随机数(同秒内防重放)
'X-Signature': signature, # HMAC-SHA256 签名 (64 hex)
}
7.2 签名包含哪些信息
签名字符串 = "POST|/upload|1716713271|a1b2c3d4e5f6a7b8|e3b0c44298fc..."
方法 路径 时间戳 随机nonce body哈希
签名绑定了五个要素:HTTP 方法、请求路径、时间戳、随机数、请求体哈希。任何一个被篡改,签名就对不上,服务端立即 403。
7.3 客户端请求代码
class SimpleHTTPS:
def request(self, method, url, headers=None, body=None):
# ① 准备 body
body_bytes = json.dumps(body).encode() if isinstance(body, dict) else b''
# ② 计算 HMAC 签名认证头
auth_headers = build_auth_headers(method, path, body_bytes)
# ③ TLS 加密 + 服务端证书验证
sock = socket.socket()
sock.connect(addr)
ssock = ssl.wrap_socket(
sock,
server_hostname=host,
cert_reqs=ssl.CERT_REQUIRED,
cadata=CA_CERT
)
# ④ 构造完整 HTTP 请求(Headers + Body 均在 TLS 加密通道内)
all_headers = {**headers, **auth_headers,
'Content-Length': str(len(body_bytes))}
request = encode_http_request(method, path, host, all_headers)
ssock.write(request + body_bytes)
# ⑤ 接收响应
response = ssock.read()
return parse_http_response(response)
7.4 服务端验证逻辑
def _check_auth(self):
device_id = self.headers.get('X-Device-Id')
timestamp = self.headers.get('X-Timestamp')
nonce = self.headers.get('X-Nonce')
signature = self.headers.get('X-Signature')
# ① 检查时间戳(±5分钟窗口)
if abs(time.time() - int(timestamp)) > 300:
return False
# ② 检查 nonce 是否已使用(防重放)
if nonce in self._used_nonces:
return False
# ③ 重算签名并比对
secret = DEVICES.get(device_id)
expected = compute_hmac_signature(secret, method, path,
timestamp, nonce, body)
if not hmac.compare_digest(expected, signature):
return False
# ④ 记录 nonce(防止同秒内重放)
self._used_nonces.add(nonce)
return True
7.5 HTTP 请求的防攻击矩阵
| 攻击方式 | 为什么无效 |
|---|---|
| 直接 curl 不带签名头发请求 | 服务端检查 Header 缺失 → 403 |
| 截获旧请求,原样重放 | X-Timestamp 已过期(超过 5 分钟) |
| 5 分钟内重放 | X-Nonce 已被标记使用过 → 拒绝 |
| 修改请求体内容(如篡改 JSON 数据) | body_hash 变了 → 签名对不上 |
| 用旧签名 + 新时间戳 | 签名绑定了 timestamp → 改动后签名失效 |
| 截获多个请求试图反推密钥 | HMAC-SHA256 单向不可逆 |
八、安全模型总结:这套方案到底能防什么?
8.1 攻击场景推演
┌─────────────┐
│ 合法服务器 │
│ server.crt │
│ server.key │
│ SECRET_KEY │
└──────┬──────┘
│
┌──────────┐ ┌────┴─────┐ ┌──────────┐
│ K230 │ ◄────────┤ 互联网 ├─────────►│ 攻击者 │
│ 合法设备 │ └──────────┘ │ │
│ CA_CERT │ └──────────┘
│ SECRET_KEY│
└──────────┘
攻击者能力假设:
✅ 可以抓取所有网络包 (窃听)
✅ 可以发起 TCP 连接到服务器 (冒充设备)
✅ 可以在网络层冒充服务器 IP (冒充服务器)
✅ 可以提取 K230 固件获取 CA_CERT (逆向)
✅ 可以截获并重放历史请求 (重放)
❌ 没有 server.key (服务器私钥)
❌ 没有 SECRET_KEY (设备共享密钥)
❌ 没有 ca.key (CA 私钥)
8.2 攻防对照表
| 攻击 | 防护层 | 原理 |
|---|---|---|
| 窃听通信内容 | TLS 加密 | AES-GCM 加密,攻击者只能看到密文 |
| 冒充服务器(中间人) | CA 证书验证 | 攻击者没有 CA 签发的合法服务器证书,TLS 握手失败 |
| 冒充合法设备 | HMAC 签名 | 攻击者没有 SECRET_KEY,挑战码 / 签名验证不通过 |
| 重放历史请求 | Timestamp + Nonce | 5 分钟过期 + nonce 一次性使用 |
| 篡改上传数据 | 签名含 Body Hash | 改 1 字节 → SHA256 变 → 签名不匹配 |
| 提取 CA_CERT 后伪造服务器 | PKI 公钥体系 | CA_CERT 只含公钥 → 只能验证不能签发证书 |
8.3 与 mTLS 的等价性
mTLS (标准方案):
TLS 握手 ─── 服务端证书 ───→ 客户端验证 CA 签发链 ← 防冒充服务器
TLS 握手 ←── 客户端证书 ─── 服务端验证 CA 签发链 ← 防冒充客户端
本方案 (分层替代):
TLS 握手 ─── 服务端证书 ───→ 客户端验证 CA 签发链 ← 防冒充服务器(同 mTLS)
应用层 ─── HMAC 签名 ───→ 服务端验证 secret_key ← 防冒充客户端(等价效果)
安全等级一致,只是客户端认证从传输层移到了应用层。
九、踩坑清单与经验总结
9.1 参数选择决策树
你需要验证服务端证书吗?
├── 不需要 → ssl.wrap_socket(sock, server_hostname=host)
│ → 仅加密,不验证(可能被中间人攻击)
│
└── 需要 → 服务端用的是什么证书?
├── 自签名证书 → cadata=SERVER_CERT_PEM
│ → 证书钉选 (certificate pinning)
│
└── CA 签发证书 → cadata=CA_CERT_PEM
→ 证书链验证 (chain verification)
9.2 关键教训
-
ca_certs≠cadata:虽然语义相近,但在 K230 mbedtls 上行为不同。前者可能导致 TLS 握手直接断开,后者才是正确参数。 -
自签名证书和 CA 签发证书的行为不同:自签名证书可以直接钉选(certificate pinning),CA 签发证书必须用 CA 根证书做链验证。不要混用。
-
cadata传递的是 PEM 字符串,不是文件路径:K230 MicroPython 不需要证书文件,直接将 PEM 内容硬编码在 Python 源码中即可。 -
CA 根证书公开不是安全漏洞:CA 根证书只含公钥,只能验证签名不能生成签名。真正需要保护的是
ca.key(CA 私钥)和SECRET_KEY(共享密钥)。 -
TCP 用 Challenge-Response,HTTP 用签名 Header:TCP 面向连接,认证一次即可;HTTP 无状态,每个请求独立签名。
-
MicroPython 缺少
uhmac模块:需要手动实现 HMAC-SHA256,核心就是ipad ⊕ key || opad ⊕ key的两轮哈希。
十、附录:文件清单
所有测试代码和文档位于项目 3.test/加密认证测试/ 目录下:
| 文件 | 说明 |
|---|---|
tcp_client_auth.py | K230 TCP 客户端 — TLS + Challenge-Response |
tcp_server_auth.py | PC TCP 服务端 — TLS + Challenge-Response |
http_client_auth.py | K230 HTTP 客户端 — TLS + HMAC 签名 |
http_server_auth.py | PC HTTP 服务端 — TLS + HMAC 签名验证 |
tcp_client_mtls_test.py | K230 mTLS 能力验证客户端 |
tcp_server_mtls_test.py | PC mTLS 能力验证服务端 |
README_TCP认证方案.md | TCP 方案设计文档 |
README_HTTP认证方案.md | HTTP 方案设计文档 |
此方案适用于所有 K230 CanMV 固件版本(基于 MicroPython + mbedtls),也适用于其他不支持客户端证书的 MicroPython 平台。
892

被折叠的 条评论
为什么被折叠?



