2025-10-05 11:23:33 +08:00

9.8 KiB
Raw Permalink Blame History

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' 编码)
算法流程:
  1. 初始化长度为 256 的 S-box。
  2. 使用密钥调度算法KSA打乱 S-box。
  3. 使用伪随机生成算法PRGA逐字节异或加密。

⚠️ 注意:此实现中返回的是 .join(chr(...)) 后再 .encode('iso-8859-1'),需注意字符边界问题。


方法:encode_bytes(self, bdata: bytes, key: bytes) -> bytes

对字节数据执行带盐值的加密。

流程:
  1. 计算密钥摘要:sha1(key + self.salt).digest()
  2. 使用上述密钥对 bdata 执行 _crypt
  3. 将原始 salt 前缀附加到密文前(共 16 字节)
返回:
  • salt + encrypted_databytes

方法: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 生成的数据。

步骤:
  1. 提取前 16 字节作为 salt但实际未使用应为 bug
  2. 使用 sha1(key + self.salt) 生成密钥
  3. 对剩余部分调用 _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

根据 indicatorseed_str 生成唯一密钥。

算法逻辑:
  1. 若缓存中已有对应密钥,直接返回
  2. 否则使用循环算法从 seed_str 中按规则选取字符构造密钥:
    • 初始值 v = indicator
    • 每次取 j = v % keylen 作为索引
    • seed_str[j] 取字符追加
    • 更新 v = v - (j + k1)*m + keylen
    • 直到生成 keylen 长度的密钥
  3. 缓存密钥,并清理过期条目(早于 indicator - period 的)

🔐 安全性:密钥依赖时间和种子,难以预测


方法:encode_bytes(bdata: bytes) -> bytes

使用当前时间对应的密钥加密数据。

流程:
  1. 获取当前 indicator
  2. 生成对应密钥 key
  3. 构造明文:key + bdata(将密钥本身也加密传输)
  4. 使用 crypter.encode_bytes(data, key) 加密

📦 输出结构:salt + encrypted(key + bdata)


方法:decode_bytes(data: bytes) -> bytes or None

智能解密:尝试用当前、上一周期、下一周期的密钥解密。

步骤:
  1. 获取当前 indicator 和密钥
  2. 尝试解密
  3. 验证解密后数据前缀是否等于密钥(完整性校验)
  4. 如果失败且接近周期底部(即将切换),尝试上一周期密钥
  5. 如果接近顶部(刚过切换点),尝试下一周期密钥
返回:
  • 成功:解密后的原始数据(不含密钥头)
  • 失败: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

使用固定密钥对密码文本加密并验证。

步骤:
  1. 加密 pwdtxt
  2. 立即解密验证一致性
  3. 一致则返回加密结果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