8.3 KiB
8.3 KiB
DataEncoder 技术文档
# 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 |
是否跳过加密(调试用途),仅压缩不加密 |
打包流程:
- 调用
identify_datatype(data)获取类型和字节数据。 - 若
uncrypt=True,直接构造明文包并返回压缩结果。 - 否则:
- 生成随机 RC4 密钥(
getID()) - 使用接收方公钥加密该密钥(RSA 加密)
- 使用 RC4 加密原始数据
- 构造 struct 格式字符串(含长度信息)
- 将以下字段按顺序打包:
- 格式头(18字节):描述后续结构
- 数据类型(1字节)
- 加密后的数据
- 加密后的 RC4 密钥
- 数字签名(覆盖以上所有内容)
- 使用
zlib.compress()压缩整个包 - 返回压缩后的二进制数据
- 生成随机 RC4 密钥(
结构体格式示例:
假设加密数据长 1024 字节,密钥长 256 字节,则格式为:
'18s b1024s256s256s'
最后一个 256s 是签名(固定长度由 RSA 决定)。
签名范围包括:头部 + 类型 + 加密数据 + 加密密钥
unpack(peer_id, data)
解包并还原从 peer_id 收到的安全数据。
参数:
| 参数 | 描述 |
|---|---|
peer_id |
发送方的身份 ID |
data |
接收到的压缩二进制数据 |
解包流程:
- 解压
zlib.decompress(data) - 判断是否为无加密模式(前 18 字节全为
\x00):- 是 → 按简单格式解析(仅类型+数据),返回明文
- 否则进入安全解包流程:
- 提取前 18 字节作为格式字符串(去除尾部
\x00) - 解析剩余数据得到:类型、加密数据、加密密钥、签名
- 验证签名(使用发送方公钥)
- 若失败 → 抛出异常
'data sign verify failed' - 成功后:
- 用本机私钥解密 RC4 密钥
- 用 RC4 解密数据
- 根据类型还原为原始数据(bytes / str / json)
- 返回解码后的原始数据
- 提取前 18 字节作为格式字符串(去除尾部
加解密辅助方法
| 方法 | 功能 |
|---|---|
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 | 原始数据 | 可变 | 未经加密 |
使用示例
初始化
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 自动生成
)
发送加密数据
msg = {"cmd": "hello", "time": 123}
packed_data = encoder.pack("node_B", msg)
# 发送 packed_data 到 node_B
接收并解密
received_data = ... # 来自网络的数据
decoded_msg = encoder.unpack("node_A", received_data)
print(decoded_msg) # {'cmd': 'hello', 'time': 123}
设置远程公钥(手动)
pubkey_text = "-----BEGIN PUBLIC KEY-----\n..." # PEM 或 Base64 文本
encoder.set_peer_text_pubkey("node_C", pubkey_text)
注意事项
-
安全性提醒:
- RC4 已被认为不安全,建议在非敏感环境使用或替换为 AES。
- RSA 密钥长度应 ≥ 2048 bits。
- 签名防止篡改,但不防重放攻击,需上层协议补充 nonce/timestamp。
-
性能优化:
- 使用
ujson提升 JSON 序列化速度。 - zlib 压缩有效减少传输体积。
- 使用
-
错误处理:
- 无法获取公钥时会抛出异常。
- 签名验证失败也会中断流程。
-
扩展建议:
- 支持 Brotli 压缩以进一步减小体积(当前代码中被注释)。
- 添加 TTL、nonce、版本号等元数据字段。
辅助函数:quotedstr(s)
将字符串中的特殊字符转义,主要用于日志输出或字符串嵌入。
转义规则:
| 原字符 | 替换为 |
|---|---|
" |
\" |
\n |
\\n |
其他字符保持不变
示例:
quotedstr('He said: "Hello\nWorld"')
# 输出: He said: \"Hello\\nWorld\"
版本信息
- 作者:Auto-generated from source
- 最后更新:根据所提供代码生成
- 许可:请参考项目整体 LICENSE