# `DataEncoder` 技术文档 ```markdown # DataEncoder - 安全数据编码与解码模块 ## 概述 `DataEncoder` 是一个用于安全传输数据的 Python 类,提供数据加密、签名、压缩和结构化打包功能。它结合了 **RC4 对称加密** 和 **RSA 非对称加密**,确保数据在传输过程中的机密性、完整性和身份验证。 该类适用于点对点通信场景,支持动态获取对方公钥,并使用本地私钥进行签名,接收方通过公钥验证签名并解密数据。 --- ## 依赖说明 ### 第三方库 - `ujson`(可选):高性能 JSON 库,若不可用则回退至标准库 `json` - `zlib`:内置,用于数据压缩 - `struct`:内置,用于二进制数据打包/解包 ### 内部模块 - `appPublic.rsawrap.RSA`:封装 RSA 加密/解密、签名/验签功能 - `appPublic.rc4.RC4`:实现 RC4 流加密算法 - `appPublic.uniqueID.getID`:生成唯一 ID(用作 RC4 密钥) > 注:未启用 Brotli 压缩(相关导入被注释) --- ## 核心常量 | 常量 | 值 | 含义 | |------|----|------| | `DATA_TYPE_BYTES` | `1` | 数据类型为原始字节流 | | `DATA_TYPE_STR` | `2` | 数据类型为字符串(UTF-8 编码) | | `DATA_TYPE_JSON` | `3` | 数据类型为 JSON 对象(序列化后为 UTF-8 字节) | --- ## `DataEncoder` 类定义 ### 初始化方法:`__init__(self, myid, func_get_peer_pubkey, private_file=None)` #### 参数: | 参数名 | 类型 | 描述 | |--------|------|------| | `myid` | str 或 any | 当前节点的唯一标识符 | | `func_get_peer_pubkey` | callable | 回调函数,用于根据 `peer_id` 获取对方公钥 | | `private_file` | str, optional | 私钥文件路径;若未提供,则自动生成新密钥对 | #### 功能: - 创建或加载 RSA 密钥对(私钥 + 公钥) - 初始化内部状态: - `self.public_keys`: 缓存其他节点的公钥 - `self.rsa`, `self.rc4`: 实例化加解密工具 #### 示例回调函数签名: ```python def get_pubkey(peer_id): # 返回对应的 RSA 公钥对象 return rsa_public_key_obj ``` --- ## 核心方法 ### `identify_datatype(data)` 自动识别输入数据类型并转换为字节格式。 #### 返回值: 元组 `(data_type, encoded_bytes)` 其中 `data_type` 为上述三种之一。 | 输入类型 | 输出类型 | 处理方式 | |---------|----------|--------| | `bytes` | `DATA_TYPE_BYTES` | 直接返回 | | `str` | `DATA_TYPE_STR` | `.encode('utf-8')` | | 其他(如 dict/list) | `DATA_TYPE_JSON` | `json.dumps().encode('utf-8')` | --- ### `pack(peer_id, data, uncrypt=False)` 将数据加密、签名并打包成安全格式发送给指定 `peer_id`。 #### 参数: | 参数 | 描述 | |------|------| | `peer_id` | 接收方的身份 ID | | `data` | 待发送的数据(任意类型) | | `uncrypt` | 是否跳过加密(调试用途),仅压缩不加密 | #### 打包流程: 1. 调用 `identify_datatype(data)` 获取类型和字节数据。 2. 若 `uncrypt=True`,直接构造明文包并返回压缩结果。 3. 否则: - 生成随机 RC4 密钥(`getID()`) - 使用接收方公钥加密该密钥(RSA 加密) - 使用 RC4 加密原始数据 - 构造 struct 格式字符串(含长度信息) - 将以下字段按顺序打包: 1. **格式头(18字节)**:描述后续结构 2. **数据类型(1字节)** 3. **加密后的数据** 4. **加密后的 RC4 密钥** 5. **数字签名(覆盖以上所有内容)** - 使用 `zlib.compress()` 压缩整个包 - 返回压缩后的二进制数据 #### 结构体格式示例: 假设加密数据长 1024 字节,密钥长 256 字节,则格式为: ``` '18s b1024s256s256s' ``` 最后一个 `256s` 是签名(固定长度由 RSA 决定)。 > 签名范围包括:头部 + 类型 + 加密数据 + 加密密钥 --- ### `unpack(peer_id, data)` 解包并还原从 `peer_id` 收到的安全数据。 #### 参数: | 参数 | 描述 | |------|------| | `peer_id` | 发送方的身份 ID | | `data` | 接收到的压缩二进制数据 | #### 解包流程: 1. 解压 `zlib.decompress(data)` 2. 判断是否为无加密模式(前 18 字节全为 `\x00`): - 是 → 按简单格式解析(仅类型+数据),返回明文 3. 否则进入安全解包流程: - 提取前 18 字节作为格式字符串(去除尾部 `\x00`) - 解析剩余数据得到:类型、加密数据、加密密钥、签名 - 验证签名(使用发送方公钥) - 若失败 → 抛出异常 `'data sign verify failed'` - 成功后: - 用本机私钥解密 RC4 密钥 - 用 RC4 解密数据 - 根据类型还原为原始数据(bytes / str / json) - 返回解码后的原始数据 --- ### 加解密辅助方法 | 方法 | 功能 | |------|------| | `encode_data(peer_pubkey, data)` | 使用 RC4 加密数据,RSA 加密密钥 | | `decode_data(data, encoded_key)` | RSA 解密密钥,再用 RC4 解密数据 | | `sign_data(data)` | 使用本机私钥对数据签名 | | `verify_sign(data, sign, pubkey)` | 使用对方公钥验证签名有效性 | --- ### 公钥管理方法 | 方法 | 功能 | |------|------| | `my_text_publickey()` | 获取本机公钥的文本表示(Base64 编码等) | | `get_peer_pubkey(peer_id)` | 获取某 peer 的公钥(缓存或调用回调函数) | | `set_peer_pubkey(peer_id, pubkey)` | 设置某 peer 的公钥对象 | | `set_peer_text_pubkey(peer_id, text_pubkey)` | 从文本形式设置公钥(自动解析) | | `get_peer_text_pubkey(peer_id)` | 获取某 peer 的公钥文本形式(需已存在) | | `exist_peer_publickeys(peer_id)` | 查询是否已有某 peer 的公钥 | --- ## 数据格式详解 ### 安全包结构(压缩前) | 偏移 | 字段 | 长度 | 说明 | |------|------|-------|------| | 0 | 格式描述字符串 | 18 字节 | 如 `'b1024s256s256s\x00...'` | | 18 | 数据类型 | 1 字节 | `1=bytes`, `2=str`, `3=json` | | 19 | 加密数据 | 可变 | RC4 加密后的 payload | | 19+len(d) | 加密密钥 | 可变 | RSA 加密后的 RC4 密钥 | | ... | 数字签名 | 固定(如 256) | RSA-PKCS1-V1_5 签名 | > 总长度 = 18 + len(d) + len(k) + len(s) ### 明文包结构(`uncrypt=True`) | 偏移 | 字段 | 长度 | 说明 | |------|------|-------|------| | 0 | 填充零 | 18 字节 | 全 `\x00` 表示无加密 | | 18 | 数据类型 | 1 字节 | 同上 | | 19 | 原始数据 | 可变 | 未经加密 | --- ## 使用示例 ### 初始化 ```python def fetch_pubkey(pid): # 实现获取 peer 公钥逻辑 return rsa_pubkey_obj encoder = DataEncoder( myid="node_A", func_get_peer_pubkey=fetch_pubkey, private_file="private.key" # 或 None 自动生成 ) ``` ### 发送加密数据 ```python msg = {"cmd": "hello", "time": 123} packed_data = encoder.pack("node_B", msg) # 发送 packed_data 到 node_B ``` ### 接收并解密 ```python received_data = ... # 来自网络的数据 decoded_msg = encoder.unpack("node_A", received_data) print(decoded_msg) # {'cmd': 'hello', 'time': 123} ``` ### 设置远程公钥(手动) ```python pubkey_text = "-----BEGIN PUBLIC KEY-----\n..." # PEM 或 Base64 文本 encoder.set_peer_text_pubkey("node_C", pubkey_text) ``` --- ## 注意事项 1. **安全性提醒**: - RC4 已被认为不安全,建议在非敏感环境使用或替换为 AES。 - RSA 密钥长度应 ≥ 2048 bits。 - 签名防止篡改,但不防重放攻击,需上层协议补充 nonce/timestamp。 2. **性能优化**: - 使用 `ujson` 提升 JSON 序列化速度。 - zlib 压缩有效减少传输体积。 3. **错误处理**: - 无法获取公钥时会抛出异常。 - 签名验证失败也会中断流程。 4. **扩展建议**: - 支持 Brotli 压缩以进一步减小体积(当前代码中被注释)。 - 添加 TTL、nonce、版本号等元数据字段。 --- ## 辅助函数:`quotedstr(s)` 将字符串中的特殊字符转义,主要用于日志输出或字符串嵌入。 ### 转义规则: | 原字符 | 替换为 | |--------|--------| | `"` | `\"` | | `\n` | `\\n` | > 其他字符保持不变 ### 示例: ```python quotedstr('He said: "Hello\nWorld"') # 输出: He said: \"Hello\\nWorld\" ``` --- ## 版本信息 - 作者:Auto-generated from source - 最后更新:根据所提供代码生成 - 许可:请参考项目整体 LICENSE ```