K230 CanMV 物联网通信安全实战:TLS 加密 + 应用层 HMAC 认证方案全解析

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.pywrap_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 层定义了 keyfilecertfile 参数,但这不代表底层支持。底层 C 代码 _ussl.wrap_socket() 实际接收的参数有一份"白名单",超出白名单的参数就会报 TypeError: extra keyword arguments given

2.2 关键参数的实测结果

通过分析 C 源码和反复试验,得出各参数的实际支持情况:

参数K230 是否支持作用
server_hostnameSNI 域名验证
cert_reqsCERT_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() 直接拒绝了 keyfilecertfile 参数。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 证书文件的路径
cadataPEM 数据——直接把证书内容作为字符串传入

按 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_CERTCA 根证书❌ TLS 握手断开
cadata=SERVER_CERT服务器证书❌ 证书验证失败
cadata=CA_CERTCA 根证书✅ 成功

教训: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 + Nonce5 分钟过期 + 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 关键教训

  1. ca_certscadata:虽然语义相近,但在 K230 mbedtls 上行为不同。前者可能导致 TLS 握手直接断开,后者才是正确参数。

  2. 自签名证书和 CA 签发证书的行为不同:自签名证书可以直接钉选(certificate pinning),CA 签发证书必须用 CA 根证书做链验证。不要混用。

  3. cadata 传递的是 PEM 字符串,不是文件路径:K230 MicroPython 不需要证书文件,直接将 PEM 内容硬编码在 Python 源码中即可。

  4. CA 根证书公开不是安全漏洞:CA 根证书只含公钥,只能验证签名不能生成签名。真正需要保护的是 ca.key(CA 私钥)和 SECRET_KEY(共享密钥)。

  5. TCP 用 Challenge-Response,HTTP 用签名 Header:TCP 面向连接,认证一次即可;HTTP 无状态,每个请求独立签名。

  6. MicroPython 缺少 uhmac 模块:需要手动实现 HMAC-SHA256,核心就是 ipad ⊕ key || opad ⊕ key 的两轮哈希。


十、附录:文件清单

所有测试代码和文档位于项目 3.test/加密认证测试/ 目录下:

文件说明
tcp_client_auth.pyK230 TCP 客户端 — TLS + Challenge-Response
tcp_server_auth.pyPC TCP 服务端 — TLS + Challenge-Response
http_client_auth.pyK230 HTTP 客户端 — TLS + HMAC 签名
http_server_auth.pyPC HTTP 服务端 — TLS + HMAC 签名验证
tcp_client_mtls_test.pyK230 mTLS 能力验证客户端
tcp_server_mtls_test.pyPC mTLS 能力验证服务端
README_TCP认证方案.mdTCP 方案设计文档
README_HTTP认证方案.mdHTTP 方案设计文档

此方案适用于所有 K230 CanMV 固件版本(基于 MicroPython + mbedtls),也适用于其他不支持客户端证书的 MicroPython 平台。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值