9.8 KiB
RC4 加密与密钥链技术文档
概述
本文档详细描述了一个基于 RC4 流加密算法 和动态密钥生成机制(KeyChain)的 Python 实现。该系统支持数据加解密、自动密钥轮换、时间敏感的密钥管理,并具备一定的容错能力以应对时钟偏差。
主要特性包括:
- 使用 RC4 算法进行流加密
- 引入随机盐值(salt)和 SHA-1 密钥扩展增强安全性
- 支持 Base64 编码输出
- 动态密钥生成(基于时间窗口)
- 容忍一定时间偏移的解密尝试(前向/后向周期补偿)
依赖模块
import time
import datetime
import random
import base64
from hashlib import sha1
注:
random模块当前未使用,可能是预留或冗余导入。
核心类说明
1. RC4 类 —— RC4 加密器
初始化方法:__init__(self, data_coding='utf8')
初始化 RC4 加密实例。
| 参数 | 类型 | 描述 |
|---|---|---|
data_coding |
str | 数据编码格式,默认为 'utf8',用于字符串与字节之间的转换 |
属性:
bcoding: 内部字节编码,固定为'iso-8859-1'(确保字节范围兼容)dcoding: 外部数据编码(默认 UTF-8)salt: 固定盐值b'AFUqx9WZuI32lnHk'(16 字节),用于密钥强化
私有方法:_crypt(self, data: bytes, key: bytes) -> bytes
执行标准 RC4 加密/解密操作。
参数:
data: 待处理的字节数据key: 密钥(bytes)
返回:
- 加密或解密后的字节串(经
'iso-8859-1'编码)
算法流程:
- 初始化长度为 256 的 S-box。
- 使用密钥调度算法(KSA)打乱 S-box。
- 使用伪随机生成算法(PRGA)逐字节异或加密。
⚠️ 注意:此实现中返回的是
.join(chr(...))后再.encode('iso-8859-1'),需注意字符边界问题。
方法:encode_bytes(self, bdata: bytes, key: bytes) -> bytes
对字节数据执行带盐值的加密。
流程:
- 计算密钥摘要:
sha1(key + self.salt).digest() - 使用上述密钥对
bdata执行_crypt - 将原始 salt 前缀附加到密文前(共 16 字节)
返回:
salt + encrypted_data(bytes)
方法:encode(self, data: str/bytes, key: str, encode=base64.b64encode) -> str
高层加密接口,支持字符串输入和最终编码。
参数:
data: 明文(字符串或字节)key: 密钥(字符串)encode: 可选编码函数(如base64.b64encode),设为None则不编码
行为:
- 若
data是字符串,则按dcoding编码为字节 - 调用
encode_bytes加密 - 若指定
encode函数,则对结果进行编码(如 Base64) - 返回编码后的字符串(UTF-8 解码)
✅ 示例:
rc = RC4() code = rc.encode("hello", "mykey")
方法:decode_bytes(self, data: bytes, key: bytes) -> bytes
解密由 encode_bytes 生成的数据。
步骤:
- 提取前 16 字节作为 salt(但实际未使用!应为 bug?)
- 使用
sha1(key + self.salt)生成密钥 - 对剩余部分调用
_crypt解密
❗⚠️ Bug 提示:虽然提取了 salt,但在密钥生成中仍使用固定的
self.salt,而非从输入读取的 salt。这导致无法正确处理不同 salt 的情况。
返回:
- 解密后的原始数据(bytes)
方法:decode(self, data: str/bytes, key: str, decode=base64.b64decode) -> str
高层解密接口。
参数:
data: 已加密并可选编码过的数据key: 解密密钥decode: 解码函数(默认 Base64)
行为:
- 若
data是字符串,先用dcoding编码成字节 - 若
decode存在,先解码(如 Base64 解码) - 调用
decode_bytes解密 - 结果以
dcoding解码为字符串返回
✅ 示例:
text = rc.decode(code, "mykey")
2. KeyChain 类 —— 时间同步密钥链
提供基于时间窗口的动态密钥管理和自动密钥匹配功能,适用于分布式环境下的安全通信。
初始化:__init__(seed_str, crypter=None, keylen=23, period=600, threshold=60, time_delta=0)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
seed_str |
str/bytes | - | 种子字符串,用于派生密钥 |
crypter |
RC4 实例 | None | 自定义加密器,若无则创建默认 RC4 |
keylen |
int | 23 | 生成密钥的字符长度 |
period |
int | 600 | 时间窗口周期(秒),即每 10 分钟换一次密钥 |
threshold |
int | 60 | 容差阈值(秒),判断是否接近周期边界 |
time_delta |
int | 0 | 时间偏移量(可用于调试或校准) |
内部属性:
keypool: 缓存已生成的密钥{indicator: key}timezone: 设置为 GMT 时区indicator: 当前时间所属的时间段起点(如每 600 秒一个区块)
方法:get_timestamp(self) -> int
获取当前 Unix 时间戳(减去 time_delta)
方法:get_indicator(ts=None) -> int
计算时间指示器(所在周期的起始时间)
例如:
period=600→ 每 10 分钟一个周期ts=1712345678→ indicator =(1712345678 // 600) * 600
返回:
- 当前时间所属周期的起始时间戳
方法:genKey(indicator) -> bytes
根据 indicator 从 seed_str 生成唯一密钥。
算法逻辑:
- 若缓存中已有对应密钥,直接返回
- 否则使用循环算法从
seed_str中按规则选取字符构造密钥:- 初始值
v = indicator - 每次取
j = v % keylen作为索引 - 从
seed_str[j]取字符追加 - 更新
v = v - (j + k1)*m + keylen - 直到生成
keylen长度的密钥
- 初始值
- 缓存密钥,并清理过期条目(早于
indicator - period的)
🔐 安全性:密钥依赖时间和种子,难以预测
方法:encode_bytes(bdata: bytes) -> bytes
使用当前时间对应的密钥加密数据。
流程:
- 获取当前
indicator - 生成对应密钥
key - 构造明文:
key + bdata(将密钥本身也加密传输) - 使用
crypter.encode_bytes(data, key)加密
📦 输出结构:
salt + encrypted(key + bdata)
方法:decode_bytes(data: bytes) -> bytes or None
智能解密:尝试用当前、上一周期、下一周期的密钥解密。
步骤:
- 获取当前
indicator和密钥 - 尝试解密
- 验证解密后数据前缀是否等于密钥(完整性校验)
- 如果失败且接近周期底部(即将切换),尝试上一周期密钥
- 如果接近顶部(刚过切换点),尝试下一周期密钥
返回:
- 成功:解密后的原始数据(不含密钥头)
- 失败:
None
方法:encode(text: str) -> bytes
加密字符串版本,内部转为 UTF-8 字节后调用 encode_bytes
方法:decode(data: bytes) -> str or None
解密并返回 UTF-8 字符串;若失败返回 None
辅助函数
password(pwdtxt: str, key=pwdkey) -> str or None
使用固定密钥对密码文本加密并验证。
步骤:
- 加密
pwdtxt - 立即解密验证一致性
- 一致则返回加密结果(Base64 编码字符串),否则返回
None
✅ 用途:确保加密过程可靠
unpassword(code: str, key=pwdkey) -> str or None
解密由 password() 生成的密文。
返回:
- 明文字符串 或
None
使用示例
if __name__ == '__main__':
data = b"231r3 feregrenerjk gkht324g8924gnfw ;ejkvwkjerv"
key = b'123456'
rc4 = RC4()
kc = KeyChain('in the heaven, we are equal', rc4)
print("原始数据:", data)
# 加密
encoded_data = kc.encode_bytes(data)
print("加密后数据:", encoded_data, "长度:", len(encoded_data))
# 解密
decoded_data = kc.decode_bytes(encoded_data)
print("解密数据:", decoded_data)
print("解密成功:", decoded_data == data)
安全性分析
| 特性 | 评估 |
|---|---|
| RC4 算法 | 已知存在弱点(如偏差输出),不推荐用于高安全场景 |
| Salt 固定 | self.salt 固定可能导致重放攻击风险 |
| 密钥派生 | 自定义算法非标准,缺乏密码学证明 |
| 密钥包含在明文中加密 | 有一定防篡改作用,但仍可能被暴力破解 |
| 时间同步机制 | 适合短期令牌、API 认证等场景 |
| Base64 支持 | 方便文本传输 |
✅ 推荐用途:轻量级认证令牌、会话加密、临时凭证
❌ 不推荐:金融交易、长期存储加密
已知问题与改进建议
| 问题 | 描述 | 建议 |
|---|---|---|
| ❗ Salt 未真正参与密钥派生 | decode_bytes 忽略传入 salt,始终用固定 salt |
应使用传入 salt 生成密钥 |
⚠️ _crypt 返回方式不安全 |
使用 chr() 可能超出 ASCII 范围引发错误 |
改为直接构建字节数组 |
| ⚠️ RC4 已过时 | 存在已知漏洞(如 WEP 攻击) | 替换为 AES-CTR 或 ChaCha20 |
| 🔒 自定义密钥生成算法 | 非标准化,可能存在碰撞或弱密钥 | 使用 HMAC-SHA256 等标准 KDF |
| 💤 时间依赖性强 | 严重依赖系统时间准确性 | 增加强时间验证或 NTP 校准提示 |
总结
本模块实现了一套基于 RC4 和时间窗口密钥链的安全通信原型,具备以下优点:
- 简洁易集成
- 支持自动密钥轮换
- 具备时间容忍机制
- 可用于短生命周期数据保护
建议在低风险场景中使用,并考虑升级至更现代的加密算法(如 AES-GCM 或 ChaCha20-Poly1305)以提升安全性。
文档版本:v1.0
最后更新:2025-04-05