辅助工具与参考工程
围绕 QXJ 主链路(后端 / 鉴权前端 / Pad 浏览器)的一组辅助工具仓:联调排障 (SM2 JWT 解析器、Wireshark 解析器、Python 全流程模拟)、配置与环境保障 (env 校验 CLI/Web)、设备绑定配套(二维码助手)、内测分发(hpack), 以及架构参考工程与本文档站自身。
1. qxj-frontend-web-test-sm2-jwt(NSP-SM JWT Inspector)
定位:纯前端的 QXJ 国密 JWT 解析 / 验签工具,对标 jwt.io。本项目后端 (qxj-backend-admin)的 JWT 签名算法是 SM2withSM3(国密非对称),不是标准的 HS256/RS256,jwt.io 等标准解析网站无法验签,故自建此工具。用于联调时判断 token 本身是否有效(与后端结果码 -3/-6 排查配套)。
技术栈:零构建、零框架——原生 HTML + CSS + JavaScript 三个文件 (index.html / style.css / app.js),深色主题、响应式三栏布局; SM2/SM3 能力从 CDN 动态加载 sm-crypto(cdn.jsdelivr.net/npm/sm-crypto@0.3.3), 不依赖任何后端验签。
JWT 格式与验签口径:
header_b64.payload_b64.signature_b64- header:
{"alg":"SM2","typ":"JWT"}的 base64url; - payload:标准 claims(
exp、iat、jti、user_id、token_type、device_id、position等)的 base64url; - signature:SM2 签名值(r||s,64 字节)的 base64url;
- 验签流程:
signing_input = header_b64 + "." + payload_b64→hash = SM3(signing_input)(32 字节)→SM2_verify(public_key, hash, signature); - 公钥格式:128 字符 hex(64 字节,
x||y各 32 字节),不带04前缀, 工具验签时自动补前缀。
功能列表:
- 粘贴 JWT 自动拆分为 Header(红)/ Payload(紫)/ Signature(蓝)三块彩色展示, 每块可展开/折叠原始 JSON;关键 claims 带说明与可读时间格式化;
- 支持 URL 参数
?token=xxx自动填充,便于分享链接; - 外层签名验证:SM3(signing_input) 后 SM2 验签,显示 「✓ Signature Verified / ✗ Invalid Signature / Cannot verify (no public key)」;
- 内层 HMAC 校验:填入握手协商得到的会话密钥后,校验 custom 中的
hmac, 期望载荷 =device_id(UTF-8) || user_id(BE 8) || exp_s(BE 8); exp过期检查与剩余时间倒计时(Valid / Expired 均有明显颜色反馈);- 一键复制 Header / Payload / Signature / 完整 Token。
使用:
# 直接用浏览器打开 index.html,或:
python -m http.server 8080
# 访问 http://localhost:80802
3
公钥获取(管理员):后端执行 python -m tools.setup qrcode,或接口 GET http://<后端>:4607/api/v3/admin/keymgr/qrcode_string/(返回 JSON 中 public_key 字段;页面也支持填后端地址后点「从后端获取」自动拉取, 后端开发模式 CORS 已放行所有来源)。
对齐的后端实现:qxj-backend-admin/apps/auth/sm2_jwt.py(NSP-SM 签发)与 BlacklistJWTAuthentication(外层 + 内层校验口径)。仓内另有 PROMPT.md, 是本工具的开发任务书(含完整验签示例代码与 base64url 处理细节), 可作为理解 NSP-SM token 结构的补充材料。
2. env-config-helper-web(可视化版)
定位:QXJ 环境配置检查 / 合并助手的 Web 版(CLI 版见下节)。在浏览器里 粘贴 Django 后端 .env 与前端 .env,完成解析、规则校验、密钥/变量合并与 差异对比,并导出合并结果。包名 @nsp/env-config-helper-web。
技术栈:Vue 3 + TypeScript + Vite 5、Element Plus 2.7 (unplugin-auto-import 按需引入)、Pinia 2。
功能列表:
- 后端视图:解析 Django
.env,按内置 schema 校验必填项与格式 (如QXJ_KEY_ENC_KEY、SECRET_KEY、Redis / DB 配置等); - 前端视图:解析 Vite
.env,校验VITE_*变量; - 对比视图:前后端配置差异对照;
- 密钥处理:密钥类字段单独输入,不随明文导出;
- 粘贴导入 / 导出:直接粘贴整份配置,一键复制合并后输出。
目录结构:
src/
├── core/ # parser/schema/validator/merger/secret/types
├── components/ # EnvEditor / DiffViewer / IssuePanel / PasteImport 等
├── views/ # BackendView / FrontendView / CompareView
├── stores/ # envStore / issueStore
├── App.vue
└── main.ts2
3
4
5
6
7
安装运行:
npm install
npm run dev # http://localhost:5174(strictPort)
npm run build # vue-tsc 类型检查 + vite build
npm run preview2
3
4
后端配置说明见 qxj-backend-admin(两级 .env 覆盖规则)。
3. env-config-helper-cli(Node CLI)
定位:校验 qxj-backend-admin(Django 后端)和 qxj-frontend-admin (Vue3 前端).env 配置一致性的零依赖 CLI 工具,包 @nsp/qxj-env-config-helper-cli v1.0.0,bin 名 qxj-envcheck。
技术栈:零运行时依赖(仅用 Node 内置模块 fs/path/readline/crypto/process),ESM("type": "module"), ANSI 彩色输出直接用转义序列,vue-cli 风格命令,Node.js >= 18。
安装:
cd qxj-backend-frontend-env-config-helper-cli
npm link # 之后即可全局使用 qxj-envcheck
qxj-envcheck --help
# 或不安装直接运行:
node src/bin/cli.mjs --help2
3
4
5
5 个命令:
| 命令 | 说明 |
|---|---|
check | 检查单一项目配置完整性(必填缺失、占位值、格式错误) |
validate | 校验前后端配置一致性(核心命令,8 条跨项目关系规则) |
diff | 对比 .env 与 .env.example 差异 |
show | 格式化展示当前生效配置(密钥自动脱敏) |
init | 交互式从 .env.example 初始化 .env |
通用选项:--backend / --frontend 指定项目、--env <name> 指定环境 (development / production,默认 development)、-h 帮助。 退出码:0 无问题 / 1 有警告 / 2 有错误(可直接接入 CI)。
使用示例:
qxj-envcheck check --backend # 检查开发环境后端配置
qxj-envcheck check --backend --env production # 检查生产环境后端配置
qxj-envcheck validate # 前后端一致性校验(核心)
qxj-envcheck diff --backend # 与模板差异对比
qxj-envcheck show --backend # 生效配置脱敏展示
qxj-envcheck init --backend # 交互式生成 .env2
3
4
5
6
check 输出形如「[错误] 必填变量 SECRET_KEY 未设置(生产环境必须配置) / [警告] 变量 REDIS_URL=redi***379/0 仍为占位值」,末尾汇总错误/警告/提示数。
8 条跨项目关系规则(validate):
| 规则 | 名称 | 说明 |
|---|---|---|
| R1 | 登录模式联动 | ALLOW_JSON_LOGIN 与 VITE_ALLOW_JSON_LOGIN 必须相等 |
| R2 | 验证码场景联动 | 后端启用验证码时前后端 SCENE_ID 必须一致 |
| R3 | 专用设备门禁联动 | 前端要求专用设备时后端不应允许跳过手机号 |
| R4 | CORS 跨域端口校验 | 前端端口必须在后端 CORS 白名单中 |
| R5 | 阿里云 AccessKey 校验 | 启用验证码或阿里云短信时 AK 必须配置 |
| R6 | 短信通道配置校验 | 根据 SMS_PROVIDER 检查必填项 |
| R7 | 跳过校验开关检查 | 生产环境不得开启跳过开关 |
| R8 | 前端锁屏密钥校验 | VITE_LOCK_ENCRYPT_KEY 必须设置且非占位 |
测试与结构:node --test tests/(parser / validator / relationships 三个测试文件, 可单跑)。源码 src/ 下分 bin/cli.mjs(入口路由)、core/(parser/merger/writer/ validator)、schema/(backend/frontend 变量元数据 + relationships 8 条规则)、 commands/(5 个命令实现)。
4. qxj-frontend-web-qrcode-helper(二维码助手)
定位:纯 HTML/CSS/JS 单文件页面(无需构建),用于手工构造 Pad 浏览器端 扫码绑定所需的两类二维码,与后端、Pad 端的生成/解析口径严格对齐。 二维码库用 CDN 上的 qrcodejs@1.0.0(离线环境需把库内联进 HTML)。
使用:直接用浏览器打开 index.html(需联网加载 CDN)。
Tab 1:服务器/设备公钥 QR(# 分隔格式)
- 文本格式:
ID#时间戳#公钥; - 与后端
apps/keymgr/models.py的ServerKey.to_qrcode_string()一致, 与 Pad 端DeviceRegisterUtil.parseServerQr()解析逻辑对齐; - 校验规则:ID 为 36 字符 UUID;公钥为 128 位 hex(自动转大写,不含
04前缀); 时间戳为 10 位秒级 Unix 时间戳(页面自动生成,可手动刷新); - 「服务器公钥」用于 Pad 扫码绑定服务器;「设备公钥」用于 Pad 导出设备配置时 扫码登记。
Tab 2:服务器配置 QR(JSON 格式)
{
"name": "测试服务器",
"backendUrl": "https://117.72.72.201:4607",
"frontendUrl": "https://117.72.72.201:3006",
"businessBackendUrl": "https://117.72.72.201:8080",
"businessFrontendUrl": "https://117.72.72.201:5173",
"serverId": "",
"serverPubKey": ""
}2
3
4
5
6
7
8
9
- 与 Pad 端
DeviceRegisterUtil.parseServerConfigJson()对齐(单对象或数组均可解析); name、backendUrl必填,其余可空;serverId/serverPubKey填入即生成 「已绑定」配置;- 生成后页面显示二维码和原始字符串,可复制核对。
注意:后端生成服务器公钥二维码的官方途径仍是 python -m tools.setup qrcode, 本工具用于手工构造 / 测试场景。
5. qxj-wireshark-binary-parser(QXJ 协议解析三端对齐)
5.1 定位与三端对齐关系
QXJ 自定义二进制协议的解析实现分布在三端,共享同一套协议常量与帧布局, 保持同步修改:
| 端 | 文件 | 职责 | 解析特点 |
|---|---|---|---|
| 后端 | [packet_parser.py](file:///c:/Users/etsuyou/Desktop/qxj-project/qxj-backend-admin/apps/keymgr/utils/packet_parser.py) | QxjFrameCodec 组帧 + parse_init_frame/parse_ack_frame 安全校验 + describe_frame 日志解析 | 安全边界:进入 C 库前做严格结构校验(长度/时间戳/密钥长度白名单),拒绝畸形帧 |
| Pad | [QxjFrameUtil.ets](file:///c:/Users/etsuyou/Desktop/qxj-project/qxj_harmony_next_pad_nsp_browser/common/src/main/ets/util/QxjFrameUtil.ets) | QxjFrameUtil 组帧 + parseHeader/parseAlarmFrame 解析 + describeFrame 日志解析 | 展示性解析:不做结构校验,带字节偏移区间输出,供日志对照协议文档 |
| Wireshark | [qxj.lua](file:///c:/Users/etsuyou/Desktop/qxj-project/qxj-wireshark-binary-parser/wireshark/plugins/qxj.lua) | dissector 把协议帧逐字段展开为 Wireshark 协议树 | 抓包现场解析:协议特征校验后按帧类型分发,hex 字段冒号格式展示 |
三端协议常量(主命令码、子命令功能码、加密认证模式、帧头字段长度/偏移)严格对齐, 修改时需三端同步。其中后端 packet_parser.py 和 Pad 端 QxjFrameUtil.ets 的 describe_frame/describeFrame 输出格式一致,均带 [offset:start:end] 字节偏移区间。
5.2 协议帧总体结构
HTTP POST body(Content-Type: application/octet-stream)
└── [自定义通信头 64B] + [数据域] (+ [16B GCM tag,仅加密完保帧;握手帧无])2
- 握手帧(INIT/RESP/ACK/TOKEN):
enc_auth=0x00(明文无签名),IV 全零,无帧尾 - 报警帧(ALARM):
enc_auth=0x00,IV 随机,无帧尾 - 加密完保帧(SECURE):
enc_auth=0x40(SM4-GCM),IV 随机,帧尾 16B GCM tag
5.2.1 安全机制分层
QXJ 协议采用三层安全架构——传输层、协议帧层、密码学层各司其职:
5.3 64B 自定义通信头布局(全部大端)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | version | 版本号(当前固定 0x01) |
| 1 | 1 | main_cmd | 主命令码(见下表) |
| 2 | 2 | sub_cmd | 子命令码(大端):最高位=方向(0=上行 Pad→服务器,1=下行 服务器→Pad),低 15 位=功能码 |
| 4 | 2 | total_len | 总长度(大端):全帧长度(含 64B 头 + 数据域 + GCM tag) |
| 6 | 2 | seq | 帧序号(大端) |
| 8 | 36 | sender_id | ASCII UUID(上行=ID_A 设备ID,下行=ID_B 服务器ID),不足右侧补 0x00 |
| 44 | 1 | enc_auth | 高 4 位加密模式 / 低 4 位认证模式(见下表) |
| 45 | 16 | iv | IV(握手帧全零,加密完保帧随机) |
| 61 | 3 | reserved | 保留字段 |
enc_auth 字段位置说明
enc_auth 位于 IV 之前(偏移 44),与 C 库 libnspsm2handshake.so 的 64B 帧头 布局一致。旧 32B 帧头时代 enc_auth 与 IV 顺序相反,已废弃。
5.4 功能码总表(协议文档 v8 表 7-1)
主命令码:
| 值 | 名称 | 说明 |
|---|---|---|
| 0x00 | 密钥协商 | SM2 四步握手(INIT→RESP→ACK→TOKEN) |
| 0x01 | 报警 | 握手失败/异常通知(上/下行同功能码) |
| 0x02 | 加密完保通信帧 | SM4-GCM 加密的业务数据 |
子命令码:最高位为上下行标识(0x8000),低 15 位为功能码。下行帧 sub_cmd = 0x8000 | 功能码。
| 主命令码 | 功能码 | sub_cmd(上行) | sub_cmd(下行) | 帧类型 |
|---|---|---|---|---|
| 0x00 | 0x0001 | 0x0001 | — | INIT(发起密钥协商) |
| 0x00 | 0x0002 | — | 0x8002 | RESP(客户端认证和密钥交换) |
| 0x00 | 0x0003 | 0x0003 | — | ACK(服务器认证、密钥确认) |
| 0x00 | 0x0004 | — | 0x8004 | TOKEN(会话密钥确认、token 生成) |
| 0x01 | 0x0001 | 0x0001 | 0x8001 | 不满足椭圆曲线方程 |
| 0x01 | 0x0002 | 0x0002 | 0x8002 | 计算椭圆曲线点失败 |
| 0x01 | 0x0003 | 0x0003 | 0x8003 | 密钥确认失败 |
| 0x01 | 0x0004 | 0x0004 | 0x8004 | 意外消息 |
| 0x01 | 0x0005 | 0x0005 | 0x8005 | 结束通知 |
| 0x02 | 0x0001 | 0x0001 | — | 加密完保消息(上行) |
| 0x02 | 0x0002 | — | 0x8002 | 加密完保消息(下行) |
5.5 加密认证模式(enc_auth 字段值域)
| 高 4 位 | 加密模式 | 低 4 位 | 认证模式 | enc_auth 值 | 适用场景 |
|---|---|---|---|---|---|
| 0x0 | 未加密 | 0x0 | 未签名 | 0x00 | 握手帧、报警帧 |
| 0x4 | SM4-GCM | 0x0 | 未签名 | 0x40 | 加密完保通信帧 |
| 0x1 | SM4-CBC | 0x1 | SM2签名 | 0x11 | 预留(协议定义,当前未使用) |
| 0x2 | SM4-ECB | 0x2 | SM4-CMAC | 0x22 | 预留(协议定义,当前未使用) |
| 0x3 | SM4-CTR | — | — | — | 预留(协议定义,当前未使用) |
5.6 各帧类型数据域布局
SM2 四步握手流程总览:
INIT(上行,main_cmd=0x00,sub_cmd=0x0001,总长 280B = 64B 头 + 216B 数据域):
| 字段 | 长度 | 说明 |
|---|---|---|
| total_len | 2 | 数据域长度(兼容 216 和 214 两种口径) |
| ida_len | 2 | ID_A 长度(固定 36) |
| idb_len | 2 | ID_B 长度(固定 36) |
| session_key_len | 2 | 会话密钥长度(白名单:16=SM4-128 / 32=SM4-256) |
| timestamp | 8 | 毫秒级 Unix 时间戳(允许 ±10 分钟时钟偏移) |
| ida | 36 | ID_A(设备 UUID,ASCII) |
| idb | 36 | ID_B(服务器 UUID,ASCII) |
| ra | 64 | SM2 临时公钥(椭圆曲线点,hex) |
| sign | 64 | SM2 签名(hex) |
RESP(下行,sub_cmd=0x8002,总长 162B = 64B 头 + 98B 数据域):
| 字段 | 长度 | 说明 |
|---|---|---|
| total_len | 2 | 数据域长度 |
| rb | 64 | 服务器 SM2 临时公钥(hex) |
| sb | 32 | 服务器会话密钥分量(hex) |
ACK(上行,sub_cmd=0x0003,总长 98B = 64B 头 + 34B 数据域):
| 字段 | 长度 | 说明 |
|---|---|---|
| total_len | 2 | 数据域长度 |
| sa | 32 | 客户端会话密钥分量(hex) |
TOKEN(下行,sub_cmd=0x8004,总长 146B = 64B 头 + 82B 数据域):
| 字段 | 长度 | 说明 |
|---|---|---|
| total_len | 2 | 数据域长度 |
| ida_len | 2 | ID_A 长度(固定 36) |
| hmac_len | 2 | HMAC 长度 |
| ida | 36 | ID_A(设备 UUID,ASCII) |
| expiration_ms | 8 | 有效时长(毫秒数,如 3600000=1 小时;非时间戳) |
| hmac | 变长 | HMAC(hex) |
TOKEN 帧字段顺序
hmac_len 在 expiration_ms 之前——这是 C 库 libnspsm2handshake.so 实测输出布局,与协议文档 v8 的文字描述顺序不同。三端解析均按实测布局对齐。
ALARM(上/下行,main_cmd=0x01):
| 字段 | 长度 | 说明 |
|---|---|---|
| total_len | 2 | 错误信息长度 |
| message | 变长 | ASCII 错误信息(非 ASCII 字符替换为 ?) |
SECURE(上/下行,main_cmd=0x02):
| 字段 | 长度 | 说明 |
|---|---|---|
| 密文 | 变长 | SM4-GCM 加密的业务数据 |
| GCM tag | 16 | 帧尾认证码(hex) |
5.7 后端安全校验(INIT/ACK 进入 C 库前)
后端 parse_init_frame/parse_ack_frame 在将帧交给 C 库 libnspsm2handshake.so 之前做严格结构校验,拒绝畸形帧进入 native 层:
- 帧总长度:必须与固定布局严格一致(INIT=280B,ACK=98B),不允许多余/截断
- total_len:INIT 兼容 216 和 214 两种口径(含/不含 total_len 自身 2B),ACK 必须为 34
- ida_len/idb_len:必须为 36
- session_key_len:必须在白名单 (16, 32) 内(SM4-128/SM4-256)
- timestamp:必须 > 0,且与服务器时间偏移 ≤ ±10 分钟(防重放/防畸形)
校验失败返回 HTTP 400 + 报警帧(QxjFrameCodec.build_alarm_frame), 不会进入 C 库调用。
5.8 组帧能力
后端 QxjFrameCodec 和 Pad 端 QxjFrameUtil 均提供组帧能力(三端同步):
build_header:组装 64B 帧头(version + main_cmd + sub_cmd + total_len + seq + sender_id(36B) + enc_auth + IV(16B) + reserved(3B))build_frame:64B 头 + 数据域,total_len 按全帧长度填充build_alarm_frame:报警帧(main_cmd=0x01,子命令=0x8000|功能码), IV 默认随机生成(os.urandom/cryptoFramework.createRandom), 错误信息 ASCII 编码(非 ASCII 替换为?)randomize_iv:将帧头 IV 替换为随机值(握手帧 IV 由 C 库生成全零, 协议文档要求随机填充;握手阶段 IV 不参与计算,替换无副作用)
握手帧(INIT/RESP/ACK/TOKEN)由 C 库 libnspsm2handshake.so 生成, 后端和 Pad 端不自行组帧。
5.9 加密完保通信帧(SECURE)加解密流程
握手完成后,Pad 端使用协商出的会话密钥进行 SM4-GCM 加解密([SessionCrypto.ets](file:///c:/Users/etsuyou/Desktop/qxj-project/qxj_harmony_next_pad_nsp_browser/features/qxj_sdk/src/main/ets/utils/SessionCrypto.ets)):
加密(上行):
- 用会话密钥 + 随机 IV 对明文做 SM4-GCM 加密,得到密文 + 16B GCM tag
- 组装 64B v2 帧头:
main_cmd=0x02,sub_cmd=0x0001,enc_auth=0x40,IV 填入帧头 - 帧结构:
[64B 头] + [密文] + [16B GCM tag]
解密(下行):
- 解析 64B v2 帧头,提取 IV、
enc_auth(应为 0x40) - 数据域 = 密文 + 帧尾 16B GCM tag
- 用会话密钥 + IV 解密密文,校验 GCM tag
5.10 Wireshark 插件安装与使用
安装(复制单文件插件后重启 Wireshark,Help → About → Plugins 应见 qxj):
- Windows:
%APPDATA%\Wireshark\plugins\ - Linux:
~/.local/share/wireshark/plugins/ - macOS:
~/.config/wireshark/plugins/(或~/Library/Application Support/Wireshark/plugins/) - 也可放安装目录下
plugins/(需管理员权限)
自动识别条件(满足任一):
- HTTP 请求/响应且
Content-Type: application/octet-stream(通过media_typedissector table 注册,Wireshark 4.x 表名;旧版 fallbackhttp.content_type) - 裸 TCP 流端口 4607 / 14607 / 8080(已注册)
协议特征校验(dissector 入口,不匹配则不认领,返回 0):
- 版本号必须为 0x01
- 主命令码必须为 0x00 / 0x01 / 0x02
此校验避免将 4607 端口上的 TLS 流量(版本号 0x14/0x17)误识别为 QXJ。
端口未注册时可在 Analyze → Decode As… 临时指定为 QXJ。
协议树展示:包详情中「QXJ 自定义协议」展开后可见:
- 帧头子树:版本号、主命令码(带中文名)、子命令码(带方向+功能名)、 总长度、帧序号、发送方ID(ASCII UUID)、加密认证模式(拆分加密/认证)、 IV(标注全零/非零)、保留;
- 数据域子树:按 INIT/RESP/ACK/TOKEN/ALARM/SECURE 展开;时间戳/有效时长为 8 字节大端整数附可读时间或毫秒值;SM2 公钥/签名/会话密钥/HMAC/GCM tag 等 hex 字段以
XX:XX:...冒号格式展示;SECURE 帧将密文与帧尾 16B GCM tag 分别展示。
5.11 过滤字段
前缀均为 qxj.:
| 类别 | 字段 |
|---|---|
| 帧头 | qxj.version、qxj.main_cmd、qxj.sub_cmd、qxj.total_len、qxj.seq、qxj.sender_id、qxj.enc_auth、qxj.iv、qxj.reserved |
| INIT | qxj.init.total_len、qxj.init.ida_len、qxj.init.idb_len、qxj.init.session_key_len、qxj.init.timestamp、qxj.init.ida、qxj.init.idb、qxj.init.ra、qxj.init.sign |
| RESP | qxj.resp.total_len、qxj.resp.rb、qxj.resp.sb |
| ACK | qxj.ack.total_len、qxj.ack.sa |
| TOKEN | qxj.token.total_len、qxj.token.ida_len、qxj.token.hmac_len、qxj.token.ida、qxj.token.expiration_ms、qxj.token.hmac |
| ALARM | qxj.alarm.total_len、qxj.alarm.message |
| SECURE | qxj.secure.ciphertext、qxj.secure.gcm_tag |
5.12 项目结构与测试
src/ # 模块化源码:qxj_proto(主入口/注册) / qxj_constants(命令码名称表)
# / qxj_header(64B 帧头) / qxj_data_layouts(6 种帧数据域) / qxj_utils
wireshark/plugins/qxj.lua # 合并单文件(约 27KB,部署到插件目录)
tests/sample_frames/ # 5 个完整 hex 样本:init(280B)/resp(162B)/ack(98B)/token(146B)/secure(112B)
tests/verify.lua # 工具函数测试(16 个用例)2
3
4
5
lua tests/verify.lua # 需 Lua 5.3+
# 或:tshark -X lua_script:tests/verify.lua -r <任意 pcap>2
样本帧可用 text2pcap 包成 pcap 或直接用 Wireshark 导入对照。
5.13 源码模块职责
| 模块 | 职责 |
|---|---|
qxj_constants.lua | 协议常量定义(主/子命令码、加密认证模式、名称表) |
qxj_header.lua | 64B 帧头解析(9 个字段逐字段展开,返回 frame_type 供数据域分发) |
qxj_data_layouts.lua | 6 种帧类型数据域解析(INIT/RESP/ACK/TOKEN/ALARM/SECURE 的字段顺序、长度、渲染方式) |
qxj_utils.lua | 工具函数(to_hex/to_ascii/render_int/format_hex_string/format_timestamp_ms/render_enc_auth) |
qxj_proto.lua | 主入口:创建 Proto('qxj')、定义 ProtoField、实现 dissector、注册 HTTP/TCP |
5.14 兼容性
Wireshark 4.x(内置 Lua 5.4;插件用纯算术替代位运算,向下兼容 Lua 5.2+); 所有 ProtoField 用 qxj. 前缀,名称表为中文。实现上的技术处理:
- 协议特征校验:dissector 入口校验版本号必须为 0x01、主命令码必须为 0x00/0x01/0x02,不匹配则不认领(返回 0),避免将 TLS 等同端口非 QXJ 流量 误识别为 QXJ。
- HTTP Content-Type 注册表名:Wireshark 4.x 将
http.content_type表 改名为media_type,插件优先使用media_type,fallback 兼容旧版http.content_type。 - 容器子树用
ProtoField.bytes替代ProtoField.none(避免FT_NONE not yet supported);uint64字段不传显式 Lua number 而由 Wireshark 从 tvb 自动取大端值(避免段错误)。
5.15 HTTPS 抓包说明
QXJ 协议帧作为 HTTP POST body 传输,若后端使用 HTTPS(TLS),Wireshark 无法直接 看到明文帧。此时可:
- 临时切 HTTP 抓包:后端临时关闭 HTTPS,抓完再切回
- TLS 密钥日志:设置
SSLKEYLOGFILE环境变量,在 WiresharkPreferences → Protocols → TLS → (Pre)-Master-Secret log filename配置密钥日志 文件,Wireshark 自动解密 TLS 流量 - mitmproxy 代理:用 mitmproxy 做中间人代理抓 Pad 流量,安装 mitmproxy CA 证书后可直接在代理界面看到解密的 HTTP body
5.16 实际抓包示例
一份完整的 SM2 四步握手抓包(HTTP 明文,4607 端口),插件识别出 4 个 QXJ 帧:
| Wireshark 帧 | 方向 | 帧类型 | sub_cmd | 发送方ID | 总长度 |
|---|---|---|---|---|---|
| Frame 1 | 上行 | INIT | 0x0001 | cfb4d397-...(设备) | 280B |
| Frame 5 | 下行 | RESP | 0x8002 | e1fe4c95-...(服务器) | 162B |
| Frame 7 | 上行 | ACK | 0x0003 | cfb4d397-...(设备) | 98B |
| Frame 11 | 下行 | TOKEN | 0x8004 | e1fe4c95-...(服务器) | 146B |
过滤表达式 qxj 可快速定位所有 QXJ 帧;qxj.main_cmd == 0x00 过滤密钥协商帧; qxj.sub_cmd == 0x8002 过滤所有下行 RESP 帧。
6. python-mock-test-tool(PC 全流程模拟)
qxj-harmony-next-pad-browser-python-mock-test-tool:在 PC 上用 Python 完整模拟 一台鸿蒙平板,对 qxj-backend-admin 走通「生成密钥 → 环境准备 → SM2 握手 → 加密登录 → Token 管理」全链路,对接后端默认 http://127.0.0.1:4607。 五个分步目录 00_generate_keypair ~ 04_token_management 按序执行。 git 状态为 detached HEAD(fa9c451,共 9 个 commit,无本地分支)。
详细运行环境、命令与目录说明见教学演示 Mock。
7. hpack(鸿蒙内测签名打包分发工具)
定位:第三方开源工具(iHongRen/hpack, Apache 2.0),专为鸿蒙 HarmonyOS 打造的内测分发工具——完成配置后一行命令完成 应用的构建、签名、上传、分发、安装。本项目中用于 Pad 浏览器 HAP 的内测打包分发。
技术栈 / 环境要求:Python 3.10+ 运行环境;hvigorw、hdc(DevEco Studio 自带, 非 DevEco 终端需配置 DEVECO_SDK_HOME 等环境变量);JDK 17+(签名工具依赖)。
安装:
pip install harmony-hpack
# 国内镜像(清华源):
pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple harmony-hpack
hpack -h # 验证安装2
3
4
准备工作:三个证书文件放 hpack/sign/——发布证书 .cer(AGC 颁发)、 内部测试 Profile .p7b(含包名/证书/权限/设备列表)、公私钥 .p12; 需先阅读华为官方「HarmonyOS 应用内部测试」文档。
快速开始:
hpack init # 项目根目录初始化:生成 hpack/{config.py, Packfile.py, sign/}
# 编辑 hpack/config.py:DeployDomain/BaseURL/AppName/签名配置(Alias/KeyPwd/KeystorePwd)/
# IndexTemplate(default/simple/tech/cartoon/tradition/custom)/ Product / Debug 等
hpack p "修复了一些已知问题" # 打包签名 + 上传,desc 可选
hpack pr "发版包说明" # 强制 release 包
hpack pd "调试包说明" # 强制 debug 包
hpack i -default # 安装指定 product 产物到当前连接设备2
3
4
5
6
7
功能特性:自动打出所有 hap/hsp 并签名;多 Product 支持;自动生成已签名 manifest.json5;自动生成分发 index 页(多种 HTML 模板,支持自定义)与下载二维码; 自定义上传(阿里云 OSS、蒲公英示例);历史打包页面 history.html; webhook 通知(钉钉/企微/飞书示例);hpack targets 显示连接设备、hpack -u 查看 UDID;hpack sign 对未签名 .app/.hap/.hsp/包目录签名;hpack i 命令安装已签名包。
打包回调:打包信息(bundle_name / version / size / manifest_url / index_url / qrcode base64 等)通过 Packfile.py 的 didPack(packInfo) 回调传出, 在其中编写上传逻辑;产物保存在 hpack/build/{product}/。
8. sample_in_harmonyos(华为官方参考工程)
「HMOS代码工坊」官方参考工程集(master 分支):4 个 entry HAP + 8 个 HAR, API 23/24,演示一多布局、窗口适配等。团队只作架构参考,不参与交付。
- 本地领先 origin 一个提交
64b45b8(本机签名材料的绝对路径配置),通常不应推送; - README.md 完整,README.en.md 为空壳。
9. qxj-vitepress-page(本文档站)
VitePress 技术文档站(Vue 3 + VitePress + Mermaid),本页面的载体。main 分支, pnpm。结构:repositories/(仓库全景)、guide/(快速入门)、architecture/、 features/、api/、development/。
pnpm dev # 本地预览
pnpm build # 构建验证(输出 .vitepress/dist)2
10. qxj_harmony_next_sdk(空占位仓)
仅含一行标题的 README 与 Node 模板 .gitignore,无鸿蒙 SDK 实质内容; 浏览器侧鸿蒙 SDK 目前以内置 HAR features/qxj_sdk(@nsp/qxj-sdk)形态存在。
相互关系与在项目中的角色
联调排障三件套 配置保障
┌─────────────────────────────────┐ ┌──────────────────────────┐
│ sm2-jwt 解析器:token 是否有效 │ │ env-config-helper-cli: │
│ (外层 SM2 验签 / 内层 HMAC) │ │ CI/终端校验 .env 一致性 │
│ python-mock-test-tool:无真机 │ │ env-config-helper-web: │
│ 走通握手/登录/token 全链路 │ │ 浏览器可视化校验与合并 │
│ wireshark 解析器:抓包逐字段核对 │ └──────────────────────────┘
└─────────────────────────────────┘ │
│ 都围绕 qxj-backend-admin / Pad 浏览器主链路
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ qrcode-helper:构造 Pad 扫码绑定的服务器公钥码/配置码 │
│ hpack:Pad 浏览器 HAP 的内测签名打包分发 │
│ sample_in_harmonyos:鸿蒙官方参考工程(仅架构参考) │
└──────────────────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 排查 token 类问题(后端返回 -3 SM2 失败 / -4 过期 / -6 内层 HMAC 失败): 先把 access token 粘进 sm2-jwt 解析器看外层签名与 exp,再填会话密钥验内层 HMAC;
- 排查握手 / 加密帧问题:用 python-mock-test-tool 分步复现,抓包后用 Wireshark 解析器对照 INIT/RESP/ACK/TOKEN/ALARM/SECURE 各字段;
- 起新环境 / 部署前:先
qxj-envcheck validate跑 8 条联动规则, 可视化核对用 Web 版; - 新 Pad 接入:管理员
python -m tools.setup qrcode出官方公钥码, 手工构造/测试场景用 qrcode-helper;内测发版走 hpack。
维护注意
- repo 工具管理的仓
.git为符号链接,Windows PowerShell 下部分 git 命令报fatal: error reading '.git'——git 操作统一走 WSL; tmp/paste.txt、截图压缩包等为工作文件,不入库;- sm2-jwt 解析器与 qrcode-helper 均依赖 CDN(sm-crypto / qrcodejs), 离线环境使用需把库内联进 HTML。